Compare commits

..

1364 Commits

Author SHA1 Message Date
advplyr e70e4b9d40 Fix typo on onTest notification body 2026-05-30 15:43:50 -05:00
advplyr aacdcc47ec Version bump v2.35.1 2026-05-28 15:22:55 -05:00
advplyr 499b52b4dd Update Sequelize where query for User username/email case insensitive 2026-05-28 14:49:49 -05:00
advplyr 1bad2d9072 Cleanup abmetadata file parsing & fix server crash #5268 #4287 #5142 2026-05-27 17:33:14 -05:00
advplyr c009db9f28 Merge pull request #5256 from nichwall/fix-bookauthor-collision-on-rename
Fix duplicate bookAuthor creation when renaming authors
2026-05-22 15:43:13 -05:00
advplyr 325469c5a5 Merge pull request #5255 from nichwall/refresh-token-uniqueness
Add unique UUID to access and refresh tokens
2026-05-22 15:39:01 -05:00
Nicholas Wallace c97b36e11c Add ignoreDuplicates for bookAuthor when renaming to respect unique index 2026-05-21 21:06:17 -07:00
Nicholas Wallace e944b2a2f5 Add unique UUID to access and refresh tokens 2026-05-21 17:08:39 -07:00
advplyr 2d0a5462d2 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2026-05-17 14:31:45 -05:00
advplyr 72dc75482f Version bump v2.35.0 2026-05-17 14:31:41 -05:00
advplyr cac74f3477 Merge pull request #5004 from nichwall/token_refresh_race_condition
Access token refresh grace period
2026-05-17 14:16:58 -05:00
advplyr 1ad11b2b9e Merge pull request #5216 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-05-17 14:16:24 -05:00
Pavel Miniutka 50eeca2e0f Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-05-15 18:13:27 +00:00
EteranlK 4f21fc023c Translated using Weblate (Arabic)
Currently translated at 96.3% (1120 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2026-05-15 18:13:26 +00:00
advplyr 52a485d135 Added translation using Weblate (Latvian) 2026-05-15 18:13:25 +00:00
d0nizam 3b025076e8 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2026-05-15 18:13:23 +00:00
Mateusz Lesiak 6d5d89429d Translated using Weblate (Polish)
Currently translated at 99.8% (1161 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2026-05-15 18:13:22 +00:00
advplyr c010f0e1eb Fix android device sdkVersion not handling it using number type, causing android session device names to show as iOS 2026-05-15 13:13:14 -05:00
advplyr eee377e081 Cleanup TokenManager logs 2026-05-13 16:23:26 -05:00
advplyr b0aaa24660 Update socket events to check client is admin & validate log level 2026-05-12 16:57:28 -05:00
advplyr 47ea6b5092 Update book/podcast scanner to sanitize description pulled from metadata 2026-05-05 17:18:49 -05:00
advplyr 4b060febc2 Merge pull request #5221 from brandonfhall/fix/rss-feed-m4b-content-type
Fix: RSS feed serves m4b files with correct Content-Type: audio/mp4
2026-05-03 14:40:43 -05:00
Brandon 40869bcf39 fix: set correct Content-Type for RSS feed audio files
Express's mime package does not recognize .m4b, causing it to fall back
to application/octet-stream. This reuses the existing
getAudioMimeTypeFromExtname utility (already applied to the download
endpoint) to set the correct audio/mp4 header before sendFile.

Fixes #5041
2026-05-02 22:13:35 -04:00
advplyr 3942805129 Cleanup rotateTokensForSession 2026-04-30 16:25:43 -05:00
advplyr dc446862c1 Rename migration to v2.35.0 & merge master 2026-04-30 16:08:24 -05:00
advplyr 379f6c716a Merge branch 'master' into token_refresh_race_condition 2026-04-30 15:59:22 -05:00
advplyr 47457ee1e7 Version bump v2.34.0 2026-04-27 16:51:34 -05:00
advplyr cb6ff9eedf Merge pull request #5204 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-04-27 16:45:57 -05:00
LvanAlphen 5dc01261c1 Translated using Weblate (Dutch)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2026-04-26 21:51:49 +00:00
Naoto Ishikawa cbc103cf05 Translated using Weblate (Japanese)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2026-04-26 21:51:49 +00:00
Pavel Miniutka e79256d0fb Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-04-26 21:51:48 +00:00
ugyes f8ef56c6bc Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2026-04-26 21:51:48 +00:00
advplyr 62d7097e23 Add ApiCacheManager test for should remove recent-episodes cache entries 2026-04-26 16:51:39 -05:00
advplyr 92df92ec99 Fix recent episodes endpoint cache not being cleared when updating media progress #5159 2026-04-26 16:51:08 -05:00
advplyr 1c229e0627 Merge pull request #5211 from na3shkw/add-i18n-japanese
Add Japanese (ja) language and Japan podcast search region
2026-04-26 16:19:37 -05:00
advplyr f8a71cc514 Merge pull request #5089 from meek2100/pass_managers
feat: add autocomplete attributes for password manager support
2026-04-26 16:16:42 -05:00
Naoto Ishikawa 63de5bb2d5 Merge branch 'advplyr:master' into add-i18n-japanese 2026-04-26 15:23:22 +09:00
advplyr 2c3108a1fa Merge pull request #5163 from pjkottke/master
The timestamp in the share URL should override the saved position for the user.
2026-04-25 17:15:23 -05:00
advplyr 928051744a ShareController check ?t param is less than duration, revert frontend mounted usage of param 2026-04-25 17:13:22 -05:00
advplyr 3ccdcaec1a Implement SSRF filter for podcast episode downloads 2026-04-25 16:46:54 -05:00
na3shkw f47bbc7886 Add Japanese language and Japan podcast search region 2026-04-25 15:56:16 +00:00
advplyr 7c0ca44727 Update podcast create/update endpoints to validate autoDownloadSchedule cron expression, validate cron expression before starting in CronManager 2026-04-24 16:55:42 -05:00
advplyr d6a2e5596b Fix undefined variable in error log for when podcast cron is invalid 2026-04-24 16:18:56 -05:00
advplyr a5362de9cc Update podcast createFromRequest to sanitize html description 2026-04-23 14:34:59 -05:00
advplyr 9ab35ef418 Update playlist endpoints to check user still has library access 2026-04-22 16:42:58 -05:00
advplyr 79cc9765cf Update collection endpoints to check user library access 2026-04-22 16:29:47 -05:00
advplyr 5b2a788cfc Update LibraryItemController test with 403 tests 2026-04-21 17:13:52 -05:00
advplyr 80b39abaa2 Update library item batch api endpoints check users per-item access & return 403 2026-04-21 17:13:06 -05:00
advplyr b41db23994 Version bump v2.33.2 2026-04-19 16:46:10 -05:00
advplyr 125f265f55 Merge pull request #5141 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-04-19 16:21:40 -05:00
Gernomaly aa4a191567 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-04-19 21:20:46 +00:00
Jan e431ea0472 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-04-19 21:20:45 +00:00
Laurin Sorgend e3388d4446 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-04-19 21:20:45 +00:00
Mario 88879f1409 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-04-19 21:20:44 +00:00
Pavel Miniutka 3e0099e8d9 Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-04-19 21:20:44 +00:00
tizio04 f558182d94 Translated using Weblate (Italian)
Currently translated at 99.9% (1162 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2026-04-19 21:20:43 +00:00
Владимир Макеев a30fe15b10 Translated using Weblate (Russian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2026-04-19 21:20:43 +00:00
A L 0bbf8bde5c Translated using Weblate (Bulgarian)
Currently translated at 91.2% (1061 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2026-04-19 21:20:42 +00:00
Alex 0e2cdde731 Translated using Weblate (Slovak)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2026-04-19 21:20:41 +00:00
tfr tint bc6bfbe804 Translated using Weblate (Italian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2026-04-19 21:20:41 +00:00
Vadzim Kurdzesau 2755204168 Translated using Weblate (Russian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2026-04-19 21:20:40 +00:00
Francisco Serrador 2d4df273f0 Translated using Weblate (Spanish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-04-19 21:20:40 +00:00
Pavel Miniutka d73b64a19c Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-04-19 21:20:39 +00:00
advplyr b7e8a0474a Update bulk download endpoint ensure items are from the same library requested 2026-04-19 16:20:31 -05:00
advplyr 39adefb632 Update backup load & upload to remove tempfile on failed backups, validate details filesize & close zip 2026-04-18 17:03:37 -05:00
advplyr 24cab79c66 Update filesystem/pathexists endpoint to use existing isSameOrSubPath func 2026-04-18 16:24:48 -05:00
advplyr b27f21fd95 Update podcastUtils to sanitize episode subtitle from rss feed 2026-04-17 16:59:22 -05:00
advplyr 09fa0b38f5 Update podcast create path validation & fix relPath 2026-04-17 16:51:22 -05:00
advplyr 455e605162 Update author & library item image endpoints to clamp width/height query params 2026-04-17 16:30:08 -05:00
advplyr 88667d00a1 Merge pull request #5115 from rktjmp/fix-mka-opus-desktop-streaming
Force AAC transcode when streaming mka+opus to desktop client
2026-04-10 16:54:18 -05:00
advplyr 94c426bd97 Update comments on matroska 2026-04-10 16:42:39 -05:00
Oliver Marriott 522b9735e2 Add audio/(x-)matroska to client player MIME types to avoid transcode
Firefox, at least, supports playing `matroska/audio` containers natively but
the client was not checking for support. Clients  that do not support
playing `matroska/audio` containers will fallback to transcoding.
2026-04-09 21:07:14 +10:00
peter.kottke 5a6b3d8e61 updates to allow share t argument to over-ride server stored position 2026-04-01 21:05:48 -04:00
advplyr 64cbf59609 Merge pull request #5160 from mikiher/fix-item-removed-payload
Fix item_removed payload to include libraryId
2026-03-31 16:43:51 -05:00
mikiher fda1a6ea9b Fix item_removed payload to include libraryId 2026-03-31 22:02:52 +03:00
advplyr c4c8b8d0f2 Merge pull request #5158 from mikiher/book-update-author-events
Emit proper author_updated/added events when updating book media
2026-03-30 16:26:47 -05:00
advplyr ab3bd6f4a1 Update JS docs 2026-03-30 16:22:27 -05:00
mikiher 093124aac6 Emit proper author_updated/added events when updating book media 2026-03-30 22:02:56 +03:00
advplyr 5de92d08f9 Fix share playback session not including coverAspectRatio 2026-03-29 15:36:07 -05:00
advplyr 8b89b27654 Version bump v2.33.1 2026-03-19 17:09:14 -05:00
advplyr 3faa6f3e7d Update playlist create/update endpoint to strip all html tags 2026-03-19 16:57:22 -05:00
advplyr 9821c31f8e Update collection create/update endpoints to strip html tags from collection name 2026-03-19 16:53:21 -05:00
advplyr efe2a22674 Merge pull request #5122 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-03-19 16:41:19 -05:00
Francisco Serrador 9634c46bc5 Translated using Weblate (Spanish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-03-18 23:01:31 +01:00
Francisco Serrador 5f8db24b96 Translated using Weblate (Spanish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-03-18 23:01:30 +01:00
Francisco Serrador e781ff5eae Translated using Weblate (Spanish)
Currently translated at 99.7% (1160 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-03-18 23:01:30 +01:00
Francisco Serrador 32a17c0044 Translated using Weblate (Spanish)
Currently translated at 99.7% (1160 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-03-18 23:01:29 +01:00
Fabian Jülich f84831d6f1 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-03-18 23:01:29 +01:00
Pavel Miniutka dc54d42dcf Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-03-18 23:01:28 +01:00
Pavel Miniutka 15af7407ff Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-03-18 23:01:27 +01:00
Charlie 5d9682410a Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2026-03-18 23:01:27 +01:00
advplyr 4bdd76d94c Update podcast episode update endpoint to sanitize subtitle 2026-03-18 17:01:19 -05:00
advplyr 7c0d9efe91 Update Confirm component to support allowHtml prompt option 2026-03-18 16:51:51 -05:00
advplyr 874e9e1856 Update API Key jwtAuthCheck to check user active status 2026-03-18 16:17:45 -05:00
advplyr 6d3773a0b8 Version bump v2.33.0 2026-03-12 17:01:54 -05:00
advplyr a47c869d0b Update migration file to v2.33.0 2026-03-12 16:45:08 -05:00
advplyr eb0383d37a Merge pull request #5073 from kevingatera/perf/minimal-upstream-patchset
Improve personalized/discover query performance and cache invalidation behavior
2026-03-12 16:38:26 -05:00
advplyr e66ffb9c23 Add indexes to MediaProgress and BookSeries models 2026-03-12 16:37:59 -05:00
advplyr 972193b193 Update server settings authLoginCustomMessage to sanitize on save and load 2026-03-11 17:18:05 -05:00
advplyr 690a7e0da9 Update session DeviceInfo with sanitize on clientDeviceInfo 2026-03-11 17:03:07 -05:00
Oliver Marriott d9355ac3aa Force AAC transcode when streaming mka+opus to desktop client
Matroska audio containers (aka mka files) with Opus codec streams inside
were unplayable on the desktop client because hls.js was unable to
decode the stream, resulting in an infinitely "spinning" play button.

When configuring a stream, we now check for the opus codec and force AAC
transcoding.

Matroska containers support other codecs besides Opus, eg: mp3, which do
not require transcoding and work fine before this patch, which is why we
check for opus in codecsToForceAAC instead of AudioMimeType.MKA in
mimeTypesToForceAAC.

The AudioMimeType.OPUS mimetype is already marked as requiring
transcoding but since its inside a container this check does not
evaluate to true, we must check the codec explicitly.
2026-03-11 00:35:12 +11:00
advplyr fbe1d1eed6 Merge pull request #5030 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-03-08 17:30:54 -05:00
Grzegorz Orlowski e83aca572e Translated using Weblate (Polish)
Currently translated at 95.7% (1113 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2026-03-08 23:26:10 +01:00
Artur 367826ce64 Translated using Weblate (Polish)
Currently translated at 95.7% (1113 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2026-03-08 23:26:10 +01:00
Torstein Eide 6e6c43c53c Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2026-03-08 23:26:10 +01:00
weblate.user.1274 6479cdb66d Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2026-03-08 23:26:10 +01:00
Øystein S. Hegnander 635e132325 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2026-03-08 23:26:10 +01:00
thehijacker 45dd843ce1 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2026-03-08 23:26:10 +01:00
Torstein Eide 7c956b1582 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2026-03-08 23:26:10 +01:00
Pavel Miniutka 9afa39e29e Translated using Weblate (Belarusian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-03-08 23:26:10 +01:00
Pavel Miniutka 5f8450602e Translated using Weblate (Belarusian)
Currently translated at 92.1% (1072 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-03-08 23:26:10 +01:00
Pavel Miniutka fa6dae1a53 Translated using Weblate (Belarusian)
Currently translated at 79.8% (929 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2026-03-08 23:26:10 +01:00
lambolighting a1d439b8d5 Translated using Weblate (Greek)
Currently translated at 27.6% (322 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/el/
2026-03-08 23:26:10 +01:00
peter cerny 2f32673991 Translated using Weblate (Slovak)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2026-03-08 23:26:10 +01:00
herny ucet c1a6b51d78 Translated using Weblate (Slovak)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2026-03-08 23:26:09 +01:00
Gneb 6c2e13fb4e Translated using Weblate (Slovak)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2026-03-08 23:26:09 +01:00
Plazec 210fa55b6a Translated using Weblate (Czech)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2026-03-08 23:26:09 +01:00
Silviu Bajenaru fb8ca043ad Translated using Weblate (Romanian)
Currently translated at 55.1% (641 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2026-03-08 23:26:09 +01:00
Karl Bernstål 69d7c399b8 Translated using Weblate (Swedish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2026-03-08 23:26:09 +01:00
Igor Dobrača a35ba05600 Translated using Weblate (Croatian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2026-03-08 23:26:09 +01:00
B0rax 40f42b2ab6 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-03-08 23:26:09 +01:00
Maxklos cf4b9e938d Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-03-08 23:26:09 +01:00
ugyes 31120ad111 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2026-03-08 23:26:09 +01:00
Yusuke Sakai cd8640f00e Translated using Weblate (Japanese)
Currently translated at 23.1% (269 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2026-03-08 23:26:09 +01:00
Luis Landeiro d0ba455ed6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2026-03-08 23:26:09 +01:00
advplyr e5b7aea46c Merge pull request #5063 from mandreko/idor-fixes
IDOR fixes
2026-03-08 17:26:03 -05:00
advplyr 2f2d026b06 Auto format 2026-03-08 17:25:52 -05:00
advplyr c156b063f5 Merge pull request #5036 from kctdfh/Slightly-better-subtitle-parsing-logic
Improved subtitle parsing to account for bare colon in title
2026-03-08 17:12:55 -05:00
advplyr e6d49a2d53 Fix home page check current user for hide from continue listening 2026-03-04 16:50:36 -06:00
advplyr 6d3404272c Fix Match click to use current label string 2026-02-28 11:32:28 -06:00
meek2100 a9e12657f5 Add autocomplete attributes to login and setup fields for password manager support 2026-02-26 14:29:28 -08:00
Kevin Gatera c0319ebbac Adjust discover/search changes for API compatibility 2026-02-23 19:22:45 -05:00
advplyr 1d0b7e383a Merge pull request #5071 from pavel-miniutka/master
Add Belarusian language option & Belarus podcast region
2026-02-22 16:19:19 -06:00
advplyr 9f5d8386f3 Merge pull request #5077 from belpe/add-slovak-language
Add Slovak (sk) language to language selector
2026-02-22 16:15:55 -06:00
advplyr 6e0da3bf7a Fix updating author name merging with same name authors in a different library #4628 2026-02-21 16:00:38 -06:00
Peter Belák ee6016f70e Add Slovak (sk) language to the language selector
The Slovak translation file (client/strings/sk.json) already exists
with a complete translation but was missing from the languageCodeMap
in i18n.js, making it inaccessible from the language dropdown.

Also adds Slovakia to the podcast search region map.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 09:11:50 +01:00
Kevin Gatera f1a2e56054 Add database indexes for discover query performance 2026-02-19 20:11:49 -05:00
Kevin Gatera d2915e689f Speed up personalized shelves and reduce search payload size 2026-02-19 20:11:38 -05:00
Kevin Gatera 05d9ab81f9 Improve API cache invalidation for high-churn models 2026-02-19 20:11:24 -05:00
pavel-miniutka 75eed9d09a Add Belarusian language option & Belarus podcast region 2026-02-19 10:36:28 +03:00
Matt Andreko e5af2f336b Move file to correct folder... 2026-02-15 22:06:20 -05:00
Matt Andreko ade1752e97 Fix IDOR bugs 2026-02-15 22:01:36 -05:00
advplyr fa5fa7b788 Fix server crash on /me/progress/:libraryItemId/:episodeId? when episodeId is not passed in for a podcast library item #5058 2026-02-14 17:17:12 -06:00
advplyr b01facc034 Merge pull request #5042 from openam/open-api-spec-fixes
Fix OpenAPI spec description
2026-02-08 17:23:18 -06:00
Michael Tuttle dd4fc09909 Fix OpenAPI spec description 2026-02-08 00:19:23 -07:00
Khashayar Toodehfallah c15cb48def Improved subtitle parsing to account for bare colon in title 2026-02-06 14:31:09 -05:00
advplyr fe13456a2b Merge pull request #4936 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-02-03 17:26:42 -06:00
Lluís Forns 2ee893062f Translated using Weblate (Catalan)
Currently translated at 92.2% (1073 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2026-02-03 20:10:06 +01:00
dapitch666 31630f50a5 Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2026-02-03 20:10:06 +01:00
Delta Umhöfer edfce46058 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2026-02-02 08:02:28 +00:00
Jan-Eric Myhrgren cc5244c596 Translated using Weblate (Swedish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2026-02-02 08:02:27 +00:00
enosh b8942c5931 Translated using Weblate (Hebrew)
Currently translated at 81.4% (947 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2026-02-02 08:02:26 +00:00
FiendFEARing 6e5feee78a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2026-02-02 08:02:25 +00:00
Dawid Kuźnicki e7cb0466e6 Translated using Weblate (Polish)
Currently translated at 94.5% (1100 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2026-02-02 08:02:24 +00:00
dv4yGY2U 6c7221d37d Translated using Weblate (Turkish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2026-02-02 08:02:24 +00:00
Samuel Guerrero 1f3fa80ddd Translated using Weblate (Spanish)
Currently translated at 97.5% (1134 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2026-02-02 08:02:23 +00:00
Mantas 87f3766299 Translated using Weblate (Lithuanian)
Currently translated at 59.9% (697 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/lt/
2026-02-02 08:02:22 +00:00
Bartłomiej d08cef11ed Translated using Weblate (Polish)
Currently translated at 94.5% (1100 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2026-02-02 08:02:21 +00:00
Kabika82 7201cced42 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2026-02-02 08:02:20 +00:00
N Visi 4f8fbbc979 Translated using Weblate (Japanese)
Currently translated at 22.8% (266 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2026-02-02 08:02:19 +00:00
Henrik Lynge e55fed4a33 Translated using Weblate (Danish)
Currently translated at 99.9% (1162 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2026-02-02 08:02:19 +00:00
herny ucet dcbeecff7a Translated using Weblate (Slovak)
Currently translated at 99.7% (1160 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2026-02-02 08:02:18 +00:00
xxzp3 32276aacd9 Translated using Weblate (Danish)
Currently translated at 99.8% (1161 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2026-02-02 08:02:17 +00:00
J. Lavoie b921a08809 Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2026-02-02 08:02:16 +00:00
J. Lavoie c089336e41 Translated using Weblate (Italian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2026-02-02 08:02:15 +00:00
Charlie 5107b0307c Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2026-02-02 08:02:14 +00:00
advplyr 8498378bca Merge pull request #4952 from sir-wilhelm/use-localized-text-for-selected-filter
Display localized/styled text for selected filter.
2026-01-31 17:56:07 -06:00
advplyr b61e2c30f2 Merge pull request #4973 from KiwiHour/jump-backwards-label-fix
Fix screen reader compatability issue for the "jump backwards" button for media controls
2026-01-31 17:50:41 -06:00
Nicholas Wallace cfeb6bd502 Fix: grace period enable statement 2026-01-24 18:57:40 -07:00
Nicholas Wallace 077b523bd6 Fix JS Doc deletion 2026-01-24 18:42:50 -07:00
Nicholas Wallace b8a2d113f0 Allow rotation without grace period for invalidating all user sessions 2026-01-24 18:26:11 -07:00
Nicholas Wallace e1ae4f2d31 Fix: race condition in rotation 2026-01-24 18:10:38 -07:00
Nicholas Wallace 7aa2f84daa Revert default token expiry 2026-01-24 17:00:07 -07:00
Nicholas Wallace da0a64daed Add: 10 second grace period to access token cycle 2026-01-24 16:57:25 -07:00
KiwiHour 3e4225bced Fix aria-label for jumpBackward button 2026-01-09 14:26:56 +00:00
sir-wilhelm e6d99d07f0 Display localized/styled text for selected filter.
The selected filter was using the id before.
2025-12-28 11:28:36 -06:00
advplyr 122fc34a75 Fix server crash filtering by decade with collapsed series 2025-12-24 17:07:05 -06:00
advplyr e5c0a9d22c Version bump v2.32.1 2025-12-23 16:51:54 -06:00
advplyr 3bf136a20b Merge pull request #4933 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-12-23 16:49:04 -06:00
Marcin b387d9484a Translated using Weblate (Polish)
Currently translated at 89.2% (1038 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-12-23 23:44:39 +01:00
bittin1ddc447d824349b2 e8668d9f22 Translated using Weblate (Swedish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-12-23 23:44:38 +01:00
Petri Hämäläinen f3e90bd420 Translated using Weblate (Finnish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-12-23 23:44:37 +01:00
Ivan Smoliakov 4bf15bbffd Translated using Weblate (Russian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-12-23 23:44:37 +01:00
advplyr 04eb3bc437 Fix server crash on audible match #4931 2025-12-23 16:44:29 -06:00
advplyr 81e96df9c5 Version bump v2.32.0 2025-12-21 15:54:07 -06:00
advplyr 44aff23e1b Merge pull request #4921 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-12-21 15:44:28 -06:00
lambolighting cc48d9f26d Translated using Weblate (Greek)
Currently translated at 26.9% (313 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/el/
2025-12-21 21:38:43 +00:00
Ahetek ac08e897ee Translated using Weblate (Polish)
Currently translated at 89.2% (1038 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-12-21 21:38:43 +00:00
FiendFEARing 3c2eec8279 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-12-21 21:38:42 +00:00
advplyr 7b37c98e88 Book tags genres dedupe (#4927)
* Update Audible provider dedupe genres/tags and return tags as array

* Update custom metadata provider to dedupe tags/genres and return tags as array
2025-12-21 15:38:34 -06:00
advplyr 088353ae26 Merge pull request #4649 from votex001/multi-select-item-fix
[fix] prevent duplicates in multi-selects
2025-12-21 14:58:04 -06:00
advplyr e003544edd Merge pull request #4766 from TN-SKYC/Authors-bug
Bug in matching author of a book when this author already exists in the db.
2025-12-21 14:49:37 -06:00
advplyr 076ece6fe7 Auto-formatting 2025-12-21 14:45:04 -06:00
advplyr 14f72ab7d4 Merge pull request #4740 from Yetangitu/fix_debian_user_exists
fix #1617 (useradd: user 'audiobookshelf' already exists)
2025-12-21 14:08:10 -06:00
advplyr ebcb122eb8 Merge pull request #4906 from sir-wilhelm/playlist-sorted
When adding items to playlist, sort the playlist sections alphabetically.
2025-12-21 14:01:08 -06:00
advplyr 626596b192 Merge pull request #4890 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-12-14 16:35:16 -06:00
lambolighting 10a4777ddf Translated using Weblate (Greek)
Currently translated at 9.0% (105 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/el/
2025-12-14 23:00:22 +01:00
MODI NAVON 0ecbb1c3f4 Translated using Weblate (Hebrew)
Currently translated at 73.0% (850 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2025-12-12 23:24:22 +00:00
Lucas Jaksys dc2398a072 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-12-12 23:24:21 +00:00
lambolighting c1e21d31ee Translated using Weblate (Greek)
Currently translated at 1.1% (13 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/el/
2025-12-12 23:24:21 +00:00
Mario 70e6efc3d0 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-12-12 23:24:20 +00:00
Alberto Coronado 092c504eb1 Translated using Weblate (Spanish)
Currently translated at 97.5% (1134 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-12-12 23:24:19 +00:00
A L f7d7c9a4f5 Translated using Weblate (Bulgarian)
Currently translated at 88.2% (1026 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-12-12 23:24:19 +00:00
kfctatertot 8bdcabf973 Translated using Weblate (Spanish)
Currently translated at 96.8% (1126 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-12-12 23:24:18 +00:00
kfctatertot 646c861bcc Translated using Weblate (Arabic)
Currently translated at 95.9% (1116 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-12-12 23:24:17 +00:00
zard Kim ee60169995 Translated using Weblate (Korean)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ko/
2025-12-12 23:24:17 +00:00
advplyr afb4108c30 Added translation using Weblate (Greek) 2025-12-12 23:24:16 +00:00
Igor Dobrača 2e2d857ce0 Translated using Weblate (Croatian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-12-12 23:24:15 +00:00
Alessandro Burzio ed5766b4ab Translated using Weblate (Italian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-12-12 23:24:14 +00:00
Petri Hämäläinen a33e87db99 Translated using Weblate (Finnish)
Currently translated at 97.6% (1136 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-12-12 23:24:14 +00:00
Napitauki 5de942aefb Translated using Weblate (Finnish)
Currently translated at 97.6% (1136 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-12-12 23:24:13 +00:00
thehijacker bcfe1e9647 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-12-12 23:24:12 +00:00
advplyr 503f4611b2 Update tooltip with plaintext prop 2025-12-12 17:24:01 -06:00
sir-wilhelm 648983708e Sort the playlist sections alphabetically. 2025-12-12 04:18:29 +00:00
advplyr 991d25f628 Version bump v2.31.0 2025-12-01 17:13:15 -06:00
advplyr d2a7c3c381 Merge pull request #4879 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-12-01 17:09:02 -06:00
Netleak 219a9fc6d5 Translated using Weblate (Czech)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-12-01 16:59:30 +00:00
advplyr ba2259d174 Merge pull request #4881 from mikiher/wrap-ensure-dir
Wrap ensureDir in try-catch blocks in manager classes
2025-12-01 10:59:21 -06:00
mikiher d7bfccdc4a BackupManager: Remove backup fallback logic 2025-12-01 18:54:43 +02:00
mikiher 5f1edcb609 Wrap ensureDir in try-catch blocks 2025-12-01 18:00:34 +02:00
advplyr 329e3c7179 Add Korean language option 2025-11-29 16:32:57 -06:00
advplyr 919ea32416 Merge pull request #4848 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-11-29 16:27:45 -06:00
J. Lavoie 3b6419bc1b Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-11-29 23:26:56 +01:00
J. Lavoie d4fdb47c7f Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-11-29 23:26:55 +01:00
Petri Hämäläinen cee9b9d8e3 Translated using Weblate (Finnish)
Currently translated at 97.5% (1134 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-11-29 23:26:54 +01:00
advplyr 9441346b0a Added translation using Weblate (Icelandic) 2025-11-29 23:26:53 +01:00
Ivan Smoliakov 6b8464c270 Translated using Weblate (Russian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-11-29 23:26:52 +01:00
Artur d12f727603 Translated using Weblate (Polish)
Currently translated at 89.2% (1038 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-11-29 23:26:51 +01:00
Renaldas Repečka 1552c250df Translated using Weblate (Lithuanian)
Currently translated at 59.8% (696 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/lt/
2025-11-29 23:26:50 +01:00
zard Kim 623c2fba12 Translated using Weblate (Korean)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ko/
2025-11-29 23:26:50 +01:00
Plazec be27908d44 Translated using Weblate (Czech)
Currently translated at 99.5% (1158 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-11-29 23:26:48 +01:00
Grzegorz Orlowski 7a39d581a1 Translated using Weblate (Polish)
Currently translated at 88.9% (1035 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-11-29 23:26:47 +01:00
Artur 53a416fd28 Translated using Weblate (Polish)
Currently translated at 88.9% (1035 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-11-29 23:26:47 +01:00
B0rax 7393c03218 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-11-29 23:26:46 +01:00
ugyes 594589da3d Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-11-29 23:26:45 +01:00
FiendFEARing 44d7deae99 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-11-29 23:26:44 +01:00
Jan-Eric Myhrgren ff9e87c4d5 Translated using Weblate (Swedish)
Currently translated at 99.9% (1162 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-11-29 23:26:43 +01:00
Cèlia Garriga c2fd87d55c Translated using Weblate (Catalan)
Currently translated at 91.8% (1068 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-11-29 23:26:42 +01:00
Максим Горпиніч 27843c3f9b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-11-29 23:26:41 +01:00
Vito0912 0ec2ced011 Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-11-29 23:26:40 +01:00
ugyes 552ed43243 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-11-29 23:26:39 +01:00
advplyr 0606738b38 Merge pull request #4873 from Vito0912/feat/fixUploadLookup
Fix Upload Lookup
2025-11-29 16:26:26 -06:00
Finn Dittmar a5d2c1bd64 Fix Upload Lookup 2025-11-29 11:02:42 +01:00
advplyr d8e272e091 Merge pull request #4870 from Vito0912/patch-1
Fix region parameter extraction in findChapters
2025-11-27 16:20:34 -06:00
Finn Dittmar 3e9ca51088 Fix region parameter extraction in findChapters 2025-11-27 21:40:29 +01:00
advplyr 8758c62ae2 Merge pull request #4702 from Vito0912/feat/uploadProgress
feat: Added progress indicator to upload
2025-11-24 17:08:03 -06:00
advplyr db9019a94f Merge pull request #4770 from jamerst/days-in-a-row-today-optional
Don't require listening today for 'Days in a row' stat
2025-11-20 16:59:17 -06:00
advplyr 39b8b9df4f Auto formatting 2025-11-20 16:58:43 -06:00
advplyr a36f097095 Merge pull request #4768 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-11-19 08:06:42 -06:00
advplyr ae0ccb1b47 Added translation using Weblate (Korean) 2025-11-19 15:03:59 +01:00
Jan-Eric Myhrgren f178841e57 Translated using Weblate (Swedish)
Currently translated at 99.8% (1161 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-11-19 05:41:52 +01:00
Mikael Engström 568b154e8a Translated using Weblate (Swedish)
Currently translated at 99.8% (1161 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-11-17 21:51:27 +01:00
dapitch666 8c5678b573 Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-11-17 21:51:19 +01:00
Jan Schoenfeld e51c7b2be1 Translated using Weblate (German)
Currently translated at 99.9% (1162 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-11-17 21:51:17 +01:00
Milo Ivir d460757df4 Translated using Weblate (Croatian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-11-14 23:51:49 +00:00
thehijacker cd295c03ca Translated using Weblate (Slovenian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-11-12 10:53:50 +00:00
FiendFEARing 38dd1beff7 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-11-12 10:53:37 +00:00
Максим Горпиніч 61b72aff9d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-11-12 10:53:35 +00:00
Lucas Jaksys 9eda4e36fa Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-11-12 10:53:14 +00:00
biuklija a05cb170a2 Translated using Weblate (Croatian)
Currently translated at 99.9% (1162 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-11-12 10:52:18 +00:00
Dinu Bălan c75d976320 Translated using Weblate (Romanian)
Currently translated at 17.3% (202 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2025-11-10 23:37:16 +01:00
Petri Hämäläinen ab9b798bfa Translated using Weblate (Finnish)
Currently translated at 97.2% (1131 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-11-10 23:37:16 +01:00
Charlie 40783c8644 Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-11-10 23:37:16 +01:00
phewi a0137fcb42 Translated using Weblate (Finnish)
Currently translated at 95.5% (1111 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-11-10 23:37:16 +01:00
Lucas Gonçalves Jaksys 9fdda6be62 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-11-10 23:37:16 +01:00
Lucas Gonçalves Jaksys b0c073dd7e Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-11-10 23:37:16 +01:00
Sami Tauseef b83e2836f6 Translated using Weblate (Bengali)
Currently translated at 92.6% (1078 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bn/
2025-11-10 23:37:16 +01:00
Lucas Gonçalves Jaksys f91be18527 Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.1% (1083 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-11-10 23:37:16 +01:00
dapitch666 c1f4e4120e Translated using Weblate (French)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-11-10 23:37:16 +01:00
Petri Hämäläinen b42c7421b0 Translated using Weblate (Finnish)
Currently translated at 95.5% (1111 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-11-10 23:37:16 +01:00
pryszczoskor 82f512d405 Translated using Weblate (Polish)
Currently translated at 88.3% (1027 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-11-10 23:37:16 +01:00
Aleksandr Zakirov 01a833ea59 Translated using Weblate (Estonian)
Currently translated at 64.2% (747 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/et/
2025-11-10 23:37:16 +01:00
advplyr f1c39e8587 Merge pull request #4815 from mikiher/fix-4095
Update wording for library watcher settings and scan note
2025-11-10 16:37:10 -06:00
mikiher 5e68936c20 Update English strings for library watcher settings and scan notes #4095 2025-11-10 09:14:46 +02:00
advplyr a4c9b062c1 Merge pull request #4808 from Vito0912/feat/eac3
Support eac3 and ac3 in transcodes
2025-11-08 17:13:06 -06:00
advplyr 763d8810e3 Update Stream ac3/eac3 check 2025-11-08 17:08:43 -06:00
Finn Dittmar 3316505d1c Really makes sure nothing can break 2025-11-07 19:12:38 +01:00
Finn Dittmar 2cf6e8a5fe Support eac3 2025-11-07 19:02:32 +01:00
Tomasz N. 961d066bdd Wrong branch. 2025-10-31 15:39:12 +01:00
Tomasz N. 372c9a5322 Increasing the timeout for bookfinder - some metadata providers heavily throttle the requests, original 10s is not enough. 2025-10-31 15:36:47 +01:00
James Tattersall f77de1743e Don't require listening today for 'Days in a row' stat 2025-10-23 21:31:37 +01:00
Tomasz N. a5750deaaf The key change: Move the Database.bookAuthorModel.create() block outside the if (!author) check,
so it runs whether the author was just created OR already existed in the database.

This bug was visible when using "Match Books" for a library and the outcome was books had no author(s) assigned
despite the custom providers correctly providing those values.
2025-10-22 23:02:49 +02:00
advplyr 0c7b738b7c Merge pull request #4730 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-10-21 17:26:49 -05:00
burghy86 c3c9e7731d Translated using Weblate (Italian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-10-22 00:24:25 +02:00
Petri Hämäläinen d3b5612fc0 Translated using Weblate (Finnish)
Currently translated at 95.3% (1109 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-10-22 00:24:24 +02:00
Blubberland 96ef0129ed Translated using Weblate (German)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-10-22 00:24:23 +02:00
nlqog 85546b7dd7 Translated using Weblate (Portuguese (Brazil))
Currently translated at 76.6% (892 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-10-22 00:24:22 +02:00
pmangro d59714d804 Translated using Weblate (Portuguese (Brazil))
Currently translated at 74.2% (863 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-10-22 00:24:22 +02:00
Coxe 96693659bf Translated using Weblate (Danish)
Currently translated at 96.9% (1127 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-10-22 00:24:21 +02:00
pryszczoskor ee2d8d1f71 Translated using Weblate (Polish)
Currently translated at 88.2% (1026 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-10-22 00:24:20 +02:00
Hezha f03b0915eb Translated using Weblate (Arabic)
Currently translated at 95.9% (1116 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-10-22 00:24:20 +02:00
advplyr a92ba564bd Merge pull request #4750 from mikiher/providers-api
Add metadata providers API and use them on web client
2025-10-21 17:24:11 -05:00
advplyr e684a8dc43 Update JSDocs & auto-formatting of PodcastFinder 2025-10-21 17:22:10 -05:00
mikiher 6db6b862e6 Upgrade codeql-actions to v3 2025-10-21 17:33:53 +03:00
mikiher 57c7b123f0 Fix codeQL error: Return json error object 2025-10-21 11:00:29 +03:00
mikiher fd593caafc SearchController: simplify query param validation logic 2025-10-21 09:38:35 +03:00
advplyr d0a3f74710 Merge pull request #4756 from Vito0912/tokenExpiry
Change token expiry
2025-10-20 17:19:54 -05:00
advplyr b1921e7034 Fix podcast failed to parse rss feed when feed is not using CData in content:encoded #4757 2025-10-19 17:25:18 -05:00
mikiher 538a5065a4 Update providers users to fetch providers on demand 2025-10-19 18:57:27 +03:00
mikiher 166e0442a0 Remove providers prefetch, refresh on custom provider add/remove 2025-10-19 11:47:17 +03:00
mikiher 816a47a4ba Remove custom providers from library fetch action 2025-10-19 11:40:40 +03:00
mikiher 141211590f Merge provider actions and mutations, add loaded state 2025-10-19 11:39:10 +03:00
mikiher b01e7570d3 Remove custom providers from library filterdata request 2025-10-19 10:54:26 +03:00
mikiher 0a8662d198 Merge providers API into a single endpoint 2025-10-19 10:53:27 +03:00
Finn Dittmar 0a4de61eff Chnage Auth Expiry 2025-10-19 09:22:12 +02:00
mikiher 0a82d6a41b CoverSearchManager: Fix broken podcast cover search 2025-10-17 08:11:03 +03:00
mikiher 3f6162f53c CodeQL fix: limit parameter sizes 2025-10-15 18:54:29 +03:00
mikiher 888190a6be Fix codeQL failures 2025-10-15 18:28:15 +03:00
mikiher ce4ff4f894 Client: Use new server providers API 2025-10-15 09:52:15 +03:00
mikiher 1da3ab7fdc ApiRouter: New provider API routes 2025-10-14 18:10:12 +03:00
mikiher 4f30cbf2f6 SearchController: New providers API, query param validation 2025-10-14 18:09:32 +03:00
advplyr a87ea32715 Fix admin user unable to close sessions of other users #4746 2025-10-13 09:50:01 -05:00
advplyr feed827223 Update settings update endpoint to validate allowedOrigins is array 2025-10-10 18:00:37 -05:00
Frank de Lange 797dba2448 fix #1617 (useradd: user 'audiobookshelf' already exists)
This change fixes the problem of failing upgrades on dpkg-based systems
by reworking the check for whether the `audiobookshelf` user/group already
exists.
2025-10-10 22:30:38 +02:00
advplyr f0acbb2e81 Merge pull request #4737 from Yetangitu/explicit_oidc_autolaunch
Explicitly launch OpenID Connect authentication with ?autoLaunch=1
2025-10-09 17:08:51 -05:00
Frank de Lange fc06aa2c78 Explicitly launch OpenID Connect authentication with ?autoLaunch=1
This change extends OIDC authentication by enabling explicit redirection
to the OAuth provider when navigating to the login page with the manual
override parameter (/login?autoLaunch=1).

Use case: directly launch audiobookshelf from within e.g. Nextcloud using the
external sites app (use something like https://abs.example.org/login?autoLaunch=1
as URL) while keeping the possibility to launch audiobookshelf using its built-in
authentication mechanism. Assuming the username or mail address used in Nextcloud
and audiobookshelf are identical the user will be logged in to his or her account
no matter which method is used.
2025-10-09 16:02:02 +02:00
Finn Dittmar 4224f44259 Remove duplicate (and also wrong byte conversion) 2025-10-09 08:49:13 +02:00
advplyr 2592467d09 Fix: Always re-load libraries when changing users #4694 2025-10-08 15:32:37 -05:00
advplyr 37beb7b37c Disable chapter editor chapter play button when time is invalid #4691 2025-10-08 15:03:33 -05:00
advplyr cafd92e206 Fix item edit modal show next/prev arrows when opening from Files or Match context menu item #4718 2025-10-08 14:52:14 -05:00
advplyr 3e876e3383 Add Turkish to the language option 2025-10-08 14:24:50 -05:00
advplyr 29752798f3 Version bump v2.30.0 2025-10-08 10:34:34 -05:00
advplyr 8c86ca4ea5 Merge pull request #4729 from mikiher/build-win-no-compress
Add a script to build an uncompressed windows executable
2025-10-08 10:08:21 -05:00
mikiher 00c62fa494 Add a script to build an uncompressed windows executable 2025-10-08 17:42:00 +03:00
advplyr 6c7f3c7e77 Merge pull request #4695 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-10-07 16:25:14 -05:00
Jan-Eric Myhrgren aec8acbdd7 Translated using Weblate (Swedish)
Currently translated at 97.2% (1131 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-10-07 12:02:09 +02:00
Юра Климович 6e19ad7777 Translated using Weblate (Russian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-10-07 12:02:08 +02:00
Grzegorz Orlowski 3aa95fec11 Translated using Weblate (Polish)
Currently translated at 88.2% (1026 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-10-07 12:02:05 +02:00
Ahetek 37dd46d31f Translated using Weblate (Polish)
Currently translated at 88.2% (1026 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-10-07 12:02:03 +02:00
Petter Schaug-Pettersen 54a996634e Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.4% (1052 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-10-07 12:02:02 +02:00
Oğuz Ersen 54a5e368c2 Translated using Weblate (Turkish)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-10-05 17:02:05 +02:00
FiendFEARing 2d313851d2 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-10-05 17:02:04 +02:00
Максим Горпиніч eb00b19457 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-10-05 17:02:03 +02:00
Kabika82 bbae9acc2d Translated using Weblate (Hungarian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-10-05 17:02:02 +02:00
Milo Ivir a4e8f01f0e Translated using Weblate (Croatian)
Currently translated at 100.0% (1163 of 1163 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-10-05 17:02:01 +02:00
SmileFate 6bdf402da8 Translated using Weblate (Turkish)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-10-03 21:43:22 +00:00
DR 80b0e3546e Translated using Weblate (Hebrew)
Currently translated at 72.9% (847 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2025-10-03 21:43:21 +00:00
B0rax 161f3cb177 Translated using Weblate (German)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-10-03 21:43:21 +00:00
Jan 4a4d4a8f17 Translated using Weblate (German)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-10-03 21:43:20 +00:00
Petri Hämäläinen b21046027c Translated using Weblate (Finnish)
Currently translated at 95.5% (1109 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-10-03 21:43:19 +00:00
max grakov 3a163e1746 Translated using Weblate (Russian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-10-03 21:43:18 +00:00
Amirhossein Ghorbanmehr 3c4e80f1c1 Translated using Weblate (Persian)
Currently translated at 2.2% (26 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fa/
2025-10-03 21:43:18 +00:00
SmileFate 2f3036faba Translated using Weblate (Turkish)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-10-03 21:43:17 +00:00
advplyr 3934461c46 Added translation using Weblate (Persian) 2025-10-03 21:43:16 +00:00
advplyr 123351e08a Merge pull request #4716 from mikiher/async-cover-search
Async Cover Search
2025-10-03 16:43:05 -05:00
advplyr 1280ddfe74 ui/ux disable inputs while cover search in progress, add padding on empty state texts 2025-10-03 16:39:36 -05:00
mikiher 7e89b97a6d Tidy up cover search console logging and error toasts 2025-10-03 09:08:17 +03:00
mikiher 20de2ea388 Add "Best" option to book cover search 2025-10-03 08:23:53 +03:00
mikiher dbb5ee79ac Revert removal of audiobookcovers provider 2025-10-03 08:20:56 +03:00
mikiher c6dabd2620 Shorten timeout and error message for remaining providers 2025-10-02 22:23:12 +03:00
mikiher 26f949b9ba Remove audiobookcovers from provider list 2025-10-02 22:14:48 +03:00
mikiher 7630dbdcb7 Replace cover search with streaming version 2025-10-02 13:30:03 +03:00
mikiher a164c17d38 Reduce provider timout to 10 secs, Shorten error message 2025-10-02 13:26:05 +03:00
advplyr 03da194953 Update for nextjs client, pass all remaining requests through to nextjs 2025-09-28 09:41:15 -05:00
Vito0912 9ce6de3100 Added progress to upload 2025-09-27 17:00:57 +02:00
advplyr e040396b20 Merge pull request #4656 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-09-22 09:41:08 -05:00
Phil Jope bcbec67fec Translated using Weblate (German)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-09-20 23:02:02 +02:00
Vito0912 1543021685 Translated using Weblate (German)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-09-20 23:02:01 +02:00
Salmanegr 577e6aaec9 Translated using Weblate (Arabic)
Currently translated at 95.4% (1108 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-09-19 08:02:02 +00:00
Milo Ivir 77579acfd4 Translated using Weblate (Croatian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-09-10 22:24:06 +00:00
Yuta Imada 9ca98ca750 Translated using Weblate (Japanese)
Currently translated at 15.5% (180 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-09-10 22:24:05 +00:00
Satanowski feb225d3a6 Translated using Weblate (Polish)
Currently translated at 82.3% (956 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-09-10 22:24:05 +00:00
Yuta Imada e501aa4f1e Translated using Weblate (Japanese)
Currently translated at 15.4% (179 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-09-10 22:24:04 +00:00
peter cerny 104f6e6c58 Translated using Weblate (Slovak)
Currently translated at 99.9% (1160 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-09-10 22:24:03 +00:00
jhonthan 552d8ae3b8 Translated using Weblate (Portuguese (Brazil))
Currently translated at 67.7% (787 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-09-10 22:24:03 +00:00
advplyr a41e9bae5d Merge pull request #4664 from advplyr/episode_download_fallback
Fix issue with episode downloads without streams
2025-09-10 17:23:53 -05:00
advplyr a456865ec0 Fix issue with episode downloads without streams, fallback to regular dl on ffprobe fail 2025-09-10 17:10:00 -05:00
advplyr 85d5531bc1 Update chapter editor remove redirect on save or delete all #4650 2025-09-07 17:50:59 -05:00
advplyr 4b840f9c97 Merge pull request #4627 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-09-05 17:24:15 -05:00
peter cerny b9510a69fe Translated using Weblate (Slovak)
Currently translated at 97.7% (1135 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-09-06 00:22:25 +02:00
Losicek d737a66af2 Translated using Weblate (Czech)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-09-06 00:22:24 +02:00
Plazec 576d18d8d6 Translated using Weblate (Czech)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-09-06 00:22:23 +02:00
petr-prikryl d238b02bd2 Translated using Weblate (Czech)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-09-06 00:22:23 +02:00
kuci-JK c6cb13ed39 Translated using Weblate (Czech)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-09-06 00:22:22 +02:00
Jon Erling Hustadnes 44c5dce8aa Translated using Weblate (Norwegian Bokmål)
Currently translated at 90.5% (1051 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-09-06 00:22:21 +02:00
Darius M b726bee4e5 Translated using Weblate (Romanian)
Currently translated at 17.0% (198 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2025-09-06 00:22:20 +02:00
Hnatiucb b07e449043 Translated using Weblate (Romanian)
Currently translated at 17.0% (198 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2025-09-06 00:22:19 +02:00
icutehunter 9273e61f1e Translated using Weblate (Turkish)
Currently translated at 28.5% (332 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-09-06 00:22:18 +02:00
ugyes 1b4a7acf13 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-09-06 00:22:18 +02:00
kuci-JK 68c1395bdf Translated using Weblate (Czech)
Currently translated at 98.1% (1140 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-09-06 00:22:17 +02:00
Hnatiucb a007a9ec98 Translated using Weblate (Romanian)
Currently translated at 3.7% (44 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2025-09-06 00:22:16 +02:00
tngch 8b33b5e383 Translated using Weblate (Japanese)
Currently translated at 14.2% (166 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-09-06 00:22:15 +02:00
Jan-Eric Myhrgren c81b762d52 Translated using Weblate (Swedish)
Currently translated at 97.2% (1129 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-09-06 00:22:15 +02:00
Hnatiucb c53a5c5a0b Translated using Weblate (Romanian)
Currently translated at 3.3% (39 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ro/
2025-09-06 00:22:14 +02:00
thehijacker 83af75a582 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-09-06 00:22:13 +02:00
Jan-Eric Myhrgren 60389a3bf3 Translated using Weblate (Swedish)
Currently translated at 97.2% (1129 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-09-06 00:22:13 +02:00
biuklija 20cceb3a8f Translated using Weblate (Croatian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-09-06 00:22:11 +02:00
lolly76 7562fb2c21 Translated using Weblate (French)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-09-06 00:22:11 +02:00
Matej Krajčovič c7647aafd7 Translated using Weblate (Slovak)
Currently translated at 96.2% (1117 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-09-06 00:22:10 +02:00
FiendFEARing 4a73247e5c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-09-06 00:22:09 +02:00
Максим Горпиніч 326086c197 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-09-06 00:22:08 +02:00
Jan-Eric Myhrgren 5ff5245476 Translated using Weblate (Swedish)
Currently translated at 97.2% (1129 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-09-06 00:22:08 +02:00
advplyr 856cf180a5 Fix chapter editor overflow, set custom wrap breakpoint #4652 2025-09-05 17:21:55 -05:00
votex001 fbe9971a8b [fix] prevent duplicates in multi-selects 2025-09-03 18:19:52 +03:00
advplyr 6ea70608a1 Merge pull request #4636 from nichwall/multiselect_duplicate_entries
Fix: MultiSelect causes web client to become unresponsive if duplicate keys exist
2025-09-02 18:37:07 -04:00
advplyr ba7160c305 Add index to removeItem on multiselect keydown 2025-09-02 17:31:48 -05:00
advplyr 7d048b7a50 Merge pull request #4635 from Vito0912/feat/OIDCfix
Fix Invalid callback URL - must be same-origin for NPM users
2025-09-02 18:18:52 -04:00
Nicholas Wallace afab429c75 Fix: ensure all keys are unique in MultiSelect 2025-08-30 10:01:25 -07:00
Vito0912 50e2fe7fd2 Fix http/https error 2025-08-30 17:47:21 +02:00
advplyr c7c21cc137 Version bump v2.29.0 2025-08-25 17:10:29 -05:00
advplyr 7e4c7a7e3b Merge pull request #4618 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-08-25 17:50:57 -04:00
FiendFEARing 40babc9650 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-25 21:50:34 +00:00
Максим Горпиніч 7a94f014ea Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-25 21:50:33 +00:00
Jan-Eric Myhrgren 32adb1bafd Translated using Weblate (Swedish)
Currently translated at 97.5% (1132 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-25 21:50:32 +00:00
Yurt Page f9a6239049 Translated using Weblate (Russian)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-25 21:50:32 +00:00
Mathias Franco 8dee1ec942 Translated using Weblate (Dutch)
Currently translated at 100.0% (1161 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-25 21:50:31 +00:00
laxandrea 58e43cc6a7 Translated using Weblate (Italian)
Currently translated at 99.6% (1157 of 1161 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-08-25 21:50:30 +00:00
Jan-Eric Myhrgren b8999fbc37 Translated using Weblate (Swedish)
Currently translated at 97.3% (1126 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-25 21:50:29 +00:00
advplyr 0dda4b6b27 Added translation using Weblate (Basque) 2025-08-25 21:50:29 +00:00
Kabika82 817f2f6915 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1157 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-08-25 21:50:28 +00:00
idojius86 77fc6bba1a Translated using Weblate (Spanish)
Currently translated at 97.1% (1124 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-08-25 21:50:28 +00:00
Jan-Eric Myhrgren c66d652a53 Translated using Weblate (Swedish)
Currently translated at 97.3% (1126 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-25 21:50:27 +00:00
thehijacker 86bddba5c3 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1157 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-08-25 21:50:26 +00:00
Jan-Eric Myhrgren 7779fd2972 Translated using Weblate (Swedish)
Currently translated at 96.8% (1121 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-25 21:50:26 +00:00
Yurt Page 05a4577792 Translated using Weblate (Russian)
Currently translated at 100.0% (1157 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-25 21:50:25 +00:00
laxandrea 56dc042282 Translated using Weblate (Italian)
Currently translated at 99.3% (1149 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-08-25 21:50:24 +00:00
advplyr 95973243a6 Update book library progress sort labels as per discord vote 2025-08-25 16:50:10 -05:00
John 18ad23d016 Issue 4540 New SortBy Options: Started Date & Finished Date (#4575)
---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2025-08-24 16:54:38 -05:00
advplyr e258f122f1 Update weblate readme widget to use vertical bar chart 2025-08-24 08:41:31 -05:00
advplyr 18200a8f01 Merge pull request #4533 from sir-wilhelm/wmic_replacement
Use PowerShell to get windows drive paths.
2025-08-23 17:55:50 -04:00
advplyr 9c47f404c9 Fix current author not showing in podcast match #4617 2025-08-23 16:55:32 -05:00
advplyr 2f6de71a3a Fix match tab local storage book provider being updated by podcast matching #4615 2025-08-22 08:35:11 -05:00
advplyr deb121c523 Fix podcast itunesId not set on create or update from match #4614 2025-08-22 08:20:49 -05:00
advplyr 320e4dfb47 Merge pull request #4586 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-08-20 18:47:09 -04:00
FiendFEARing 6194c48549 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1157 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-20 22:45:34 +00:00
Максим Горпиніч 6aa9ecaaba Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1157 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-20 22:45:33 +00:00
Jan-Eric Myhrgren b3d020b89f Translated using Weblate (Swedish)
Currently translated at 96.8% (1121 of 1157 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-20 22:45:33 +00:00
owlcollector e196a6e5ca Translated using Weblate (Japanese)
Currently translated at 9.7% (113 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-08-20 22:45:32 +00:00
Jan-Eric Myhrgren 73cf22b499 Translated using Weblate (Swedish)
Currently translated at 96.7% (1118 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-20 22:45:32 +00:00
ugyes ac7464ce7e Translated using Weblate (Hungarian)
Currently translated at 99.8% (1154 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-08-20 22:45:31 +00:00
B0rax 84e742f2a5 Translated using Weblate (German)
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-20 22:45:30 +00:00
Daniel Schosser a1e882cbf1 Translated using Weblate (German)
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-20 22:45:29 +00:00
Zhelyan Radoev 09121acbd5 Translated using Weblate (Bulgarian)
Currently translated at 87.3% (1010 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-08-20 22:45:28 +00:00
owlcollector 5b9df84ba3 Translated using Weblate (Japanese)
Currently translated at 8.5% (99 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-08-20 22:45:28 +00:00
Zhelyan Radoev 266db491aa Translated using Weblate (Bulgarian)
Currently translated at 83.9% (971 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-08-20 22:45:27 +00:00
FiendFEARing c7a317a87b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-20 22:45:26 +00:00
Максим Горпиніч b027f3bda1 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-20 22:45:26 +00:00
vanapro1 cea991b82f Translated using Weblate (Russian)
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-20 22:45:25 +00:00
biuklija 7e2b51e6d2 Translated using Weblate (Croatian)
Currently translated at 100.0% (1156 of 1156 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-08-20 22:45:24 +00:00
Hang Pham 8f310b6bf0 Translated using Weblate (Vietnamese)
Currently translated at 62.2% (708 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/vi/
2025-08-20 22:45:24 +00:00
Sneaky b2a5fb46f1 Translated using Weblate (Swedish)
Currently translated at 96.6% (1100 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-20 22:45:23 +00:00
Jan-Eric Myhrgren 6d7639853b Translated using Weblate (Swedish)
Currently translated at 96.6% (1100 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-20 22:45:22 +00:00
ugyes 3a16acbba4 Translated using Weblate (Hungarian)
Currently translated at 99.8% (1136 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-08-20 22:45:22 +00:00
Troj@ 027e1efaca Translated using Weblate (Belarusian)
Currently translated at 66.5% (757 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-08-20 22:45:21 +00:00
Ivan Smoliakov d1fabba86b Translated using Weblate (Russian)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-20 22:45:20 +00:00
Mathias Franco b290a4ada3 Translated using Weblate (Dutch)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-20 22:45:19 +00:00
Paolo Ricci bb477c617e Translated using Weblate (Italian)
Currently translated at 99.7% (1135 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-08-20 22:45:19 +00:00
Michael Förster 9238c38842 Translated using Weblate (German)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-20 22:45:18 +00:00
Daniel Schosser d268516fcb Translated using Weblate (German)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-20 22:45:17 +00:00
Vito0912 d353cff1ae Translated using Weblate (German)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-20 22:45:17 +00:00
kuci-JK 604f17f60b Translated using Weblate (Czech)
Currently translated at 98.6% (1123 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-08-20 22:45:16 +00:00
advplyr 3911a7273b Merge pull request #4595 from renesat/fix/listening-ipv6-url
Fix listening url log with ipv6 host
2025-08-20 18:45:04 -04:00
advplyr 138bb563b8 Update ipv6 server listening log 2025-08-20 17:42:22 -05:00
advplyr 3801ef062a Merge pull request #4596 from renesat/fix/redirect-url
Fix freeze on some audio sources
2025-08-19 18:11:13 -04:00
advplyr e4b9ac5446 Add episode updated translation for podcast match #4606 2025-08-19 16:52:50 -05:00
advplyr 9987d219f8 Remove success toast on podcast episodes removed #4606 2025-08-19 16:50:43 -05:00
advplyr dc7045c562 Merge pull request #4608 from laxandrea/remove-token-from-hls-url
Remove token from hls url
2025-08-19 17:29:10 -04:00
laxandrea 2cc6e56bd1 remove token from hls url
- following PR #4263
2025-08-19 15:29:49 +02:00
advplyr a89a24e48e Merge pull request #4598 from advplyr/episode_meta_tagging
Update podcast episode downloads to always attempt embedding meta tags
2025-08-17 10:20:54 -04:00
advplyr a968aca304 Update podcast episode downloads to always attempt embedding meta tags regardless of format 2025-08-17 09:05:29 -05:00
renesat 8d1f460640 Fix freeze on some audio sources 2025-08-16 18:56:23 +02:00
renesat 553ffd1934 Fix listening url log with ipv6 host 2025-08-16 18:24:31 +02:00
advplyr fd4932cdbb Add additional debug logs for OIDC login 2025-08-15 17:23:20 -05:00
advplyr dcaca43817 Merge pull request #4384 from josh-vin/feat/ChaptersEnhancments
Enhancement: Improves chapter editing and adds bulk import
2025-08-14 17:38:56 -04:00
advplyr 0eed4e82f9 Fix bulk add chapter icon button tooltip 2025-08-14 16:35:28 -05:00
advplyr 2ed2328401 Remove negative chapter end check & tooltip 2025-08-14 16:18:33 -05:00
advplyr 8b260c8bc6 Update bulk chapter modal styles, decreased text and button sizes 2025-08-14 16:16:34 -05:00
advplyr 7dcb9b98a0 Chapter lookup modal add back button to clear lookup results 2025-08-14 16:03:32 -05:00
advplyr 311ac7104e Merge pull request #4590 from advplyr/fix_authorize_race_condition
Fix authorize race condition by not updating the user on token refresh
2025-08-13 09:36:17 -04:00
advplyr 2c45b28d48 Fix authorize race condition by not updating the user on token refresh #4567 2025-08-13 08:31:01 -05:00
advplyr b53613f82c Merge pull request #4552 from Toby222/master
Replace some SVG icons with material-symbols
2025-08-12 18:55:50 -04:00
advplyr 751371abb8 Update ReadIcon svg with material-symbols 2025-08-12 17:46:01 -05:00
advplyr 6365c02875 Update explicit material symbols icon to fill 2025-08-12 17:40:48 -05:00
advplyr fb3834156b Version bump v2.28.0 2025-08-10 17:42:32 -05:00
advplyr c03f3f722d Merge pull request #4559 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-08-10 18:31:40 -04:00
FiendFEARing a06f48ca29 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-10 22:26:37 +00:00
NickSkier 9d79552dda Translated using Weblate (Russian)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-10 22:26:36 +00:00
Laurin Sorgend ed98614b6f Translated using Weblate (German)
Currently translated at 99.9% (1137 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-10 22:26:36 +00:00
owlcollector 09dd2cc79c Translated using Weblate (Japanese)
Currently translated at 6.0% (69 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-08-10 22:26:35 +00:00
weblate.user.1274 e87237048a Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.1% (1049 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-08-10 22:26:35 +00:00
Kent Henriksen d71968fd80 Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.1% (1049 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-08-10 22:26:34 +00:00
Thomas f83c605ae1 Translated using Weblate (French)
Currently translated at 99.1% (1128 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-08-10 22:26:34 +00:00
J. Lavoie 4325f470dd Translated using Weblate (German)
Currently translated at 99.8% (1136 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-10 22:26:33 +00:00
numerfolt 800ecf8e82 Translated using Weblate (German)
Currently translated at 99.8% (1136 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-10 22:26:32 +00:00
Vito0912 5cb143d50b Translated using Weblate (German)
Currently translated at 99.8% (1136 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-10 22:26:32 +00:00
Troj@ 798c73c66c Translated using Weblate (Belarusian)
Currently translated at 64.6% (736 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-08-10 22:26:31 +00:00
Максим Горпиніч 0fa7c46274 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-10 22:26:31 +00:00
Kent Henriksen c2d420ec70 Translated using Weblate (Norwegian Bokmål)
Currently translated at 91.0% (1036 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-08-10 22:26:30 +00:00
biuklija 152daf7bf3 Translated using Weblate (Croatian)
Currently translated at 100.0% (1138 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-08-10 22:26:29 +00:00
Ashish Wadekar 8d99249e50 Translated using Weblate (Hindi)
Currently translated at 8.7% (100 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hi/
2025-08-10 22:26:29 +00:00
Camille de Lune c6724ba353 Translated using Weblate (French)
Currently translated at 99.1% (1128 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-08-10 22:26:28 +00:00
Aleksandr Zakirov a519d44666 Translated using Weblate (Estonian)
Currently translated at 65.4% (745 of 1138 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/et/
2025-08-10 22:26:27 +00:00
Grzegorz Orlowski 7e8bf977cc Translated using Weblate (Polish)
Currently translated at 82.9% (942 of 1135 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-08-10 22:26:27 +00:00
advplyr 4018be6330 Fix oidc auto-register not cleaning up new user on errors #4563 2025-08-10 17:26:15 -05:00
advplyr 99a3867ce9 Update callback url check
Co-authored-by: Denis Arnst <git@sapd.eu>
2025-08-10 17:08:25 -05:00
advplyr 2116f60133 Merge pull request #4565 from advplyr/redirect_transcode_requests
Fix server crash when transcode requests are made to the direct play endpoint
2025-08-07 18:31:45 -04:00
advplyr 794f0ef42a Fix server crash when transcode requests are made to the direct play endpoint #4555 2025-08-07 17:21:05 -05:00
Josh Vincent 3e423839a1 Fixes UI for Bulk Chapter adder, and changes logic around locking 2025-08-04 18:33:06 -06:00
Josh Vincent 2773c8c4a9 Merge remote-tracking branch 'josh-vin/master' into feat/ChaptersEnhancments 2025-08-04 18:32:28 -06:00
advplyr e510174f12 Merge pull request #4557 from Vito0912/cors
Allow a whitelist of CORS origins
2025-08-04 19:02:30 -04:00
advplyr 08c9e8d47d Fix i18n string order 2025-08-04 17:56:56 -05:00
advplyr 1908ec3df5 Remove commented out experimental features setting 2025-08-04 17:54:59 -05:00
advplyr df3878d4ca Add Security section to settings with allowed cors origin setting, increase width of setting inputs 2025-08-04 17:54:29 -05:00
Vito0912 1097de6f1f now updates the input field 2025-08-04 19:17:46 +02:00
Vito0912 e408070b19 better heading 2025-08-03 14:02:33 +02:00
Vito0912 af67c2e86f locale 2025-08-03 13:57:44 +02:00
Vito0912 6a52d2a968 CORS 2025-08-03 13:52:58 +02:00
advplyr 3337b3af18 Version bump v2.27.0 2025-08-02 17:53:27 -05:00
advplyr 835d2c7f36 Merge pull request #4535 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-08-02 18:52:04 -04:00
FiendFEARing 03f91099e0 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1135 of 1135 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-02 15:02:00 +02:00
Максим Горпиніч c04afd0787 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1135 of 1135 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-02 15:01:59 +02:00
Grzegorz Orlowski b03bd79f5d Translated using Weblate (Polish)
Currently translated at 77.6% (881 of 1135 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-08-02 15:01:57 +02:00
Tobias Berger 5ef632a7eb Replace some SVG icons with material-symbols 2025-08-01 09:20:34 +02:00
Troj@ 79b4042e8e Translated using Weblate (Belarusian)
Currently translated at 63.6% (721 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-08-01 02:51:15 +02:00
FiendFEARing 8f718ef91c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-01 02:51:15 +02:00
Pepijn 4053b20623 Translated using Weblate (Dutch)
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-01 02:51:15 +02:00
Remco Schrijver c4d654635f Translated using Weblate (Dutch)
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-01 02:51:15 +02:00
enosh ef5d0ffa48 Translated using Weblate (Hebrew)
Currently translated at 74.7% (847 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2025-08-01 02:51:15 +02:00
Troj@ 6a826cdb36 Translated using Weblate (Belarusian)
Currently translated at 58.1% (659 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-08-01 02:51:15 +02:00
thehijacker 1d837f5f21 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-08-01 02:51:15 +02:00
Максим Горпиніч 80873b379c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-01 02:51:15 +02:00
Remco Schrijver 82a8f8f126 Translated using Weblate (Dutch)
Currently translated at 98.6% (1118 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-01 02:51:15 +02:00
Kabika82 4725a466da Translated using Weblate (Hungarian)
Currently translated at 100.0% (1133 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-08-01 02:51:15 +02:00
Jannik 031edc870c Translated using Weblate (German)
Currently translated at 99.8% (1131 of 1133 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-01 02:51:15 +02:00
Jan-Eric Myhrgren 625e2445b5 Translated using Weblate (Swedish)
Currently translated at 96.2% (1089 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-08-01 02:51:15 +02:00
max grakov 1640af2f1c Translated using Weblate (Russian)
Currently translated at 100.0% (1131 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-08-01 02:51:15 +02:00
Remco Schrijver c76f76cc27 Translated using Weblate (Dutch)
Currently translated at 98.4% (1113 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-08-01 02:51:15 +02:00
ugyes 74af212293 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1131 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-08-01 02:51:15 +02:00
Vito0912 e04efb9c6a Translated using Weblate (German)
Currently translated at 99.9% (1130 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-01 02:51:14 +02:00
B0rax ee17e7a555 Translated using Weblate (German)
Currently translated at 99.9% (1130 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-08-01 02:51:14 +02:00
FiendFEARing 694a852c07 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1131 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-08-01 02:51:14 +02:00
Максим Горпиніч 18068bb261 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1131 of 1131 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-08-01 02:51:14 +02:00
Mikkel Dupont Olesen 71257f6c6c Translated using Weblate (Danish)
Currently translated at 99.4% (1124 of 1130 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-08-01 02:51:14 +02:00
advplyr 4d70929d2e Add locale strings for user stats heatmap #4550 2025-07-31 19:51:05 -05:00
advplyr 578e9559e4 Merge pull request #4551 from chriscam85/master
Including total durations into the de-branding from #4226 as warning message is always present currently
2025-07-31 20:23:47 -04:00
advplyr 894ea0b80a Update chapter data log 2025-07-31 19:19:11 -05:00
Chris Campanile e54571f011 Including total durations into the de-branding from #4226 as warning message is always present currently 2025-07-31 16:48:05 -07:00
Josh Vincent 77d7a50b99 Merge remote-tracking branch 'josh-vin/master' into feat/ChaptersEnhancments 2025-07-30 16:51:12 -06:00
advplyr 32da0f1224 Merge pull request #4542 from advplyr/progress_updated_sort
Add book library sort by progress updated #1215
2025-07-28 15:13:40 -04:00
advplyr 2054accdc9 Update library sort dropdown to use max height available 2025-07-28 15:07:57 -04:00
advplyr 7d8b857c77 Add book library sort by progress updated #1215 2025-07-28 14:58:28 -04:00
advplyr 0107cb4782 UI/UX fix x overflow for sessions tables on mobile 2025-07-26 09:45:44 -05:00
advplyr f273eee807 Merge pull request #4534 from michaeldvinci/sepia-theme
Add 'sepia' theme to EpubReader
2025-07-25 17:34:34 -05:00
advplyr 4af21b079a Fix epub toc search input colors for themes 2025-07-25 17:31:59 -05:00
Michael Vinci c9eaf2db2d Add 'sepia' theme to EpubReader 2025-07-25 17:01:16 -05:00
sir-wilhelm cae1560344 Use PowerShell to get windows drive paths.
wmic has been deprecated on newer versions of Windows 11 and is not installed.

resolves #4531
2025-07-25 12:34:29 -05:00
advplyr a5fb0d9cdb Merge pull request #4530 from advplyr/fix_ereader_socket_event
Fix ereader update socket event sending all devices #4529
2025-07-24 17:33:42 -05:00
advplyr 53c80d9798 Merge pull request #4528 from FelixSche/master
Update SideRail.vue
2025-07-24 17:32:44 -05:00
advplyr 832165716b Fix ereader update socket event sending all devices #4529 2025-07-24 17:29:08 -05:00
Felix d9f2d8bf1d Update SideRail.vue
Changed cursor at version to pointer
2025-07-24 13:57:26 +02:00
advplyr a7a3a56509 Version bump v2.26.3 2025-07-23 17:18:51 -05:00
advplyr 4082fadf90 Merge pull request #4525 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-07-23 17:17:51 -05:00
FiendFEARing 93160b83bf Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1130 of 1130 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-23 13:06:32 +02:00
Максим Горпиніч 472240f994 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1130 of 1130 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-07-23 13:06:31 +02:00
Dmitry c3f0fb8e5e Translated using Weblate (Russian)
Currently translated at 100.0% (1130 of 1130 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-07-23 13:06:30 +02:00
Daniel Schosser b156ebeb9f Translated using Weblate (German)
Currently translated at 99.9% (1129 of 1130 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-23 13:06:29 +02:00
advplyr e4c775c847 Merge pull request #4523 from advplyr/fix_change_empty_root_password
Update change password to support null or empty string passwords #4522
2025-07-22 17:01:07 -05:00
advplyr 45e8e72759 Update change password to support null or empty string passwords #4522 2025-07-22 15:17:00 -05:00
advplyr 0ae7340889 Merge pull request #4520 from advplyr/fix_podcast_session_track_index
Fix podcast episode track index null in playback session
2025-07-22 15:04:22 -05:00
advplyr 8c38987d92 Fix podcast episode track index null in playback session 2025-07-22 14:44:36 -05:00
advplyr 878f0787ba Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2025-07-21 17:07:12 -05:00
advplyr 880d85eaef Version bump v2.26.2 2025-07-21 17:07:06 -05:00
advplyr f7aaebc1de Merge pull request #4508 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-07-21 17:01:15 -05:00
Charlie d96ebbe82d Translated using Weblate (French)
Currently translated at 100.0% (1129 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-07-22 00:00:51 +02:00
kuci-JK 70d67156f0 Translated using Weblate (Czech)
Currently translated at 99.2% (1121 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-07-22 00:00:51 +02:00
Serhat Gülaştı f293b317be Translated using Weblate (Turkish)
Currently translated at 28.3% (320 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-07-22 00:00:51 +02:00
Simple16 1f23794f88 Translated using Weblate (Russian)
Currently translated at 99.9% (1128 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-07-22 00:00:51 +02:00
FiendFEARing e6bfd118f6 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1129 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-22 00:00:51 +02:00
SunSpring 1166400ab1 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1129 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-22 00:00:51 +02:00
Максим Горпиніч 55f0ac871b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1129 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-07-22 00:00:51 +02:00
Angelo Prandelli 3584f6e24f Translated using Weblate (Italian)
Currently translated at 98.2% (1109 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-07-22 00:00:51 +02:00
biuklija 23bf2594c8 Translated using Weblate (Croatian)
Currently translated at 100.0% (1129 of 1129 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-07-22 00:00:51 +02:00
advplyr 8fb460ce05 Merge pull request #4319 from mikiher/audible-confidence-score
Audible confidence score
2025-07-21 17:00:44 -05:00
advplyr 8c4bbfd6a2 Add match confidence as a badge on match book card 2025-07-21 16:52:21 -05:00
advplyr 742961e0b8 Merge pull request #4510 from advplyr/fix_set_token
Fix set token on page load #4509
2025-07-18 17:11:09 -05:00
advplyr 5b6807892f Fix set token on page load #4509 2025-07-18 16:59:27 -05:00
advplyr b911a25c57 Version bump v2.26.1 2025-07-16 17:16:43 -05:00
advplyr 53110674e4 Merge pull request #4492 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-07-16 17:15:48 -05:00
Grzegorz Orlowski f963cd4753 Translated using Weblate (Polish)
Currently translated at 76.1% (859 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-07-16 22:11:56 +00:00
Grzegorz Orlowski 0dccc3bcae Translated using Weblate (Polish)
Currently translated at 76.0% (858 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-07-16 22:11:55 +00:00
FiendFEARing 5b4fd5b254 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-16 22:11:55 +00:00
Fredrik Lindqvist bdb9d3caeb Translated using Weblate (Swedish)
Currently translated at 95.2% (1074 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-07-16 22:11:54 +00:00
Jannik 9aca824b59 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:53 +00:00
Jannik 8e891805eb Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:53 +00:00
Jannik 2760517445 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:52 +00:00
Jannik 889ee33320 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:51 +00:00
Jannik 4f65801713 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:50 +00:00
Jannik 3e75acd4ef Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:50 +00:00
Jannik 3e8fe2ef60 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:49 +00:00
Jannik 0bc441de20 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:48 +00:00
Jannik a8c2f0d4c8 Translated using Weblate (German)
Currently translated at 99.9% (1127 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-16 22:11:48 +00:00
FiendFEARing b59da8bd0c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-16 22:11:47 +00:00
FiendFEARing 77cb4f75c6 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-16 22:11:46 +00:00
Максим Горпиніч 9cf1711fae Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-07-16 22:11:45 +00:00
Jan-Eric Myhrgren f472116dc3 Translated using Weblate (Swedish)
Currently translated at 94.5% (1066 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-07-16 22:11:45 +00:00
Jan-Eric Myhrgren c7eb9d7799 Translated using Weblate (Swedish)
Currently translated at 94.5% (1066 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-07-16 22:11:44 +00:00
Jan-Eric Myhrgren c66380eaeb Translated using Weblate (Swedish)
Currently translated at 94.5% (1066 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-07-16 22:11:43 +00:00
Simple16 1bebb22705 Translated using Weblate (Russian)
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-07-16 22:11:43 +00:00
Simple16 4e96649fe3 Translated using Weblate (Russian)
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-07-16 22:11:42 +00:00
Simple16 a21cec806e Translated using Weblate (Russian)
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-07-16 22:11:41 +00:00
biuklija 8a3b8d2249 Translated using Weblate (Croatian)
Currently translated at 100.0% (1128 of 1128 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-07-16 22:11:40 +00:00
advplyr 581277914c Merge pull request #4503 from advplyr/session_modal_user
Update sessions modal to show username & update legacy token input with warning
2025-07-16 17:11:28 -05:00
advplyr e678fe6e2f Update sessions modal to show username & update sessions endpoints to always return username 2025-07-16 16:56:07 -05:00
advplyr 3845940245 Add warning under legacy token input on users page to use api keys instead 2025-07-16 16:43:53 -05:00
advplyr 6c63e2131c Update AllowCors to apply to every request #4497 2025-07-15 16:28:41 -05:00
advplyr e25e2b238f Merge pull request #4493 from advplyr/localize_durations
Localize elapsed duration on sessions tables
2025-07-14 17:28:58 -05:00
advplyr 99110f587a Localize elapsed duration on sessions tables 2025-07-14 17:17:39 -05:00
advplyr b553e959e2 Merge pull request #4486 from advplyr/fix_oidc_create_user
Fix OIDC auto register user #4485
2025-07-13 17:09:40 -05:00
advplyr f7b94a4b6d Fix OIDC auto register user #4485 2025-07-13 17:04:02 -05:00
mikiher e9a705587a Merge branch 'advplyr:master' into audible-confidence-score 2025-07-13 10:13:00 +03:00
advplyr 264ae928a9 Version bump v2.26.0 2025-07-12 11:43:14 -05:00
advplyr f5248a9f00 Merge pull request #4476 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-07-12 11:41:54 -05:00
FiendFEARing 3473ff594a Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-12 18:32:35 +02:00
FiendFEARing 20bb6e13b5 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-12 18:32:35 +02:00
FiendFEARing a05d32b1d7 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-12 18:32:34 +02:00
Kabika82 c6b3521cb6 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-07-12 18:32:34 +02:00
Kabika82 2444504c6a Translated using Weblate (Hungarian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-07-12 18:32:33 +02:00
advplyr d38532c07a Merge pull request #4444 from advplyr/jwt_auth_refactor
Implement new JWT auth
2025-07-12 11:32:22 -05:00
advplyr 4f7831611f Update auth re-login i18n string 2025-07-12 11:23:08 -05:00
advplyr d09db19cd5 Update re-login message to show for users without github discussion link, add message to i18n strings 2025-07-12 11:21:52 -05:00
advplyr 030e43f382 Support disabled rate limiter by setting max to 0, add logs when rate limit is changed from default 2025-07-12 10:51:07 -05:00
advplyr f081a7fdc1 Update rate limiter to use requestIp as key, pass in configurable error message 2025-07-12 10:32:35 -05:00
advplyr f0d5f46199 Merge branch 'master' into jwt_auth_refactor 2025-07-11 16:59:19 -05:00
advplyr 0b8f6db45e Merge pull request #4445 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-07-11 16:58:05 -05:00
advplyr 806c0a2991 Remove return_tokens query param for login 2025-07-11 16:01:45 -05:00
advplyr 7d6d3e6687 Move invalidate refresh token to TokenManager 2025-07-11 14:43:07 -05:00
FiendFEARing ad07ed7e25 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-11 03:04:29 +02:00
advplyr d3402e30c2 Update ereaders to handle refreshing, epubjs to use custom request method, separate accessToken in store 2025-07-10 16:54:28 -05:00
advplyr 25fe4dee3a Update epub reader to use axios for handling refresh tokens 2025-07-09 17:03:10 -05:00
advplyr 3c21c82ce1 Merge branch 'master' into jwt_auth_refactor 2025-07-09 14:55:05 -05:00
thehijacker 3c8876a37d Translated using Weblate (Slovenian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-07-09 19:54:31 +00:00
thehijacker fba70c9831 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-07-09 19:54:30 +00:00
SunSpring 27e40d16fd Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-09 19:54:30 +00:00
FiendFEARing 448cbf8530 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-09 19:54:29 +00:00
SunSpring f1153f9da5 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-09 19:54:28 +00:00
Raj d09a21d922 Translated using Weblate (Gujarati)
Currently translated at 16.6% (184 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/gu/
2025-07-09 19:54:28 +00:00
Richard Požgay 62afa3c3ee Translated using Weblate (Czech)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-07-09 19:54:27 +00:00
Richard Požgay 85446be0e5 Translated using Weblate (Czech)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-07-09 19:54:27 +00:00
Michal 018ca8e7ee Translated using Weblate (Slovak)
Currently translated at 99.9% (1107 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-07-09 19:54:26 +00:00
Максим Горпиніч f02453ac92 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-07-09 19:54:25 +00:00
DavevanIersel 84b77f4c7f Translated using Weblate (Dutch)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-07-09 19:54:25 +00:00
FiendFEARing d41276ba8c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.9% (1107 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-09 19:54:24 +00:00
FiendFEARing 576d7dc024 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.9% (1107 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-07-09 19:54:24 +00:00
Максим Горпиніч 6d2b1df560 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-07-09 19:54:23 +00:00
DavevanIersel 8255e4308c Translated using Weblate (Dutch)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-07-09 19:54:22 +00:00
DavevanIersel 794adf0292 Translated using Weblate (Dutch)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-07-09 19:54:22 +00:00
Daniel Schosser f2e0b9762c Translated using Weblate (German)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-09 19:54:21 +00:00
Daniel Schosser 7d0def0edb Translated using Weblate (German)
Currently translated at 100.0% (1108 of 1108 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-09 19:54:21 +00:00
Vito0912 0653572396 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-09 19:54:20 +00:00
Vito0912 d9a3750667 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-07-09 19:54:19 +00:00
advplyr 9c0c7b6b08 Merge pull request #4469 from advplyr/fix_scanner_deleting_single_file_books
Fix scanner after deleting single file books #4459
2025-07-09 14:54:05 -05:00
advplyr df1391d93f Fix scanner after deleting single file books #4459 2025-07-09 13:42:53 -05:00
mikiher bf6d81b333 Merge branch 'advplyr:master' into audible-confidence-score 2025-07-09 09:04:52 +03:00
advplyr 8775e55762 Update jwt secret handling 2025-07-08 16:39:50 -05:00
advplyr d0d152c20d Seperate setUserToken from setUser in store 2025-07-08 09:45:24 -05:00
advplyr 4ff7355262 Fix hashPassword 2025-07-08 09:14:07 -05:00
advplyr 6cc7a44a22 Update oidc redirect to pass both new and old token in url 2025-07-07 17:21:25 -05:00
advplyr ad092ef8f8 Merge branch 'master' into jwt_auth_refactor 2025-07-07 16:50:58 -05:00
advplyr 4102ed8be4 Fix LazySeriesCard component test 2025-07-07 16:49:20 -05:00
advplyr 691f291843 Update LibraryItemController unit test 2025-07-07 16:26:17 -05:00
advplyr ac381854e5 Add rate limiter for auth endpoints 2025-07-07 16:23:15 -05:00
advplyr 9c8900560c Seperate out auth strategies, update change password to return error status codes 2025-07-07 15:04:40 -05:00
advplyr d9cfcc86e7 Update oidc to return refresh token in response body for mobile 2025-07-07 09:16:07 -05:00
advplyr ce803dd6de Use getServerSetting to ensure serverSettings is set before accessing 2025-07-06 17:39:03 -05:00
advplyr 97afd22f81 Refactor Auth to breakout functions in TokenManager, handle token generation for OIDC 2025-07-06 16:43:03 -05:00
advplyr e24eaab3f1 Log when token expiry is set via env var, api-keys create/update returns with user association 2025-07-06 13:10:14 -05:00
advplyr e201247d69 Handle socket re-authentication, fix socket toast to be re-usable, socket cleanup 2025-07-06 11:07:01 -05:00
advplyr a24dae5262 Merge branch 'master' into jwt_auth_refactor 2025-07-06 09:06:39 -05:00
advplyr e59babdf24 Force re-login if using old token, show alert if admin user, add isOldToken flag to user 2025-07-05 17:46:18 -05:00
advplyr 8dbe1e4e5d Fix express.json position 2025-07-04 16:49:45 -05:00
advplyr cdc37ddb0f Use x-refresh-token for alt method of passing refresh token, check x-refresh-token for logout 2025-07-04 13:54:37 -05:00
advplyr f127a7beb5 Update router for internal-api routes 2025-07-03 17:31:38 -05:00
advplyr df60aeb456 Update narrator name to be clickable to filter by narrator 2025-07-02 17:30:00 -05:00
advplyr 30c327d92a Merge pull request #4454 from advplyr/fix_mediaprogress_updatedat_2
Fix manually setting updatedAt of mediaProgresses using progress sync lastUpdate timestamp
2025-07-01 17:08:50 -05:00
advplyr 596bddf791 Fix manually setting updatedAt of mediaProgresses using progress sync lastUpdate timestamp #4366 2025-07-01 16:48:07 -05:00
advplyr 44ff90a6f2 Update refresh endpoint to support override cookie token 2025-07-01 16:31:26 -05:00
advplyr 293851d931 Fix missing translation in remove podcast episode modal #4434 2025-06-30 17:49:05 -05:00
advplyr 8b995a179d Add support for returning refresh token for mobile clients 2025-06-30 17:31:31 -05:00
advplyr 4d32a22de9 Update API Keys to be tied to a user, add apikey lru-cache, handle deactivating expired keys 2025-06-30 14:53:11 -05:00
advplyr af1ff12dbb Add get all, update and delete endpoints. Add api keys config page 2025-06-30 11:32:02 -05:00
advplyr d96ed01ce4 Set up ApiKey model and create Api Key endpoint 2025-06-30 10:12:39 -05:00
advplyr 7610e97f0f Merge pull request #4416 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-06-29 17:32:52 -05:00
advplyr 4f5123e842 Implement new JWT auth 2025-06-29 17:22:58 -05:00
Eigen_art d102065d02 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-06-27 00:22:11 +02:00
Dan Johansen 34315d4c10 Translated using Weblate (Danish)
Currently translated at 99.7% (1104 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-06-27 00:22:10 +02:00
Michael Förster 276a179446 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-27 00:22:10 +02:00
burghy86 4462d32e98 Translated using Weblate (Italian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-06-27 00:22:09 +02:00
SunSpring 9722674072 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-06-27 00:22:09 +02:00
Mathias Franco 35bb77c9c2 Translated using Weblate (Dutch)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-06-27 00:22:08 +02:00
biuklija cf6f49ce75 Translated using Weblate (Croatian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-06-27 00:22:07 +02:00
Daniel Schosser d614373c64 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-27 00:22:07 +02:00
Stefan Ha b9969c78a6 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-27 00:22:06 +02:00
B0rax fbf482d6b6 Translated using Weblate (German)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-27 00:22:06 +02:00
David Havndrup Munch dd74d0a726 Translated using Weblate (Danish)
Currently translated at 98.9% (1095 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-06-27 00:22:05 +02:00
petr-prikryl b13b80e011 Translated using Weblate (Czech)
Currently translated at 99.9% (1106 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-06-27 00:22:04 +02:00
advplyr e384863148 Add support for running in production with dev.js config, node index --prod-with-dev-env 2025-06-26 17:21:58 -05:00
mikiher 9c44fc0d01 Merge branch 'advplyr:master' into audible-confidence-score 2025-06-26 18:09:13 +03:00
advplyr d21fe49ce2 Merge pull request #4430 from advplyr/experimental_next_client
Add ENV REACT_CLIENT_PATH to target a Nextjs frontend instead of Nuxt
2025-06-23 17:23:15 -05:00
advplyr a992400d6a Add ENV REACT_CLIENT_PATH to target a Nextjs frontend instead of Nuxt 2025-06-23 16:56:08 -05:00
advplyr 108b2a60f5 Merge pull request #4425 from Vito0912/feat/addExplicit
Add explicit filter
2025-06-21 17:03:25 -05:00
advplyr af684e6a69 Explicit library filter not shown for users without permission 2025-06-21 17:01:13 -05:00
Vito0912 5336d0525e add explicit to podcasts 2025-06-21 12:29:54 +02:00
Vito0912 bb4eec9355 add explicit 2025-06-21 12:02:44 +02:00
advplyr 28404f37b8 Merge pull request #4422 from advplyr/podcast_episode_duration
Show duration in episode view modal & episode feed modal
2025-06-19 17:35:36 -05:00
advplyr 7b92c15a46 Include durationSeconds on RSS podcast episode parsed from duration 2025-06-19 17:28:21 -05:00
advplyr c150ed4e98 Update view episode modal to include duration & episode feed modal to include duration & size 2025-06-19 17:14:56 -05:00
advplyr cb7632b216 Merge pull request #4419 from advplyr/episode-timestamps-clickable
Episode view modal makes timestamps in description clickable
2025-06-18 17:28:55 -05:00
advplyr b8849677de Episode view modal makes timestamps in description clickable 2025-06-18 17:20:36 -05:00
advplyr 9bf8d7de11 Fix server crash when FantLab provider request times out #4410 2025-06-17 17:21:21 -05:00
advplyr 6634ce8fd4 Merge pull request #4417 from advplyr/book_author_secondary_sort_title
Update book library secondary title sort to use title ignore prefixes
2025-06-17 16:40:59 -05:00
advplyr 9d4303ef7b Update book library secondary title sort to use title ignore prefixes #4414 2025-06-17 16:25:30 -05:00
advplyr 1f7be58124 Fix database cleanup query pulling duplicate mediaProgresses 2025-06-16 17:50:53 -05:00
advplyr 6b8b27b04f Merge pull request #4413 from HadrienPatte/nusqlite3-path
Make `NUSQLITE3_PATH` build arg configurable
2025-06-16 17:22:21 -05:00
Hadrien Patte ba4061e5a4 Make NUSQLITE3_PATH build arg configurable 2025-06-16 23:03:02 +02:00
mikiher 5017e7ce9e Merge branch 'advplyr:master' into audible-confidence-score 2025-06-16 10:26:58 +03:00
advplyr 693dc00fa3 Update local session sync logs to help debug sync errors 2025-06-15 17:21:47 -05:00
advplyr f3f5f3b9bd Version bump v2.25.1 2025-06-14 17:57:19 -05:00
advplyr b515c6c746 Remove mediaProgresses duplicate check 2025-06-14 17:56:35 -05:00
advplyr 35e196238a Version bump v2.25.0 2025-06-14 17:18:53 -05:00
advplyr 2dc93258f1 Merge pull request #4364 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-06-13 17:32:53 -05:00
thehijacker 5123f7d240 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-06-14 00:29:31 +02:00
Usama Khalil 06d3bd76a8 Translated using Weblate (Arabic)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-06-14 00:29:31 +02:00
Ivan Smoliakov 52196afd99 Translated using Weblate (Russian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-06-14 00:29:30 +02:00
ugyes 3e44ee6f50 Translated using Weblate (Hungarian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-06-14 00:29:29 +02:00
Максим Горпиніч 9841826e10 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1107 of 1107 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-06-14 00:29:29 +02:00
Dawid Kuźnicki def93d18ec Translated using Weblate (Polish)
Currently translated at 76.9% (850 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-06-14 00:29:28 +02:00
Rekentek 387a3d05b4 Translated using Weblate (Dutch)
Currently translated at 98.5% (1089 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-06-14 00:29:28 +02:00
Daniel Schosser 398d04fc08 Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-14 00:29:27 +02:00
David Havndrup Munch c5e5e516af Translated using Weblate (Danish)
Currently translated at 98.9% (1093 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-06-14 00:29:27 +02:00
Plazec 1c6f99b876 Translated using Weblate (Czech)
Currently translated at 99.7% (1102 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-06-14 00:29:26 +02:00
Grzegorz Orlowski d0af82e71a Translated using Weblate (Polish)
Currently translated at 76.9% (850 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-06-14 00:29:25 +02:00
Usama Khalil 76e7616439 Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-06-14 00:29:25 +02:00
max grakov fe99a269bc Translated using Weblate (Russian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-06-14 00:29:24 +02:00
thehijacker 5315f65023 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-06-14 00:29:24 +02:00
Максим Горпиніч c2809808c3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-06-14 00:29:23 +02:00
Anders Norman 204ac4f204 Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.6% (1024 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-06-14 00:29:22 +02:00
Arieh Kellermann accd5d1096 Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-06-14 00:29:22 +02:00
advplyr 5025c6a3ea Merge pull request #4383 from JKubovy/improve-podcast-episode-search
Use fuse.js for podcast episode search
2025-06-13 17:29:13 -05:00
advplyr 6d0d1415e4 Add fuse.basic.min.js in libs instead of full npm package, use lower threshold for quick matching 2025-06-13 17:23:24 -05:00
advplyr 514f5c2409 Merge pull request #4394 from Vito0912/feat/addISBNAudible
Added the ISBN for Audible providers (returned data)
2025-06-13 16:21:32 -05:00
advplyr 2cc58b2c8a Merge pull request #4404 from advplyr/podcast_useragents
Update podcast episode downloads to have a fallback user agent string
2025-06-12 17:40:42 -05:00
advplyr 777a055fcd Update podcast episode downloads to have a fallback user agent string 2025-06-12 17:31:12 -05:00
advplyr b45085d2d6 Update podcast episode download user agent to fix #4401 2025-06-12 17:19:24 -05:00
advplyr 22f6e86a12 Fix pathexists filepath back to posix 2025-06-11 16:37:07 -05:00
advplyr dc6783ea76 Merge pull request #4398 from advplyr/pathexists_user_access
Update pathexists endpoint to check user has access to library
2025-06-11 16:31:14 -05:00
advplyr a6f10ca48e Update upload endpoint to check user has access to library 2025-06-11 16:14:51 -05:00
advplyr aac01d6d9a Update pathexists endpoint to check user has access to library 2025-06-11 16:04:18 -05:00
Vito0912 a617994207 added isbn 2025-06-11 08:12:23 +02:00
advplyr 7a33a412fc Merge pull request #4393 from advplyr/fix_pathexists_join
Fix filesystem pathexists path join
2025-06-10 17:20:23 -05:00
advplyr 0135b3560c Fix filesystem pathexists path join 2025-06-10 17:02:42 -05:00
advplyr 6968a5c02a Merge pull request #4378 from Vito0912/feat/PodcastNots
Notifications for failed rss feeds and disabled rss feeds
2025-06-09 16:25:19 -05:00
advplyr 5e2bb0b12c Fix notification js docs and update description/defaults 2025-06-09 16:21:05 -05:00
advplyr 7122756e58 Update notification description grammar 2025-06-09 15:51:14 -05:00
advplyr 8ecc912c2d Merge pull request #4388 from advplyr/book_author_secondary_sort
Update book library sort by author to use title as secondary sort #4380
2025-06-08 17:38:45 -05:00
advplyr c8cea4e6af Update book library sort by author to use title as secondary sort #4380 2025-06-08 17:28:19 -05:00
Josh Vincent 9da0be6d36 Allow clicking on elapsedTime to adjust chapter start 2025-06-08 13:18:41 -06:00
advplyr 0c5d05d319 Fix chapter table on audiobook tools page uneven column widths 2025-06-07 17:10:23 -05:00
Josh Vincent c41bdb951c Moves the lock button and fixes padding on bulk add feature.
Moves the lock button the right of the Title text box.

Enhances the bulk chapter add feature by preserving zero-padding in chapter titles and prevents editing of locked chapters. Also allows Enter to be pressed in the Add Multiple Chapters modal.

Adds a warning toast when attempting to modify locked chapters.

Fixes sizing of boxes on smaller windows
2025-06-07 14:48:05 -06:00
advplyr 4a3eb7727b Merge pull request #4385 from advplyr/clean_duplicate_mediaprogress
Update cleanDatabase to remove duplicate mediaProgresses
2025-06-06 17:17:43 -05:00
advplyr 81640464ba Update cleanDatabase to remove duplicate mediaProgresses 2025-06-06 17:05:07 -05:00
Josh Vincent 54815ea9c7 Add a second to bulk chapters so its valid
This will enable users to go in and fix the chapter timing later but still save easily with the bulk import.
2025-06-06 13:25:20 -06:00
Josh Vincent 679ffed0ea Alphabetizes strings 2025-06-06 11:50:44 -06:00
Josh Vincent 09397cf3de Improves chapter editing and adds bulk import
Adds chapter locking functionality, allowing users to lock individual chapters or all chapters at once to prevent accidental edits.

Implements time increment buttons to precisely adjust chapter start times.

Introduces bulk chapter import functionality, allowing users to quickly add multiple chapters using a detected numbering pattern.

Adds elapsed time display during chapter playback for better user feedback.

Updates UI tooltips and icons for improved clarity and user experience.
2025-06-06 11:22:38 -06:00
Jan Kubovy eda7036f70 Use fuse.js for podcast episode search
Replace levenshtein distance with fuse.js fuzzy searching library. Search in episode's title and subtitle
2025-06-06 10:43:52 +00:00
advplyr e669a8d378 Merge pull request #4370 from Vito0912/feat/MaxFailedEpisodeChecks-
Adds ENV for MaxFailedEpisodeChecks
2025-06-05 15:06:27 -05:00
advplyr 8e01859075 Cast PODCAST_DOWNLOAD_TIMEOUT and MAX_FAILED_EPISODE_CHECKS env vars to numbers 2025-06-05 14:31:12 -05:00
Vito0912 f0525d4f0d abc is hard 2025-06-05 14:09:35 +02:00
Vito0912 84c9c6cb50 move to global 2025-06-05 14:07:35 +02:00
Vito0912 346df3680c local strings 2025-06-05 14:02:29 +02:00
Vito0912 6aa7c8a3d8 added notification 2025-06-05 13:34:18 +02:00
advplyr 704c6f7bde Merge pull request #4374 from Vito0912/feat/allowBase64Images
Corrects removing of attachments for Trix
2025-06-04 16:36:46 -05:00
advplyr f01055f6e6 Merge pull request #4373 from Vito0912/feat/maybeFixPodcast
Potential fix/new knowledge for hangig podcasts
2025-06-04 16:33:40 -05:00
Vito0912 759c58d3f7 remove any attachment 2025-06-04 16:38:01 +02:00
Vito0912 357176b301 catch timeout 2025-06-04 16:15:18 +02:00
Vito0912 9bb4dc3ab0 potential fix 2025-06-04 10:58:44 +02:00
Vito0912 709c33f27a ensure proper type 2025-06-04 10:05:16 +02:00
Vito0912 4d846e225a Adds ENV for MaxFailedEpisodeChecks 2025-06-04 10:02:17 +02:00
advplyr 5dc6d613bd Merge pull request #4361 from Vito0912/feat/encoderSettings
Fix: Audiobook m4b advanced encoder ignore
2025-06-02 16:53:28 -05:00
advplyr 63ccdb68f0 Fix m4b encoder backup file overwriting the encoded file when they have the same filename 2025-06-02 16:50:03 -05:00
Vito0912 424ef1aec3 prettier 2 2025-06-02 19:34:25 +02:00
Vito0912 b6995ba5d1 prettier 2025-06-02 19:33:50 +02:00
Vito0912 9968743a93 fix wrong display and ignored values 2025-06-02 19:32:52 +02:00
advplyr c377b57601 Version bump v2.24.0 2025-06-01 16:00:16 -05:00
advplyr 262d0b46e3 Merge pull request #4350 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-06-01 15:40:16 -05:00
Charlie 32fc4f6555 Translated using Weblate (French)
Currently translated at 99.9% (1104 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-06-01 15:57:47 +02:00
DR 81572adab6 Translated using Weblate (Hebrew)
Currently translated at 76.4% (845 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2025-06-01 00:37:34 +02:00
kuci-JK 1ad2e71fd5 Translated using Weblate (Czech)
Currently translated at 98.9% (1093 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-06-01 00:37:33 +02:00
FiendFEARing db66b9eaeb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-06-01 00:37:32 +02:00
Simple16 28c2e62e61 Translated using Weblate (Russian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-06-01 00:37:32 +02:00
Tommaso Bellandi 96401c377c Translated using Weblate (Italian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-06-01 00:37:31 +02:00
advplyr 9d45880b37 Merge pull request #4355 from advplyr/sanitize_html_description
Sanitize media item & episode description on update
2025-05-31 17:37:18 -05:00
advplyr 9052ceedd3 Sanitize media item & episode description on update 2025-05-31 17:01:58 -05:00
advplyr 4968864498 Fix safari specific issue with line clamp on description #4348 2025-05-30 17:33:15 -05:00
advplyr f44c2d9e11 Merge pull request #4349 from advplyr/trix_prevent_attachments
Update rich text editor to prevent pasting in images from the browser
2025-05-29 17:37:31 -05:00
advplyr 0c8e334b1a Update rich text editor to prevent pasting in images from the browser 2025-05-29 17:27:29 -05:00
advplyr abaa7b5ad0 Add arabic language option 2025-05-28 17:09:39 -05:00
advplyr df01e493ec Merge pull request #4303 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-05-28 17:05:27 -05:00
Adolfo Jayme Barrientos 949c8ce230 Translated using Weblate (Catalan)
Currently translated at 96.2% (1064 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-27 22:57:04 +00:00
Grzegorz Orlowski 9eaa0c26cd Translated using Weblate (Polish)
Currently translated at 73.3% (810 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-05-27 22:57:03 +00:00
Adolfo Jayme Barrientos d71f091e3e Translated using Weblate (Spanish)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-27 22:57:02 +00:00
Biepa 2589121908 Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-05-27 22:57:02 +00:00
ABS translator ff425212e7 Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:57:01 +00:00
thehijacker 243baaf775 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-05-27 22:57:00 +00:00
Jan Schoenfeld 7275b1063b Translated using Weblate (German)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-05-27 22:57:00 +00:00
peter cerny 4fd97510b8 Translated using Weblate (Slovak)
Currently translated at 99.9% (1104 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-27 22:56:59 +00:00
Adolfo Jayme Barrientos 6e67b1d9dd Translated using Weblate (Catalan)
Currently translated at 96.0% (1061 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-27 22:56:59 +00:00
SunSpring 0fc6afec26 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-05-27 22:56:58 +00:00
Adolfo Jayme Barrientos c950ac7d69 Translated using Weblate (Spanish)
Currently translated at 99.9% (1104 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-27 22:56:57 +00:00
Usama Khalil 8979e19e92 Translated using Weblate (Arabic)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:56:57 +00:00
Максим Горпиніч 6a51cb07e8 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-05-27 22:56:56 +00:00
biuklija 846a8c3881 Translated using Weblate (Croatian)
Currently translated at 100.0% (1105 of 1105 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-05-27 22:56:55 +00:00
peter cerny 0cd698cc8d Translated using Weblate (Slovak)
Currently translated at 99.9% (1103 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-27 22:56:55 +00:00
Antoniy Chonkov 13d9462868 Translated using Weblate (Bulgarian)
Currently translated at 81.7% (903 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-05-27 22:56:54 +00:00
peter cerny d8e2ff8b0e Translated using Weblate (Slovak)
Currently translated at 99.5% (1099 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-27 22:56:53 +00:00
Usama Khalil 35c2a5c1a3 Translated using Weblate (Arabic)
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:56:53 +00:00
Antoniy Chonkov 19dc096d22 Translated using Weblate (Bulgarian)
Currently translated at 75.5% (834 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-05-27 22:56:52 +00:00
Usama Khalil 535ebc10f0 Translated using Weblate (Arabic)
Currently translated at 98.5% (1088 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:56:51 +00:00
SunSpring 7486a0659b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-05-27 22:56:51 +00:00
Usama Khalil 273866fe92 Translated using Weblate (Arabic)
Currently translated at 36.5% (404 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:56:50 +00:00
Usama Khalil 6425d95deb Translated using Weblate (Arabic)
Currently translated at 27.4% (303 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-27 22:56:50 +00:00
Vito0912 68a39449a2 Translated using Weblate (German)
Currently translated at 99.6% (1100 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-05-27 22:56:49 +00:00
advplyr 8e08458ea2 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2025-05-27 17:56:32 -05:00
advplyr 1119ddef8a Add RSS Feed Open filter for podcast libraries to match book libraries #4335 2025-05-27 17:56:27 -05:00
advplyr 3d0219a866 Merge pull request #4342 from advplyr/check_path_api_fix
Update pathexists file system API endpoint
2025-05-26 17:12:13 -05:00
advplyr 6ce1806359 Update pathexists file system API endpoint 2025-05-26 16:56:50 -05:00
advplyr f05a513767 Fix m4b encoder bitrate preset selection #4337 2025-05-25 16:12:35 -05:00
advplyr d03c338b48 Fix log for podcast rss feed with no guid #4325 2025-05-24 17:09:58 -05:00
advplyr 5e5a988f7a Merge pull request #4326 from advplyr/fix_mediaprogress_updatedat
Fix MediaProgress not using the lastUpdate time sent for local progress syncs
2025-05-22 17:43:31 -05:00
advplyr 6d1f0b27df Fix MediaProgress not using the lastUpdate time sent for local progress syncs 2025-05-22 17:30:38 -05:00
mikiher de25763a74 Add match confidence display to BookMatchCard 2025-05-21 11:16:46 +03:00
mikiher a894ceb9cf Match confidence calculation for audible results 2025-05-21 10:25:42 +03:00
mikiher 387e58a714 Add levenshteinSimilarity function to utils 2025-05-21 09:57:44 +03:00
advplyr d01a7cb756 Merge pull request #4318 from advplyr/increase_express_json_limit
Update max allowed json request size #4250
2025-05-20 18:04:02 -05:00
advplyr cae874ef05 Update max allowed json request size #4250 2025-05-20 17:44:13 -05:00
advplyr 733afc3e29 Update edit series sequence to show error when sequence has spaces #4314 2025-05-19 17:37:11 -05:00
advplyr 0772730336 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2025-05-16 16:42:05 -05:00
advplyr 8b02fe07c8 Version bump v2.23.0 2025-05-16 16:41:59 -05:00
advplyr 98f93a665c Merge pull request #4288 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-05-16 16:40:31 -05:00
Adolfo Jayme Barrientos 754566b221 Translated using Weblate (Catalan)
Currently translated at 95.5% (1055 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-16 23:20:02 +02:00
Adolfo Jayme Barrientos f4f9adad35 Translated using Weblate (Spanish)
Currently translated at 99.8% (1102 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-16 23:20:02 +02:00
Adolfo Jayme Barrientos 16f7f1166e Translated using Weblate (Catalan)
Currently translated at 95.0% (1049 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-16 23:20:01 +02:00
Usama Khalil f527b0f4d5 Translated using Weblate (Arabic)
Currently translated at 24.2% (268 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-16 23:20:00 +02:00
thehijacker 4f41df53c9 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-05-16 23:20:00 +02:00
cebo29 8a15f775a2 Translated using Weblate (German)
Currently translated at 99.4% (1098 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-05-16 23:19:59 +02:00
peter cerny 5e83bcd283 Translated using Weblate (Slovak)
Currently translated at 99.6% (1100 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-16 23:19:59 +02:00
Максим Горпиніч 2fd5dfcb66 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-05-16 23:19:58 +02:00
Ivan Smoliakov 872ce4fa38 Translated using Weblate (Russian)
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-05-16 23:19:57 +02:00
J. Lavoie ba792d91e5 Translated using Weblate (Italian)
Currently translated at 99.9% (1103 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-05-16 23:19:57 +02:00
biuklija 4997c716db Translated using Weblate (Croatian)
Currently translated at 100.0% (1104 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-05-16 23:19:56 +02:00
J. Lavoie fd72d05280 Translated using Weblate (French)
Currently translated at 99.2% (1096 of 1104 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-05-16 23:19:56 +02:00
advplyr 241b56ad45 Merge pull request #4166 from balki/patch-1
Support listening on unix socket
2025-05-16 16:19:47 -05:00
advplyr 635c384952 Handle undefined Host and make chmod async 2025-05-16 16:14:13 -05:00
advplyr ef930fd1b4 Merge pull request #4299 from advplyr/fix_dockerfile_nunicode
Fix Dockerfile to include nunicode in the final stage
2025-05-16 16:08:13 -05:00
advplyr 49997a1336 Fix Dockerfile to include nunicode in the final stage 2025-05-16 15:23:19 -05:00
advplyr 8d0434143c Merge pull request #4293 from advplyr/search_episodes
Add support for searching podcast episode titles #3301
2025-05-15 17:51:51 -05:00
advplyr 8e0319994e Update total results in global search component 2025-05-15 17:22:20 -05:00
advplyr 0ed6045d1e Add support for searching podcast episode titles #3301 2025-05-15 17:16:15 -05:00
advplyr 25c7e95a64 Version bump v2.22.0 2025-05-14 17:04:01 -05:00
advplyr 1781c4bbcb Merge pull request #4285 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-05-14 17:00:23 -05:00
Michal c4ce72d44e Translated using Weblate (Slovak)
Currently translated at 99.7% (1100 of 1103 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-13 22:01:14 +00:00
Jaakko Rantamäki 78813c4b28 Translated using Weblate (Finnish)
Currently translated at 99.6% (1099 of 1103 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-05-13 22:01:13 +00:00
peter cerny 990baa2dc6 Translated using Weblate (Slovak)
Currently translated at 99.7% (1100 of 1103 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-13 22:01:12 +00:00
Максим Горпиніч c85f4467d2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1103 of 1103 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-05-13 22:01:12 +00:00
burghy86 59f7609054 Translated using Weblate (Italian)
Currently translated at 100.0% (1103 of 1103 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-05-13 22:01:11 +00:00
advplyr 2ef827e3fa Add restart server message on authentication page when oidc is enabled #4064 2025-05-13 17:01:00 -05:00
advplyr 5cadc8d90f Merge pull request #4150 from pinjeff/docker-build
Reduce final docker image size
2025-05-12 15:20:51 -05:00
advplyr 40e7e36ef6 Dockerfile unnecessary stage 1 apks 2025-05-12 15:15:18 -05:00
advplyr d60ad96f8a Update search to exclude returning series with no books #3736 2025-05-11 16:59:11 -05:00
advplyr 46ba342d49 Merge pull request #4237 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-05-10 16:56:33 -05:00
peter cerny ace6b2b81f Translated using Weblate (Slovak)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos fa7e2dfafe Translated using Weblate (Catalan)
Currently translated at 95.3% (1049 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-10 23:54:41 +02:00
Oleg Ivasenko 015310c15d Translated using Weblate (Russian)
Currently translated at 99.9% (1099 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos f624f04dec Translated using Weblate (Spanish)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-10 23:54:41 +02:00
Michal 7c13cfcda2 Translated using Weblate (Slovak)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Azorimor fc265dadae Translated using Weblate (German)
Currently translated at 99.6% (1096 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-05-10 23:54:41 +02:00
peter cerny f9905f887e Translated using Weblate (Slovak)
Currently translated at 96.1% (1058 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Michal eb72bfbbc0 Translated using Weblate (Slovak)
Currently translated at 96.1% (1058 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos c268cace09 Translated using Weblate (Catalan)
Currently translated at 95.2% (1048 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-10 23:54:41 +02:00
Juraj Borza 9666caf7a3 Translated using Weblate (Slovak)
Currently translated at 80.5% (886 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos 9e01e5c24e Translated using Weblate (Catalan)
Currently translated at 94.3% (1038 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos 25e613a867 Translated using Weblate (Spanish)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-10 23:54:41 +02:00
Juraj Borza fe23a86eaa Translated using Weblate (Slovak)
Currently translated at 79.0% (870 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
peter cerny cb5a7d6aef Translated using Weblate (Slovak)
Currently translated at 79.0% (870 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
peter cerny 7deb89ce7a Translated using Weblate (Slovak)
Currently translated at 76.5% (842 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos 1e300c77c9 Translated using Weblate (Catalan)
Currently translated at 94.3% (1038 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-10 23:54:41 +02:00
peter cerny ed7cc42959 Translated using Weblate (Slovak)
Currently translated at 70.3% (774 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos f681ff68a1 Translated using Weblate (Catalan)
Currently translated at 94.3% (1038 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-05-10 23:54:41 +02:00
thehijacker ba112bf9c2 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-05-10 23:54:41 +02:00
Adolfo Jayme Barrientos 718434545a Translated using Weblate (Spanish)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-05-10 23:54:41 +02:00
Максим Горпиніч 0e9a4c95a9 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1100 of 1100 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-05-10 23:54:41 +02:00
ABS translator 3c997c8468 Translated using Weblate (Arabic)
Currently translated at 23.0% (253 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-10 23:54:41 +02:00
burghy86 eb49646256 Translated using Weblate (Italian)
Currently translated at 100.0% (1099 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-05-10 23:54:41 +02:00
ABS translator c54b5eadfd Translated using Weblate (Arabic)
Currently translated at 20.7% (228 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-05-10 23:54:40 +02:00
advplyr 659c671c25 Merge pull request #4226 from Vito0912/feat/chapterLookUp
Changes to Chapter-Lookup
2025-05-10 16:54:33 -05:00
advplyr 0df5a7816d Update chapter toast strings, update spacing, autoformat 2025-05-10 16:42:34 -05:00
advplyr 26c976b6b9 Merge branch 'master' into feat/chapterLookUp 2025-05-10 16:22:30 -05:00
advplyr bdeb22615e Merge pull request #4261 from nschum/fix-ignore-parent
Fix .ignore file causing ignores outside the directory
2025-05-09 17:42:26 -05:00
advplyr 257bf2ebe0 Update edit chapters page breakpoint & cleanup padding 2025-05-09 17:24:02 -05:00
advplyr fc33da447a Remove unused album component 2025-05-09 17:13:17 -05:00
advplyr df45347690 Merge pull request #4274 from advplyr/audiobook_tools_update
Audiobook tools update
2025-05-08 17:31:46 -05:00
advplyr b876256736 Audiobook tools page include edit item button and item title linked to item page 2025-05-08 17:17:44 -05:00
advplyr 3ce6e45761 Support m4b encoder tool for single m4b audiobooks 2025-05-08 17:08:11 -05:00
advplyr 5ac6b85da1 Merge pull request #4270 from advplyr/episode_secondary_sorts
Update episode secondary sort to pubDate and episode #4262
2025-05-07 17:45:53 -05:00
advplyr 69e0a0732a Update episode secondary sort to pubDate and episode #4262 2025-05-07 17:30:07 -05:00
advplyr 087835a9f3 Merge pull request #4266 from advplyr/hls_stream_url_update
Update HLS stream endpoints to not include user token
2025-05-06 17:39:16 -05:00
advplyr 1f7b181b7b Update HLS stream endpoints to not include user token 2025-05-06 17:28:19 -05:00
advplyr 1afb8840db Merge pull request #4263 from advplyr/new_session_track_endpoint
Add new api endpoint for direct playing audio files using session id
2025-05-05 17:25:40 -05:00
advplyr d9531166b6 Fix for HLS transcode urls 2025-05-05 17:07:51 -05:00
advplyr 336de49d8d Add new api endpoint for direct playing audio files using session id #4259 2025-05-05 17:00:43 -05:00
Nikolaj Schumacher 3cc527484d Fix .ignore file causing ignores outside the directory
The file "a/.ignore" should only cause the directory "a" to be ignored.
However, it also ignores all files starting with "a".
After this fix, it will only ignore paths starting with "a/".
2025-05-04 22:43:44 +02:00
advplyr 45987ffd63 Fix library stats returning null instead of 0 #4251 2025-05-03 17:25:01 -05:00
advplyr 1a1ef9c378 Merge pull request #4253 from advplyr/audiobook_tools_enhancements
Audiobook tools enhancements
2025-05-02 17:43:39 -05:00
advplyr 342d100f3e Replace advanced options with presets/advanced card 2025-05-02 17:24:46 -05:00
advplyr e0b90c6813 Add channels, codec and bitrate to tracks table & breakpoint updates 2025-05-02 15:06:31 -05:00
advplyr 2706a9c4aa Merge pull request #4249 from advplyr/watcher_rescans_update
Update watcher to re-scan library items for non-media file only updates
2025-05-01 17:29:25 -05:00
advplyr 2cc9d1b7f8 Update watcher to re-scan library items when non-media files are added/updated #4245 2025-05-01 17:17:40 -05:00
advplyr 2b7268c952 Merge pull request #4240 from josh-vin/feat/defaultYearInReview
Improves Year in Review display logic
2025-04-30 17:26:00 -05:00
advplyr e097fe1e88 Merge pull request #4241 from advplyr/player_track_tooltip
Fix player track tooltip overflowing on share player
2025-04-29 18:01:25 -05:00
advplyr 6819c0b108 Fix player track tooltip overflowing on share player 2025-04-29 17:46:54 -05:00
Josh Vincent 58cd751b43 Improves Year in Review display logic 2025-04-28 21:00:22 -06:00
advplyr 9f834a5345 Merge pull request #4234 from advplyr/fix_exclude_prefixes_crash
Fix server crash when updating excluded prefixes #4221
2025-04-28 16:57:38 -05:00
advplyr 5eaf9c69ad Fix server crash when updating excluded prefixes #4221 2025-04-28 16:40:06 -05:00
Vito0912 a1074e69ac Fixed crash 2025-04-27 19:51:56 +02:00
Vito0912 65aec6a099 Adds locale 2025-04-27 19:44:28 +02:00
Vito0912 38957d4f32 fix shift times not works when editing 2025-04-27 19:34:12 +02:00
Vito0912 a2dc76e190 remove brading 2025-04-27 19:21:37 +02:00
advplyr fd84cd0d7f Version bump v2.21.0 2025-04-27 10:51:31 -05:00
advplyr db7744eb84 Merge pull request #4192 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-04-27 10:48:14 -05:00
SunSpring af513a2fb6 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1099 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-04-27 16:27:46 +02:00
Sergey Ponomarev 4cb5c934d5 Translated using Weblate (Russian)
Currently translated at 100.0% (1099 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-04-27 16:27:46 +02:00
biuklija 37f84a0f62 Translated using Weblate (Croatian)
Currently translated at 100.0% (1099 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-04-27 16:27:45 +02:00
Bezruchenko Simon 70595181f1 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1099 of 1099 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-04-27 16:27:45 +02:00
Fredrik Lindqvist b357bbed60 Translated using Weblate (Swedish)
Currently translated at 95.8% (1050 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-27 16:27:44 +02:00
Robin Stolpe f7a720c6ac Translated using Weblate (Swedish)
Currently translated at 95.8% (1050 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-27 16:27:43 +02:00
dvc05 6549605efd Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.3% (1012 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2025-04-27 16:27:43 +02:00
kuci-JK 33952fb1fd Translated using Weblate (Czech)
Currently translated at 99.4% (1090 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-04-27 16:27:42 +02:00
SunSpring 7b207dc5d8 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-04-27 16:27:42 +02:00
Fredrik Lindqvist cb24a9c1ec Translated using Weblate (Swedish)
Currently translated at 94.9% (1041 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-27 16:27:41 +02:00
Pim 3b42af5213 Translated using Weblate (Dutch)
Currently translated at 98.8% (1083 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-04-27 16:27:40 +02:00
Kabika82 b56691f1a2 Translated using Weblate (Hungarian)
Currently translated at 95.8% (1051 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-04-27 16:27:40 +02:00
cebo29 ac3154093c Translated using Weblate (German)
Currently translated at 99.7% (1093 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-04-27 16:27:39 +02:00
peter cerny 01ef24f5e6 Translated using Weblate (Slovak)
Currently translated at 66.4% (728 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-27 16:27:39 +02:00
thehijacker 3fb73c7426 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-04-27 16:27:38 +02:00
NickSkier bf3bc06322 Translated using Weblate (Russian)
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-04-27 16:27:37 +02:00
petr-prikryl 2733c28784 Translated using Weblate (Czech)
Currently translated at 98.4% (1079 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-04-27 16:27:37 +02:00
Jan-Eric Myhrgren b3dac831e6 Translated using Weblate (Swedish)
Currently translated at 94.8% (1040 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-27 16:27:36 +02:00
ABS translator 35702aa770 Translated using Weblate (Arabic)
Currently translated at 19.4% (213 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ar/
2025-04-27 16:27:36 +02:00
Jakob Zoll b2ffb3b7b9 Translated using Weblate (German)
Currently translated at 99.7% (1093 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-04-27 16:27:35 +02:00
Charlie c52fe4b583 Translated using Weblate (French)
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-04-27 16:27:34 +02:00
Максим Горпиніч af8ace7d1f Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-04-27 16:27:34 +02:00
biuklija de37e40a1e Translated using Weblate (Croatian)
Currently translated at 100.0% (1096 of 1096 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-04-27 16:27:33 +02:00
polarwood 56f5df91dc Translated using Weblate (Turkish)
Currently translated at 27.2% (298 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-04-27 16:27:32 +02:00
polarwood fc590abb09 Translated using Weblate (Turkish)
Currently translated at 24.7% (271 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-04-27 16:27:32 +02:00
Marc Casalprim bc7bbc1b7d Translated using Weblate (Catalan)
Currently translated at 94.4% (1034 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-04-27 16:27:31 +02:00
polarwood 4345973213 Translated using Weblate (Turkish)
Currently translated at 23.4% (257 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-04-27 16:27:30 +02:00
Joao b4ff9f5944 Translated using Weblate (Portuguese (Brazil))
Currently translated at 71.7% (786 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-04-27 16:27:30 +02:00
peter cerny a9a253f769 Translated using Weblate (Slovak)
Currently translated at 64.6% (708 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-27 16:27:29 +02:00
polarwood a9783efa34 Translated using Weblate (Turkish)
Currently translated at 22.4% (246 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-04-27 16:27:28 +02:00
peter cerny a380ee080f Translated using Weblate (Slovak)
Currently translated at 51.6% (566 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-27 16:27:28 +02:00
Ricky Tigg eabefd099c Translated using Weblate (Finnish)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-04-27 16:27:27 +02:00
advplyr 97799919e6 Update tooltip position of sync button on upload page to prevent overlapping 2025-04-27 09:27:19 -05:00
advplyr 35870a0158 Update upload API endpoint to validate request body 2025-04-27 09:18:52 -05:00
advplyr ec05bd36e4 Merge pull request #4223 from Vito0912/feat/fixCrashEmptyCollection
Fix server crash when a user requests the RSS feed of an empty collection
2025-04-26 13:43:38 -05:00
advplyr be041f93c2 Merge pull request #4213 from nichwall/chapter_lookup_error_fix
Chapter lookup error interface update
2025-04-26 12:17:28 -05:00
advplyr a156d3595b Merge pull request #4212 from Nishantsingh11/fix/AIFF-is-supported-but-AIF-isnot
fix(AIFF is supported, but AIF isn't)
2025-04-26 12:05:33 -05:00
Vito0912 a1d549a2b1 prettier 2025-04-26 17:46:19 +02:00
Vito0912 812cb5a160 feat/fixCrashEmptyCollection 2025-04-26 17:35:17 +02:00
Nishantsingh11 e6264540af constants.js in server/utils and client/plugins updated. 2025-04-20 18:34:33 +05:30
Nicholas Wallace 79fe064c4a Add: server localization for chapter lookup 2025-04-19 23:25:17 -07:00
Nicholas Wallace 7e69713683 Change: chapter lookup to be in modal 2025-04-19 23:13:38 -07:00
Nishantsingh11 3bbeb8f27a fix(AIFF is supported, but AIF isn't) 2025-04-20 07:56:44 +05:30
advplyr 04fb8fa61d Merge pull request #3690 from Vito0912/feat/metadataForPlaybackSessions
Improved Metadata Handling and PlaybackSession Metadata Robustness
2025-04-18 17:08:12 -05:00
advplyr 2caa861b8a Update local session mediaMetadata with current item mediaMetadata for undefined values 2025-04-18 17:04:11 -05:00
advplyr d7f0815fb3 Merge branch 'master' into feat/metadataForPlaybackSessions 2025-04-18 16:34:13 -05:00
Vito0912 e6ab05e177 update so also populates data if mediaMetadata is not null 2025-04-18 07:29:34 +02:00
advplyr c2ecfd428b Update docker-build workflow to use ubuntu-24.04 2025-04-17 16:28:06 -05:00
advplyr 9f26274ca8 Add pub date sort in episode feed modal #4073 2025-04-16 16:45:49 -05:00
advplyr 7764f1cf75 Merge pull request #4200 from advplyr/socket_item_events
Fix socket events check user permissions for library items #4199
2025-04-12 17:48:22 -05:00
advplyr bc1b99efd6 Fix socket events check user permissions for library items #4199 2025-04-12 17:39:51 -05:00
advplyr 26309019e7 Merge pull request #4195 from advplyr/fix_podcast_episode_scanner_promise
Fix podcast re-scan promise
2025-04-10 17:54:26 -05:00
advplyr b47d7b734d Update podcast scanner to remove media progress and episodes from playlist 2025-04-10 17:51:42 -05:00
advplyr 62194b8781 Fix podcast re-scan promise 2025-04-10 17:39:41 -05:00
advplyr 7c114a051a Set podcast episode audio file index to 1 for scanned in episodes 2025-04-09 08:33:00 -05:00
advplyr 26c0c89b94 Update podcast latest page to show latest 50 episodes #3343 2025-04-08 17:39:24 -05:00
advplyr c81071a7b3 Fix item page text overlap on details #4187 2025-04-07 17:19:48 -05:00
advplyr 31f48edcc3 Update close feed button on rss feeds table to be consistent with other UI 2025-04-06 17:56:17 -05:00
advplyr f7109a055c Fix rss feeds table overflow with long slugs 2025-04-06 17:50:39 -05:00
advplyr 9d0e7759e0 Merge pull request #4158 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-04-04 17:24:59 -05:00
peter cerny d15ccbd2fc Translated using Weblate (Slovak)
Currently translated at 50.7% (556 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:31 +02:00
Coxe cfeb1743df Translated using Weblate (Danish)
Currently translated at 99.8% (1093 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-04-05 00:17:31 +02:00
peter cerny a7e0330b06 Translated using Weblate (Slovak)
Currently translated at 43.2% (474 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:30 +02:00
biuklija 5f69e83d46 Translated using Weblate (Croatian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-04-05 00:17:30 +02:00
peter cerny fdde62896f Translated using Weblate (Slovak)
Currently translated at 41.9% (459 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:29 +02:00
thehijacker f1909d0fc7 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-04-05 00:17:28 +02:00
Mikkel Dupont Olesen 28225618fd Translated using Weblate (Danish)
Currently translated at 99.2% (1087 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-04-05 00:17:27 +02:00
Marc 1a562a5f23 Translated using Weblate (German)
Currently translated at 99.6% (1091 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-04-05 00:17:27 +02:00
peter cerny bfbbcba160 Translated using Weblate (Slovak)
Currently translated at 29.5% (324 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:26 +02:00
Jan-Eric Myhrgren 21e4d17ef3 Translated using Weblate (Swedish)
Currently translated at 94.8% (1039 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-04-05 00:17:25 +02:00
Augusto Massini Pinto 87faebc7d9 Translated using Weblate (Portuguese (Brazil))
Currently translated at 71.4% (782 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pt_BR/
2025-04-05 00:17:25 +02:00
Adolfo Jayme Barrientos 5d868d1355 Translated using Weblate (Spanish)
Currently translated at 99.4% (1089 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:24 +02:00
Adolfo Jayme Barrientos ac0fd41740 Translated using Weblate (Catalan)
Currently translated at 94.2% (1032 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-04-05 00:17:24 +02:00
Adolfo Jayme Barrientos 1202e95b66 Translated using Weblate (Spanish)
Currently translated at 99.4% (1089 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:23 +02:00
Simple16 c05cf9718b Translated using Weblate (Russian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-04-05 00:17:22 +02:00
Adolfo Jayme Barrientos d8f1e43e85 Translated using Weblate (Spanish)
Currently translated at 99.2% (1087 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-04-05 00:17:22 +02:00
Adolfo Jayme Barrientos ed7e87b168 Translated using Weblate (Catalan)
Currently translated at 94.1% (1031 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-04-05 00:17:21 +02:00
Martin Balko 4ca2c9e97f Translated using Weblate (Slovak)
Currently translated at 28.5% (313 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:20 +02:00
Kabika82 01dd1c0615 Translated using Weblate (Hungarian)
Currently translated at 95.9% (1051 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hu/
2025-04-05 00:17:20 +02:00
Martin Balko cd387b8fed Translated using Weblate (Slovak)
Currently translated at 27.7% (304 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-04-05 00:17:19 +02:00
SunSpring c85cd69152 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-04-05 00:17:18 +02:00
Максим Горпиніч 9e9b52a252 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1095 of 1095 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-04-05 00:17:18 +02:00
advplyr 292d5783a9 Update player queue button on item page to be to the right of the read button to keep consistent spacing 2025-04-04 17:17:06 -05:00
advplyr c7faafd0f3 Fix author order when scanning in multi-author books by setting bookAuthor.createdAt when bulk creating #4177 2025-04-03 17:50:10 -05:00
advplyr ca7388b14e Fix download podcast update library item size #4180 2025-04-02 17:35:57 -05:00
advplyr ddf2ca3670 Update item image in audio player when updated on item #4025 2025-04-01 17:32:21 -05:00
advplyr 96825c3c2b Update feedepisode psc customElement 2025-03-31 17:59:16 -05:00
advplyr 6ed66fea16 Update podcast rss feed parser to use psc chapters on episodes 2025-03-31 17:57:39 -05:00
advplyr ddcda197b4 Fix manage, chapters edit tracks and library stats page not setting the current library properly #4170 2025-03-30 17:27:36 -05:00
advplyr 8bea5d83f5 Merge pull request #4168 from advplyr/new_stats_controller
Create new StatsController and move year in review stats endpoint
2025-03-29 17:44:25 -05:00
advplyr 73c1ea92f3 Add admin middleware for StatsController 2025-03-29 17:37:13 -05:00
advplyr 4fb5330308 Create new StatsController and move year in review stats endpoint 2025-03-29 17:34:17 -05:00
advplyr f853cff920 Fix LazyBookCard test with new globals getter for placeholder url 2025-03-28 17:42:41 -05:00
Balki dc3c978f8d Merge branch 'advplyr:master' into patch-1 2025-03-28 04:55:20 +00:00
advplyr e87825f8df Merge pull request #4151 from mikiher/fix-component-tests
Fix broken component tests
2025-03-27 17:38:22 -05:00
advplyr 718433183b Fix Cover modal showing error image for items with no cover, update placeholder cover url to all come from global store 2025-03-27 17:37:25 -05:00
advplyr 7f3421f78a Merge pull request #4164 from advplyr/count_cache_for_userpermissions
Fix items count for users with item restricting permissions
2025-03-26 17:41:43 -05:00
advplyr 7013600697 Fix library items count for users with item restricting permissions #4163 2025-03-26 17:29:48 -05:00
advplyr d3fe52692b Merge pull request #4162 from Toby222/master
Consistent icons in stats page
2025-03-26 17:19:30 -05:00
Tobias Berger d94e7975dc Consistent icons in stats page 2025-03-26 13:00:10 +01:00
Balki 13fac2d5bc Support http server listening on unix socket 2025-03-25 19:36:19 -04:00
advplyr 792424c4cd Merge pull request #4126 from mikiher/tailwind-4-migration
Tailwind 4 migration
2025-03-25 17:51:01 -05:00
advplyr 366fd1a6da Add base style for button for cursor:pointer, update default btn bg and confirm yes btn color 2025-03-25 17:47:11 -05:00
advplyr 2ce0c7ea47 Merge master 2025-03-25 17:29:09 -05:00
advplyr 973bd22199 Merge pull request #4135 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-03-25 03:56:34 -05:00
Adolfo Jayme Barrientos 252f4cb96a Translated using Weblate (Catalan)
Currently translated at 94.1% (1029 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:02:08 +01:00
Максим Горпиніч 8e824366b0 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-03-25 00:02:07 +01:00
Jan-Eric Myhrgren 52517c3bec Translated using Weblate (Swedish)
Currently translated at 94.5% (1033 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-25 00:02:06 +01:00
biuklija c2862d4941 Translated using Weblate (Croatian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-03-25 00:02:05 +01:00
thehijacker b4621b7e19 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-03-25 00:02:05 +01:00
Adolfo Jayme Barrientos ac3c41dd88 Translated using Weblate (Catalan)
Currently translated at 93.9% (1027 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:02:04 +01:00
Adolfo Jayme Barrientos 9dca5f0bc0 Translated using Weblate (Spanish)
Currently translated at 99.2% (1085 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:02:03 +01:00
Jan-Eric Myhrgren daeb2b3f56 Translated using Weblate (Swedish)
Currently translated at 94.7% (1033 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-25 00:02:03 +01:00
ConfusedAlex 37e079866e Translated using Weblate (German)
Currently translated at 99.9% (1089 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-03-25 00:02:02 +01:00
Jan-Eric Myhrgren ccdc76e88c Translated using Weblate (Swedish)
Currently translated at 94.7% (1033 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-25 00:02:01 +01:00
Adolfo Jayme Barrientos 381f91edd8 Translated using Weblate (Catalan)
Currently translated at 93.7% (1022 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:02:00 +01:00
Adolfo Jayme Barrientos bbb0fbe63f Translated using Weblate (Catalan)
Currently translated at 93.7% (1022 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:59 +01:00
Adolfo Jayme Barrientos 378651265a Translated using Weblate (Catalan)
Currently translated at 93.7% (1022 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:59 +01:00
Adolfo Jayme Barrientos c2cd962607 Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:58 +01:00
Adolfo Jayme Barrientos e5535c7fc4 Translated using Weblate (Catalan)
Currently translated at 93.7% (1022 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:57 +01:00
Simple16 a46394b2fe Translated using Weblate (Russian)
Currently translated at 100.0% (1090 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-03-25 00:01:57 +01:00
Adolfo Jayme Barrientos 5e5a30cd5c Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:56 +01:00
Adolfo Jayme Barrientos a2e84d9c24 Translated using Weblate (Catalan)
Currently translated at 93.5% (1020 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:55 +01:00
Adolfo Jayme Barrientos 26ba8fd12f Translated using Weblate (Catalan)
Currently translated at 93.5% (1020 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:55 +01:00
Adolfo Jayme Barrientos 4ffc3b30f4 Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:54 +01:00
Adolfo Jayme Barrientos 6be37632ff Translated using Weblate (Catalan)
Currently translated at 92.2% (1006 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ca/
2025-03-25 00:01:53 +01:00
Adolfo Jayme Barrientos 2954d6dd47 Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:52 +01:00
Adolfo Jayme Barrientos 45f7ceeef6 Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:51 +01:00
Adolfo Jayme Barrientos ae65c7d650 Translated using Weblate (Spanish)
Currently translated at 99.2% (1082 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2025-03-25 00:01:50 +01:00
Jan-Eric Myhrgren 9edc8b4d21 Translated using Weblate (Swedish)
Currently translated at 94.6% (1032 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-25 00:01:49 +01:00
peter cerny de2efda577 Translated using Weblate (Slovak)
Currently translated at 16.8% (184 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-03-25 00:01:49 +01:00
Jan-Eric Myhrgren 6760fa3498 Translated using Weblate (Swedish)
Currently translated at 94.6% (1032 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-25 00:01:48 +01:00
peter cerny af50fabdbf Translated using Weblate (Slovak)
Currently translated at 16.3% (178 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-03-25 00:01:47 +01:00
advplyr e16d3d72b6 Fix uploader check if item already exists in a subdirectory #4146 2025-03-24 18:01:38 -05:00
mikiher 59e099f950 Add GitHub Actions workflow for running component tests 2025-03-24 07:42:26 +02:00
advplyr ec4f275d52 Remove tooltip for re-scan in item modal, add missing translations #4017 2025-03-23 18:09:59 -05:00
advplyr 006af2d616 Add translations for bulk editor #4017 2025-03-23 18:00:29 -05:00
mikiher b18da959db Fix broken component tests 2025-03-23 20:40:58 +02:00
mikiher 8d0676b780 Revert blur-sm events to blur 2025-03-23 09:14:58 +02:00
mikiher f88e40ea3e Fix double slash color modifer typo 2025-03-23 08:57:13 +02:00
mikiher 37dc537830 Fix double slash in color modifier 2025-03-23 08:34:11 +02:00
mikiher f1af5a1cef Remove debug printing 2025-03-23 08:29:02 +02:00
mikiher 1ea86a0e7a Revert !important additions 2025-03-23 08:07:02 +02:00
Jaffar Ashoor fd0af6b2dd Reduce final docker image size
this adds a third stage to the build, copying the required files only
from the previos stages, this reduces the final image size from 600MB+
down to ~320MB
2025-03-23 02:59:40 +03:00
advplyr 84f99370ee Update audio file scanner to support multiple series in series/series-part tags separated by semicolon if they match #4136 2025-03-21 17:53:17 -05:00
advplyr bef3fa473c Merge master 2025-03-21 09:29:46 -05:00
advplyr 91c1f50a51 Use separate postcss.config and remove from nuxt.config, uninstall autoprefixer 2025-03-21 09:25:29 -05:00
advplyr 89d0257a76 Update using keyboard controls to toggle checkboxes and show outline when buttons are focused #4140 2025-03-20 18:02:33 -05:00
mikiher ca7852171b Merge branch 'advplyr:master' into tailwind-4-migration 2025-03-20 13:06:04 +02:00
mikiher 06919af6b8 Remove tailwind.config.js 2025-03-20 13:01:41 +02:00
mikiher 73c10aac7d Migrate tailwind.config.js to tailwind theme variables 2025-03-20 12:34:08 +02:00
mikiher bd91e466fd Don't use constructed tailwind classes in libraryIcon 2025-03-20 12:33:33 +02:00
mikiher 64b2c34031 Fix buggy loading opacity 2025-03-20 12:32:13 +02:00
advplyr 92bb3527de Add logs when sanitizing filename and update podcast episode download to set targetFilename on init #4121 2025-03-19 17:39:23 -05:00
advplyr 7d0f61663e Merge pull request #4133 from Vito0912/feat/downloadMultiple
Adds the option to download selected books
2025-03-18 17:37:50 -05:00
advplyr 3b7db82bf0 Update bulk download to not open in new tab 2025-03-18 17:33:46 -05:00
advplyr ff36a9327c Fix multiple download for podcasts & cleanup 2025-03-18 17:28:49 -05:00
advplyr 1def32aa50 Fix req.query check and response 2025-03-18 17:03:43 -05:00
advplyr 38f05a857f Version bump v2.20.0 2025-03-17 17:11:01 -05:00
mikiher 40504da4d7 Improve book library page query performance for author sort order (#4080)
* Add migration to create authorNames* columns, in libraryItems including update triggers and indices

* Add authorNames columns and indices to LibraryItem model

* Add database triggers for updating author names in libraryItems (for new databases)

* Populate authorNames during book scanning

* Update book sorting to use new authorNames columns

* Add an index on podcastEpisodes.publishedAt

* Fix group_concat order by and update to sqlite 3.44.2

---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2025-03-17 17:09:49 -05:00
advplyr bba09626a7 Merge pull request #4115 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-03-17 17:07:30 -05:00
Vito0912 3c9966e849 clean up 2025-03-17 20:04:01 +01:00
Vito0912 9b79aab4d5 logging 2025-03-17 19:58:55 +01:00
Vito0912 0a9a846a33 added download to frontend 2025-03-17 19:56:42 +01:00
Vito0912 0123dacb29 download multiple items 2025-03-17 19:35:59 +01:00
thehijacker 6c968bfca4 Translated using Weblate (Slovenian)
Currently translated at 100.0% (1090 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-03-17 14:24:50 +01:00
J. Lavoie 8fa733e144 Translated using Weblate (French)
Currently translated at 99.3% (1083 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-03-17 09:04:44 +01:00
mikiher 6ea2ef0845 Remove autoprefixer plugin 2025-03-17 08:16:32 +02:00
peter cerny e76fbda9e0 Translated using Weblate (Slovak)
Currently translated at 8.3% (91 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-03-17 00:02:33 +01:00
peter cerny 9fedab738f Translated using Weblate (Slovak)
Currently translated at 7.7% (84 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-03-17 00:02:31 +01:00
peter cerny 5d8a88dc08 Translated using Weblate (Slovak)
Currently translated at 7.5% (82 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sk/
2025-03-17 00:02:31 +01:00
SunSpring 23d20f4a5c Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1090 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-03-17 00:02:30 +01:00
biuklija 3dc2022239 Translated using Weblate (Croatian)
Currently translated at 100.0% (1090 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-03-17 00:02:29 +01:00
advplyr b2001eca23 Added translation using Weblate (Slovak) 2025-03-17 00:02:28 +01:00
Jan-Eric Myhrgren 0f7867a12a Translated using Weblate (Swedish)
Currently translated at 94.5% (1031 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-17 00:02:27 +01:00
Максим Горпиніч 43706aac6d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1090 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-03-17 00:02:26 +01:00
Jan-Eric Myhrgren 5c7865f945 Translated using Weblate (Swedish)
Currently translated at 94.5% (1031 of 1090 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-17 00:02:24 +01:00
advplyr 7f8de7915c Update remove playlist translations and use our custom confirm modal 2025-03-16 18:02:16 -05:00
mikiher 30db5d50fb Tailwind V4 migration initial commit 2025-03-16 16:41:37 +02:00
Gabriel Gavrilov 394bf8cb70 Allow number types for payload metadata when updating books. (#4118)
* Allow number types for payload metadata

* cast numbers to string
2025-03-16 08:42:18 -05:00
advplyr 3f6609ab1b Merge pull request #4119 from jfrazx/master
ci: update actions
2025-03-15 17:43:54 -05:00
advplyr e29d3a3672 Cast OpenLibrary publishYear to string #4114 2025-03-15 17:41:07 -05:00
jfrazx ecd782c8a9 fix: docker action 2025-03-15 00:49:27 -07:00
jfrazx cb102deaed Merge pull request #1 from jfrazx/ci/update-actions
ci: update actions
2025-03-14 20:18:59 -07:00
jfrazx 9f883a5019 ci: update actions 2025-03-14 19:43:09 -07:00
advplyr 607f143861 Merge pull request #4113 from advplyr/parsing-opf-v3
Update opf parser to support refines meta elements
2025-03-14 17:39:20 -05:00
advplyr 804dafdfcb Add test for parseOpfMetadata OPF v3 author 2025-03-14 17:32:32 -05:00
advplyr de22177dbf Update opf parser to support refines meta elements 2025-03-13 17:49:05 -05:00
advplyr 2bd46eb67b Fix conflicts 2025-03-13 17:15:30 -05:00
mikiher d60ccff5e8 Add misisng semicolon 2025-03-13 10:14:11 +02:00
mikiher 39d2a25d01 add excplict @config directive 2025-03-13 10:05:50 +02:00
advplyr 9960986e6e Remove old unused i18n strings 2025-03-12 17:22:12 -05:00
ejlaner 759efc0d7d Translated using Weblate (Japanese)
Currently translated at 1.7% (19 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-03-12 23:20:27 +01:00
Xeratone 72f2712a5f Translated using Weblate (Japanese)
Currently translated at 0.2% (3 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-03-12 23:20:26 +01:00
Jan-Eric Myhrgren 1f609e023d Translated using Weblate (Swedish)
Currently translated at 94.5% (1033 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-12 23:20:25 +01:00
Jan-Eric Myhrgren d2f506eefe Translated using Weblate (Swedish)
Currently translated at 94.5% (1033 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-12 23:20:24 +01:00
Ricky Tigg 78031b1a89 Translated using Weblate (Finnish)
Currently translated at 78.0% (853 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-03-12 23:20:23 +01:00
Ricky Tigg c3ce084aac Translated using Weblate (Finnish)
Currently translated at 76.3% (835 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-03-12 23:20:23 +01:00
Jan-Eric Myhrgren 3d6e50a099 Translated using Weblate (Swedish)
Currently translated at 94.5% (1033 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-12 23:20:22 +01:00
Максим Горпиніч 8820fac6a6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-03-12 23:20:21 +01:00
Jan Schoenfeld d6950eab21 Translated using Weblate (German)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-03-12 23:20:20 +01:00
Miró Allard 03f5038882 Translated using Weblate (Swedish)
Currently translated at 94.6% (1034 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-12 23:20:19 +01:00
advplyr 2685d12ca3 Replace enable watcher setting strings, update enable automatic backups #4095 2025-03-12 17:20:11 -05:00
advplyr e504bb09eb Merge pull request #4106 from Roukanken42/fix/loading-epub-covers
Fix: Load epub covers via cover-image property
2025-03-12 17:06:00 -05:00
advplyr 90d1aab1de Merge pull request #4097 from Vito0912/master
fix updating progress not updating finishedAt
2025-03-12 17:01:10 -05:00
advplyr a3cd9e4440 Update confirm mark as finished to use translation #4017 2025-03-11 17:52:42 -05:00
Roukanken b86797a245 Fix: Load epub covers via cover-image property 2025-03-11 21:05:21 +01:00
Vito0912 953f21ed53 fix updating progress not updating finishedAt 2025-03-10 13:58:52 +01:00
advplyr ef77a88fce Merge pull request #4093 from gitting/master
Fix spelling
2025-03-09 17:09:54 -05:00
IUser e7ca6a4ea9 Fix spelling 2025-03-09 14:01:53 -07:00
advplyr e74b6982f9 Merge pull request #4046 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-03-08 18:04:15 -06:00
Troj@ 438364dafb Translated using Weblate (Belarusian)
Currently translated at 56.2% (615 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:04:04 +00:00
Troj@ c8f79dca6c Translated using Weblate (Belarusian)
Currently translated at 53.0% (580 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:04:04 +00:00
Phantomwise 9a2eb24d4b Translated using Weblate (French)
Currently translated at 99.7% (1090 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-03-09 00:04:03 +00:00
Andreas Morell-Reng e5f7f0812e Translated using Weblate (Danish)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-03-09 00:04:02 +00:00
Simple16 4705e73714 Translated using Weblate (Russian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-03-09 00:04:01 +00:00
Troj@ 738d936243 Translated using Weblate (Belarusian)
Currently translated at 51.6% (564 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:59 +00:00
Troj@ 507338d906 Translated using Weblate (Belarusian)
Currently translated at 50.9% (557 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:59 +00:00
Troj@ 776819ad52 Translated using Weblate (Belarusian)
Currently translated at 48.3% (529 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:58 +00:00
Jan-Eric Myhrgren f8af265440 Translated using Weblate (Swedish)
Currently translated at 94.6% (1034 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:57 +00:00
Marcus skoding d426ed101e Translated using Weblate (Swedish)
Currently translated at 94.2% (1030 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:56 +00:00
Fredrik Lindqvist e4eead75b1 Translated using Weblate (Swedish)
Currently translated at 94.2% (1030 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:56 +00:00
Jan-Eric Myhrgren 4dd2f7cf18 Translated using Weblate (Swedish)
Currently translated at 94.2% (1030 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:55 +00:00
Troj@ 9c9c60a5bd Translated using Weblate (Belarusian)
Currently translated at 47.5% (520 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:54 +00:00
Prashant Mhatre 4d88deabd2 Translated using Weblate (Hindi)
Currently translated at 8.3% (91 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hi/
2025-03-09 00:03:54 +00:00
Troj@ 3a539a4dd3 Translated using Weblate (Belarusian)
Currently translated at 43.3% (474 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:53 +00:00
phewi a57addccae Translated using Weblate (Finnish)
Currently translated at 55.3% (605 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-03-09 00:03:52 +00:00
Troj@ a73372c51d Translated using Weblate (Belarusian)
Currently translated at 40.4% (442 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:51 +00:00
thehijacker dea457adcd Translated using Weblate (Slovenian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-03-09 00:03:50 +00:00
phewi 20e007ecd4 Translated using Weblate (Finnish)
Currently translated at 51.6% (565 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-03-09 00:03:49 +00:00
Ricky Tigg 4ee6b6d49b Translated using Weblate (Finnish)
Currently translated at 51.6% (565 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2025-03-09 00:03:48 +00:00
Jan-Eric Myhrgren e5cb43bd75 Translated using Weblate (Swedish)
Currently translated at 92.6% (1013 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:47 +00:00
Troj@ fb877779d1 Translated using Weblate (Belarusian)
Currently translated at 35.4% (388 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-03-09 00:03:46 +00:00
Krissse10 abcc2eb22b Translated using Weblate (Swedish)
Currently translated at 92.6% (1013 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-03-09 00:03:46 +00:00
Ranforingus a21b6e1dec Translated using Weblate (Dutch)
Currently translated at 99.5% (1088 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-03-09 00:03:44 +00:00
advplyr ddd8f15f2b Merge pull request #4088 from nichwall/checkRemoveAuthors_fix
Fix empty series delete check
2025-03-08 18:03:36 -06:00
advplyr 8f308c6180 Close RSS feeds after removing empty series 2025-03-08 17:47:47 -06:00
advplyr e93b18745e Merge pull request #4089 from nichwall/logger_cleanup
Simplify log level determination
2025-03-08 17:25:42 -06:00
Nicholas Wallace 10acf28fa6 Simplify log level determination 2025-03-08 12:46:36 -07:00
Nicholas Wallace 84e20e041c Fix: empty series delete flakiness 2025-03-08 11:16:34 -07:00
Nicholas Wallace 167617cce0 Add: transaction to empty author remove 2025-03-08 10:43:27 -07:00
advplyr d3fd19da65 Fixes for screen readers on podcast page and episodes table 2025-03-07 17:23:18 -06:00
advplyr 31be775c32 Merge pull request #4082 from mikiher/fix-lazy-episode-row-rtl
Fix RTL issue in LazyEpisodeRow
2025-03-07 16:56:18 -06:00
mikiher 81cd6f6c7d Fix RTL issue in LazyEpisodeRow 2025-03-07 21:14:50 +02:00
advplyr 4fdb37c9dc Merge pull request #4078 from advplyr/validate_migration_files
Update migration manager to validate migration files #4042
2025-03-06 17:35:12 -06:00
advplyr c29935e57b Update migration manager to validate migration files #4042 2025-03-06 17:24:33 -06:00
advplyr d41b48c89a Merge pull request #4075 from Vito0912/feat/fixCrashCustomProvider
Fixes search not returning results if description field is not provided by a custom provider
2025-03-04 17:53:58 -06:00
advplyr b17e6010fd Add validation for custom metadata provider responses 2025-03-04 17:50:40 -06:00
Vito0912 a296ac6132 fix crash 2025-03-04 18:06:58 +01:00
advplyr 5746e848b0 Fix:Trim whitespace from custom metadata provider name & url #4069 2025-03-02 17:13:27 -06:00
advplyr c6b5d4aa26 Update author by string translation #4017 2025-03-01 17:48:11 -06:00
advplyr 43a507faa8 Merge pull request #4030 from 4ch1m/add_filename_sorting_for_podcasts-view
new sort option for podcasts view (-> sort by filename)
2025-02-28 17:45:43 -06:00
advplyr 828d5d2afc Update episode row to show filename when sorting by filename 2025-02-28 17:42:56 -06:00
advplyr 6075f2686f Merge pull request #3546 from justcallmelarry/master
API PATCH /me/progress/:id - allow providing createdAt and respect provided finishedAt when syncing progress
2025-02-28 17:25:46 -06:00
advplyr ae3517bcde Merge pull request #4055 from nichwall/2_15_0_migration_fix
Fix: flaky 2.15.0 migration test
2025-02-27 18:28:21 -06:00
Nicholas Wallace 0a00ebcde1 Fix: flaky 2.15.0 migration test 2025-02-26 21:40:56 -07:00
advplyr 68ef0f83e1 Update select all in feed modal to check downloading 2025-02-26 18:00:36 -06:00
advplyr e4a34b0145 Merge pull request #4041 from nichwall/podcast_queue_no_duplicates
Prevent duplicate episodes from being added to queue
2025-02-26 17:58:27 -06:00
advplyr 0ca65d1f79 Show download icon for queued/downloaded episodes in rss feed modal 2025-02-26 17:56:17 -06:00
advplyr bd3d396f37 Merge pull request #4035 from nichwall/podcast_episode_play_order
Play first podcast episode in table
2025-02-25 17:31:48 -06:00
advplyr fd1c8ee513 Update episode list to come from component ref, populate queue from table order when playing episode 2025-02-25 17:25:56 -06:00
advplyr b0045b5b8b Update browser confirm prompts to use confirm prompt modal instead 2025-02-24 17:44:17 -06:00
Nicholas Wallace 6674189acd Add: prevent duplicates from being added to queue 2025-02-23 19:23:26 -07:00
advplyr c7d8021a16 Version bump v2.19.5 2025-02-23 17:20:30 -06:00
advplyr 9e83ad25b9 Merge pull request #4015 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-02-23 17:18:50 -06:00
Troja 2eccb9465c Translated using Weblate (Belarusian)
Currently translated at 31.0% (339 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-24 00:01:03 +01:00
Максим Горпиніч 599b6bd6ad Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-24 00:01:03 +01:00
Jan-Eric Myhrgren e01ac489fb Translated using Weblate (Swedish)
Currently translated at 92.6% (1013 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-24 00:01:03 +01:00
biuklija 271dbc4764 Translated using Weblate (Croatian)
Currently translated at 100.0% (1093 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-24 00:01:03 +01:00
Vito0912 84c2931434 Translated using Weblate (German)
Currently translated at 99.9% (1092 of 1093 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-02-24 00:01:03 +01:00
burghy86 38483c9269 Translated using Weblate (Italian)
Currently translated at 100.0% (1092 of 1092 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-02-24 00:01:03 +01:00
mickeynos b2e97d70df Translated using Weblate (Czech)
Currently translated at 99.5% (1087 of 1092 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/cs/
2025-02-24 00:01:03 +01:00
Troja 78aafe038d Translated using Weblate (Belarusian)
Currently translated at 28.6% (313 of 1092 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-24 00:01:03 +01:00
Максим Горпиніч 34f7ddfdd7 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1092 of 1092 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-24 00:01:03 +01:00
Jan-Eric Myhrgren 0e9777feec Translated using Weblate (Swedish)
Currently translated at 92.0% (1005 of 1092 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-24 00:01:03 +01:00
Troja 6351fd8d7b Translated using Weblate (Belarusian)
Currently translated at 25.6% (280 of 1091 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-24 00:01:03 +01:00
Максим Горпиніч b7591abd06 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1091 of 1091 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-24 00:01:03 +01:00
Michał Rączka-Dudek 2b36caf096 Translated using Weblate (Polish)
Currently translated at 74.5% (812 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2025-02-24 00:01:03 +01:00
Charlie f87a0bfc2f Translated using Weblate (French)
Currently translated at 99.6% (1085 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-02-24 00:01:03 +01:00
Nicholas W b109b2edee Added translation using Weblate (Romanian) 2025-02-24 00:01:03 +01:00
Milo Ivir 7795bf25d0 Translated using Weblate (Croatian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-24 00:01:03 +01:00
advplyr 3d5c02ae7c Merge pull request #4037 from mikiher/route-to-library-if-last-issue-removed
Route from Issues to Library page after last issue was removed
2025-02-23 17:00:57 -06:00
advplyr 373d14a49e Merge pull request #4034 from nichwall/custom-metadata-provider-logging
Add: log custom metadata provider to match other providers
2025-02-23 16:58:07 -06:00
advplyr a17127f078 Merge pull request #4031 from nichwall/temp_file_ignore_refactor
Refactor ignore file logic
2025-02-23 16:56:09 -06:00
advplyr 20f812403f Add fileUtils recurseFiles and shouldIgnoreFile tests 2025-02-23 16:53:11 -06:00
advplyr a864c6bcc6 Merge pull request #4020 from mikiher/invalidate-count-cache-on-entity-update
Invalidate count cache on entity update
2025-02-23 15:21:36 -06:00
mikiher 6c0e42db49 Route from Issues to Library if last issue is removed 2025-02-23 18:06:36 +02:00
mikiher 364ccd85fe Use count cache only when no filter is set 2025-02-23 08:53:57 +02:00
mikiher d6b58c2f10 Revert "Invalidate count cache on entity update"
This reverts commit e8b60defb6.
2025-02-23 08:03:10 +02:00
Nicholas Wallace 72169990ac Fix: double reverse of array 2025-02-22 22:06:51 -07:00
Nicholas Wallace 5f105dc6cc Change: Play button for podcast picks first episode in table 2025-02-22 21:50:37 -07:00
Nicholas Wallace 706b2d7d72 Add: store for filtered podcast episodes 2025-02-22 21:50:09 -07:00
advplyr 64185b7519 Add backup schedule string translation #4017 2025-02-22 17:53:05 -06:00
advplyr e1b3b657c4 Merge pull request #4027 from Alexshch09/Add-admin-auth-to-LibraryController
fix(auth): Add admin-level auth to LibraryController 'delete', 'update' and 'delete items with issues'
2025-02-22 17:45:38 -06:00
Nicholas Wallace 4662fc5244 Add: log custom metadata provider to match other providers 2025-02-22 14:48:13 -07:00
Nicholas Wallace 13c20e0cdd Add: generic function to ignor files 2025-02-22 12:28:51 -07:00
Achim 007691ffe5 add "sort by filename" 2025-02-22 17:08:29 +01:00
advplyr 19a65dba98 Update backup schedule description translations #4017 2025-02-21 18:18:54 -06:00
Mike Smith 799879d67d prevent long author strings from pushing the player controls down by truncating (#3944)
* prevent long author strings from pushing the player controls down by truncating

* move truncate to single author, instead of the main container
2025-02-21 17:45:29 -06:00
alexshch09 452d354b52 fix(auth): Add admin-level auth to LibraryController delete update and issue removal 2025-02-22 00:44:52 +01:00
advplyr 9d7f44f73a Fix RSS Feed Open query 2025-02-21 17:39:36 -06:00
mikiher e8b60defb6 Invalidate count cache on entity update 2025-02-21 09:45:10 +02:00
advplyr 0cc2e39367 Update en-us string order 2025-02-20 17:59:09 -06:00
advplyr a34b01fcb4 Add localization strings for Cover Provider and Activities #4017 2025-02-20 17:45:33 -06:00
advplyr 7919a8b581 Fix get podcast library items endpoint when not including a limit query param #4014 2025-02-20 17:40:54 -06:00
advplyr 565eb423ee Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2025-02-19 17:44:21 -06:00
advplyr 42b0e31b4a Version bump v2.19.4 2025-02-19 17:44:14 -06:00
advplyr 97a8959bf8 Merge pull request #3974 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-02-19 17:16:19 -06:00
advplyr b5b99cbaca Merge pull request #4008 from mikiher/resort-after-title-change
Re-sort title-sorted bookshelf after title change
2025-02-19 17:15:45 -06:00
advplyr f04ef320aa Restore scroll position on title change re-sort 2025-02-19 17:12:19 -06:00
polarwood 4e33059ac8 Translated using Weblate (Turkish)
Currently translated at 18.8% (205 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-02-19 23:59:53 +01:00
Jan-Eric Myhrgren 699644322b Translated using Weblate (Swedish)
Currently translated at 91.9% (1001 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-19 23:59:52 +01:00
biuklija 49ba364b2a Translated using Weblate (Croatian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-19 23:59:52 +01:00
Armanc Keser adb3967f89 Translated using Weblate (Turkish)
Currently translated at 14.2% (155 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-02-19 23:59:51 +01:00
polarwood cfdcac9475 Translated using Weblate (Turkish)
Currently translated at 13.0% (142 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/tr/
2025-02-19 23:59:51 +01:00
Jan-Eric Myhrgren b1d57bc0b3 Translated using Weblate (Swedish)
Currently translated at 90.6% (987 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-19 23:59:50 +01:00
A L f7cea8ca12 Translated using Weblate (Bulgarian)
Currently translated at 77.2% (841 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-02-19 23:59:50 +01:00
Ivan Penchev 293440006b Translated using Weblate (Bulgarian)
Currently translated at 77.2% (841 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-02-19 23:59:49 +01:00
Ivan Penchev 45f7f54b6c Translated using Weblate (Bulgarian)
Currently translated at 70.8% (772 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/bg/
2025-02-19 23:59:49 +01:00
advplyr bb5e16157c Merge pull request #4005 from mikiher/fix-triggers-for-new-databases
Add title triggers in new databases
2025-02-19 16:59:41 -06:00
mikiher 2e8cb46c57 Resort title-sorted bookshelf after title change 2025-02-19 21:04:07 +02:00
mikiher f9c0e52f18 Add title triggers in new databases 2025-02-19 17:39:32 +02:00
advplyr 6290cfaeb1 Auto format 2025-02-18 17:19:06 -06:00
advplyr fd3d4f5fcf Merge pull request #3978 from sloped/fix/detect-http-https-upgrades
fix: allow upgrading HTTP to HTTPS for redirects
2025-02-18 17:18:36 -06:00
advplyr 9f9bee2ddc Merge pull request #3996 from mikiher/optimize-podcast-queries
Improve podcast library page query performance on title, titleIgnorePrefix, and addedAt sort orders
2025-02-18 17:04:45 -06:00
mikiher 568bf0254d Change migration version to v2.19.4 2025-02-18 07:57:46 +02:00
advplyr 79f4db5ff3 Version bump v2.19.3 2025-02-16 17:01:45 -06:00
mikiher 7038f5730f Set title[IgnorePrefix] when a podcast libraryItem is created 2025-02-16 14:57:05 +02:00
mikiher 0a8186cbda Add ANALYZE to database init sequence 2025-02-16 13:38:54 +02:00
mikiher 659164003f Clear LibraryItemsPodcastFilters count cache after podcast[Episode] is created or destroryed 2025-02-16 13:27:47 +02:00
mikiher de5d8650e8 Add profiling to podcast library filterdata queries 2025-02-16 12:47:23 +02:00
mikiher bacefb5f6f Format PodcastScanner (Pretteier-only changes) 2025-02-16 12:41:47 +02:00
mikiher 0169bf5518 Update podcast.numEpisodes when episodes are created or destroyed 2025-02-16 12:38:44 +02:00
mikiher 8f192b1b17 Add profiling to podcasts and podcast episodes page queries 2025-02-16 09:46:32 +02:00
mikiher 21343b5aa0 Add count cache to libraryItemsPodcastQueries 2025-02-16 09:40:29 +02:00
mikiher a5508cdc4c Remove unnecessary 'distinct: true' from podcast episodes page query 2025-02-16 09:32:00 +02:00
mikiher bd4f48ec39 Add required: true to includes in podcast episodes page query 2025-02-16 09:29:57 +02:00
mikiher cb9fc3e0d1 Replace numEpisodesIncomplete subquery with cached user progress calculation 2025-02-16 09:22:06 +02:00
mikiher 707533df8f Remove numEpisodes subquery from podcasst page query 2025-02-16 09:15:54 +02:00
mikiher 2e48ec0dde Use libraryItem.title[IgnorePrefix] for sorting podcasts page query 2025-02-16 09:08:27 +02:00
mikiher f1e46a351b Separate feed query from podcasts page query 2025-02-16 09:05:54 +02:00
mikiher da8fd2d9d5 Set podcastId when mediaProgress is created 2025-02-16 08:57:10 +02:00
mikiher f1de307bf9 Update cached user whenever mediaProgress is removed 2025-02-16 08:52:33 +02:00
mikiher 7282afcfde Add podcastId to mediaProgress model 2025-02-16 08:42:09 +02:00
mikiher e2f1aeed75 Add numEpisodes to podcast model 2025-02-16 08:38:03 +02:00
mikiher 23a750214f Add migration in preparation for podcast query optimization 2025-02-16 08:35:51 +02:00
advplyr 6a7418ad41 Fix:Edit book cover tab local images overflowing #3986 2025-02-15 17:55:56 -06:00
advplyr 8b00c16062 Merge pull request #3993 from mikiher/fix-stringify-sequelize-query
fix stringifySequelizeQuery and add tests
2025-02-15 17:24:19 -06:00
mikiher 8ee5646d79 fix stringifySequelizeQuery and add tests 2025-02-15 23:57:27 +02:00
advplyr 373551fb74 Merge pull request #3985 from advplyr/fix-quick-match-all-crash
Fix server crash when quick match all updates series sequence #3961
2025-02-14 17:22:29 -06:00
advplyr d9b206fe1c Fix server crash when quick match all updates existing series sequence #3961 2025-02-14 16:56:37 -06:00
advplyr fe4e0145c9 Merge pull request #3984 from advplyr/fix-chapter-end-sleep-timer
Fix chapter end sleep timer sometimes not stopping #3969
2025-02-14 16:39:26 -06:00
advplyr c4d99a118f Fix chapter end sleep timer sometimes not stopping #3969 2025-02-14 16:24:39 -06:00
advplyr b96226966b Merge pull request #3980 from advplyr/stringify_sequelize_query
Fix count cache by stringify Symbols #3979
2025-02-13 18:24:36 -06:00
advplyr 5ca12eee19 Fix count cache by stringify Symbols #3979 2025-02-13 18:07:59 -06:00
Conner McCall f460297daf fix: allow upgrading HTTP to HTTPS for redirects
Re: #3142 and #3658

When adding certain podcasts, the server encountered a redirect from an HTTP URL to an HTTPS domain, causing an error that was difficult for end users to diagnose without inspecting logs or HTML.

This issue arose due to SSRF security measures that blocked such redirects. Instead of failing in these cases, we now detect when the error is caused by an HTTP-to-HTTPS upgrade. If confirmed, we upgrade the initial URL to HTTPS and resend the request.

Since this change does not allow cross-protocol or cross-domain redirections, it remains secure while resolving most of the reported issues.

Affected podcasts that are now fixed:

- D&D is for Nerds
- The New Yorker: The Writer's Voice - New Fiction from The New Yorker
- Radiolab
2025-02-13 09:19:02 -06:00
advplyr ebdf377fc1 Version bump v2.19.2 2025-02-12 10:01:05 -06:00
advplyr 808d23561c Merge pull request #3972 from advplyr/remove-col-ambiguity
Fix server crash remove column name ambiguity #3966
2025-02-12 09:59:54 -06:00
advplyr a34813b3ab Fix server crash remove column name ambiguity #3966 2025-02-12 08:52:20 -06:00
advplyr 725192fbc0 Version bump v2.19.1 2025-02-11 17:17:07 -06:00
advplyr 2915c072b5 Merge pull request #3931 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-02-11 16:52:14 -06:00
Troja 03a1d7da32 Translated using Weblate (Belarusian)
Currently translated at 19.4% (212 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-11 22:51:07 +00:00
Mario 1be1ce6f87 Translated using Weblate (German)
Currently translated at 99.9% (1088 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-02-11 22:51:07 +00:00
Troja 21b27c432c Translated using Weblate (Belarusian)
Currently translated at 16.0% (175 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-11 22:51:06 +00:00
Troja cbe5e3db8a Translated using Weblate (Belarusian)
Currently translated at 13.0% (142 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/be/
2025-02-11 22:51:05 +00:00
burghy86 08b4d4d7a2 Translated using Weblate (Italian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2025-02-11 22:51:04 +00:00
Jan-Eric Myhrgren ac8324e595 Translated using Weblate (Swedish)
Currently translated at 90.1% (982 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-11 22:51:03 +00:00
Pepijn a14c6a3a8b Translated using Weblate (Dutch)
Currently translated at 99.8% (1087 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2025-02-11 22:51:03 +00:00
Jan-Eric Myhrgren 74b35ea9d1 Translated using Weblate (Swedish)
Currently translated at 88.7% (966 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-11 22:51:02 +00:00
Jan-Eric Myhrgren 78d8c83e6d Translated using Weblate (Swedish)
Currently translated at 85.9% (936 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-11 22:51:01 +00:00
Jan-Eric Myhrgren bf795d3662 Translated using Weblate (Swedish)
Currently translated at 85.9% (936 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-11 22:51:00 +00:00
Jan-Eric Myhrgren 1fbd090441 Translated using Weblate (Swedish)
Currently translated at 85.8% (935 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-11 22:50:59 +00:00
biuklija 70621e72e8 Translated using Weblate (Croatian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-11 22:50:59 +00:00
advplyr d30a09f503 Merge pull request #3963 from mikiher/security-fix-GHSA-pg8v-5jcv-wrvw
Security fix for GHSA-pg8v-5jcv-wrvw
2025-02-11 16:50:52 -06:00
advplyr 39567c6c22 Update view feed modal to sort episodes by pub date ascending 2025-02-11 16:47:34 -06:00
advplyr ed3af5bdcd Fix server crash when feed cover image is requested but doesnt exist 2025-02-11 16:14:49 -06:00
advplyr 9e54b4f7ca Merge pull request #3952 from mikiher/query-performance
Improve book library page query performance on title, titleIgnorePrefix, and addedAt sort orders.
2025-02-11 15:41:59 -06:00
mikiher ec65376569 Security fix for GHSA-pg8v-5jcv-wrvw 2025-02-11 22:02:51 +02:00
advplyr 4e8cd6fba0 Update index.js dev fallback router base path 2025-02-10 17:58:18 -06:00
advplyr 1a3d70d041 Merge pull request #3958 from devnoname120/fix-apex-path-support
Fix `ROUTER_BASE_PATH` override for empty string
2025-02-10 10:16:47 -06:00
Paul 14e92435ec Fix ROUTER_BASE_PATH override for empty string
When the `ROUTER_BASE_PATH` env variable is set to an empty string it's mistakenly overriden to `/audiobookshelf` instead.
The `/audiobookshelf` fallback should only be used when the `ROUTER_BASE_PATH` env variable is undefined, not just an empty string.

Regression introduced in https://github.com/advplyr/audiobookshelf/pull/3810
See also: https://github.com/advplyr/audiobookshelf/pull/3810#discussion_r1948790937

Partially address https://github.com/advplyr/audiobookshelf/issues/3874
2025-02-10 12:08:49 +01:00
advplyr 0ccb88904a fix v2.15.0 migration test 2025-02-09 17:40:29 -06:00
mikiher 4cc300d6e9 Update changelog with v2.19.1 migration 2025-02-09 21:39:43 +02:00
advplyr 068ba84a8c Merge pull request #3954 from advplyr/fix_next_prev_edit_description
Fix next/prev buttons on edit modals not changing description when focused
2025-02-08 13:17:50 -06:00
advplyr 36ef675556 Fix edit episode next/prev buttons showing when editing from home page 2025-02-08 13:05:50 -06:00
advplyr 0dd57a8912 Fix using next/prev in edit modals while rich text input is focused #3951 2025-02-08 13:02:27 -06:00
advplyr ef45f844e5 Update upwards migration to be idempotent 2025-02-08 12:37:34 -06:00
advplyr 9a261195b7 Update server/models/Book.js 2025-02-08 10:19:13 -06:00
mikiher 3d08a35aa0 Add index on (libraryId, mediaType, createdAt) 2025-02-08 14:53:01 +02:00
mikiher a13143245b Improve page load queries on title, titleIgnorePrefix, and addedAt sort order 2025-02-08 12:29:23 +02:00
mikiher 52bb28669a Add a profile utility function 2025-02-08 10:41:56 +02:00
advplyr 25ae6dd59a Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2025-02-07 17:10:12 -06:00
advplyr a37fe3c3d2 Fix: Users with update permission unable to remove books from collection #3947 2025-02-07 17:09:48 -06:00
advplyr 59bcbe0dfa Merge pull request #3946 from advplyr/details_trim_whitespace
Trim whitespace from podcast/book/episode & batch edit text inputs
2025-02-06 17:51:49 -06:00
advplyr b5e69630de Update batch edit text inputs to trim whitespace 2025-02-06 17:29:27 -06:00
advplyr 0bba709124 Trim whitespace from book/podcast/episode details text inputs #3943 2025-02-06 17:27:33 -06:00
advplyr e93bb5cb07 Merge pull request #3941 from Vynce/accept-encoding
Add `Accept-Encoding` header to `getPodcastFeed()`
2025-02-06 17:01:31 -06:00
Michael Vincent 3f7af8acfb Add Accept-Encoding header to getPodcastFeed()
This commit adds the Accept-Encoding header to getPodcastFeed() with
gzip, compress, and deflate support. This allows servers to send a
compressed response that'll be decompressed by axios transparently.

Audiobookshelf is currently using axios v0.27.2, which enables the
decompress option by default. The decompress feature supports gzip,
compress, and deflate algorithms (see axios/lib/adapters/http.js).
axios v0.27.2 does not add the Accept-Encoding header to requests
automatically, so that's the responsibility of the caller.
2025-02-05 23:12:58 -06:00
advplyr 5e5a604d03 Fix name parser to not use "last, first" format when not using comma separators. Adds unit tests #3940 2025-02-05 17:25:31 -06:00
advplyr 201e12ecc3 Update downloadFile to debug log percentage complete 2025-02-05 16:15:00 -06:00
advplyr 24d6e390f0 Fix Book/Podcast updateFromRequest to support null values in string fields #3938 2025-02-05 15:31:57 -06:00
advplyr 0cf7a6abec Merge pull request #3929 from mikiher/fix-trix-resize
Add resize to trix editor
2025-02-04 17:22:30 -06:00
mikiher 76ac0d001b Add resize to trix editor 2025-02-04 09:54:28 +02:00
advplyr 00343a953b Update Collection/Playlist and batch quick match modal bg colors to be consistent with other modals 2025-02-03 17:47:10 -06:00
advplyr 82ab95ab02 Version bump v2.19.0 2025-02-02 15:39:46 -06:00
advplyr a1d8ebc01b Merge pull request #3893 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2025-02-02 15:36:43 -06:00
advplyr eeaae5f934 Added translation using Weblate (Turkish) 2025-02-02 22:06:22 +01:00
thehijacker 4464276a6e Translated using Weblate (Slovenian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-02-02 00:07:53 +01:00
biuklija 3465790fe9 Translated using Weblate (Croatian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-02 00:07:53 +01:00
Jonathan 5fa4c5a2c3 Translated using Weblate (German)
Currently translated at 99.3% (1082 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2025-02-02 00:07:52 +01:00
SunSpring 13f353596b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-02-02 00:07:51 +01:00
Simple16 3d9100e5b8 Translated using Weblate (Russian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-02-02 00:07:50 +01:00
Максим Горпиніч b62309ead2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-02 00:07:50 +01:00
Andreas Morell-Reng 1fce94ad4a Translated using Weblate (Danish)
Currently translated at 100.0% (1089 of 1089 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-02-02 00:07:49 +01:00
thehijacker 9abd6698ae Translated using Weblate (Slovenian)
Currently translated at 100.0% (1087 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-02-02 00:07:48 +01:00
Jan-Eric Myhrgren 88c10ad619 Translated using Weblate (Swedish)
Currently translated at 85.4% (929 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-02 00:07:48 +01:00
biuklija c62a6fbffd Translated using Weblate (Croatian)
Currently translated at 100.0% (1087 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-02 00:07:47 +01:00
Michel Neuba 989388d3ed Translated using Weblate (French)
Currently translated at 99.7% (1084 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2025-02-02 00:07:46 +01:00
Will Forde 4cc97a22f6 Translated using Weblate (Japanese)
Currently translated at 0.1% (1 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ja/
2025-02-02 00:07:45 +01:00
Максим Горпиніч 8bd336a4ba Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1087 of 1087 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-02 00:07:45 +01:00
thehijacker 437c8dd09c Translated using Weblate (Slovenian)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sl/
2025-02-02 00:07:44 +01:00
Максим Горпиніч f82697cbbf Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2025-02-02 00:07:43 +01:00
Andreas Morell-Reng 74c87a0bbd Translated using Weblate (Danish)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/da/
2025-02-02 00:07:43 +01:00
biuklija 35eb5bcfc0 Translated using Weblate (Croatian)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/hr/
2025-02-02 00:07:42 +01:00
Simple16 0a29b549df Translated using Weblate (Russian)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-02-02 00:07:41 +01:00
SunSpring a38a92b948 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2025-02-02 00:07:40 +01:00
Jan-Eric Myhrgren d245c93da4 Translated using Weblate (Swedish)
Currently translated at 85.1% (925 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/sv/
2025-02-02 00:07:40 +01:00
Илья Червонный bcf8f6b732 Translated using Weblate (Russian)
Currently translated at 100.0% (1086 of 1086 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/ru/
2025-02-02 00:07:39 +01:00
advplyr 40e11db5e5 Merge pull request #3921 from advplyr/fix_content_url_basepath
Fix API including basepath in tracks contentUrl
2025-02-01 17:07:29 -06:00
Vito0912 121805ba39 Merge branch 'master' into feat/metadataForPlaybackSessions 2025-01-07 17:01:01 +01:00
Vito0912 f9bbd71174 added type to be saved. Should support podcasts
It should support everything important from the podcast metadata:
https://api.audiobookshelf.org/#podcast-metadata

And the book metadata:
https://api.audiobookshelf.org/#book-metadata
2024-12-17 15:27:37 +01:00
Vito0912 2fbb31e0ea added null saftey and added displayTitle and displayAuthor 2024-12-07 10:37:00 +01:00
Vito0912 89167543fa added author for podcasts 2024-12-07 10:25:52 +01:00
Vito0912 33e0987d73 Added mediaMetadata to playbackSessions 2024-12-07 10:09:14 +01:00
Lauri Vuorela 2fdab39e27 Merge branch 'advplyr:master' into master 2024-10-29 22:08:01 +01:00
Lauri Vuorela 9b01d11b27 allow setting createdAt and respect set finishedAt when syncing progress 2024-10-22 23:58:09 +02:00
402 changed files with 28229 additions and 6614 deletions
+3 -3
View File
@@ -14,11 +14,11 @@ jobs:
steps:
- name: Check issue headings
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
const issueBody = context.payload.issue.body || "";
// Match Markdown headings (e.g., # Heading, ## Heading)
const headingRegex = /^(#{1,6})\s.+/gm;
const headings = [...issueBody.matchAll(headingRegex)];
@@ -39,4 +39,4 @@ jobs:
issue_number: context.payload.issue.number,
state: "closed"
});
}
}
+4 -4
View File
@@ -43,11 +43,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -60,7 +60,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -73,6 +73,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: '/language:${{matrix.language}}'
+48
View File
@@ -0,0 +1,48 @@
name: Run Component Tests
on:
workflow_dispatch:
inputs:
ref:
description: 'Branch/Tag/SHA to test'
required: true
pull_request:
paths:
- 'client/**'
- '.github/workflows/component-tests.yml'
push:
paths:
- 'client/**'
- '.github/workflows/component-tests.yml'
jobs:
run-component-tests:
name: Run Component Tests
runs-on: ubuntu-latest
steps:
- name: Checkout (push/pull request)
uses: actions/checkout@v4
if: github.event_name != 'workflow_dispatch'
- name: Checkout (workflow_dispatch)
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
if: github.event_name == 'workflow_dispatch'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: |
cd client
npm ci
- name: Run tests
run: |
cd client
npm test
+11 -12
View File
@@ -1,5 +1,4 @@
---
name: Build and Push Docker Image
on:
@@ -11,7 +10,7 @@ on:
required: true
default: 'latest'
push:
branches: [main,master]
branches: [main, master]
tags:
- 'v*.*.*'
# Only build when files in these directories have been changed
@@ -23,16 +22,16 @@ on:
jobs:
build:
if: "!contains(github.event.head_commit.message, 'skip ci')"
runs-on: ubuntu-20.04
if: ${{ !contains(github.event.head_commit.message, 'skip ci') && github.repository == 'advplyr/audiobookshelf' }}
runs-on: ubuntu-24.04
steps:
- name: Check out
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: advplyr/audiobookshelf,ghcr.io/${{ github.repository_owner }}/audiobookshelf
tags: |
@@ -40,13 +39,13 @@ jobs:
type=semver,pattern={{version}}
- name: Setup QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
@@ -54,20 +53,20 @@ jobs:
${{ runner.os }}-buildx-
- name: Login to Dockerhub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Login to ghcr
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_PASSWORD }}
- name: Build image
uses: docker/build-push-action@v3
uses: docker/build-push-action@v6
with:
tags: ${{ github.event.inputs.tags || steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2 -1
View File
@@ -20,7 +20,8 @@ jobs:
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: 20
cache: 'npm'
# The only argument is the `directory`, which is where the i18n files are
# stored.
+5 -4
View File
@@ -18,14 +18,15 @@ jobs:
name: build and test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: setup nade
uses: actions/setup-node@v3
- name: setup node
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: install pkg (using yao-pkg fork for targetting node20)
- name: install pkg (using yao-pkg fork for targeting node20)
run: npm install -g @yao-pkg/pkg
- name: get client dependencies
+7
View File
@@ -18,15 +18,22 @@ jobs:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
# Set up node to run the javascript
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
# Install Redocly CLI
- name: Install Redocly CLI
run: npm install -g @redocly/cli@latest
# Perform linting for exploded spec
- name: Run linting for exploded spec
run: redocly lint docs/root.yaml --format=github-actions
# Perform linting for bundled spec
- name: Run linting for bundled spec
run: redocly lint docs/openapi.json --format=github-actions
+1
View File
@@ -29,6 +29,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
+1
View File
@@ -23,3 +23,4 @@ sw.*
.DS_STORE
.idea/*
tailwind.compiled.css
tailwind.config.js
+34 -16
View File
@@ -1,34 +1,32 @@
ARG NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
ARG NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
### STAGE 0: Build client ###
FROM node:20-alpine AS build
FROM node:20-alpine AS build-client
WORKDIR /client
COPY /client /client
RUN npm ci && npm cache clean --force
RUN npm run generate
### STAGE 1: Build server ###
FROM node:20-alpine
FROM node:20-alpine AS build-server
ARG NUSQLITE3_DIR
ARG TARGETPLATFORM
ENV NODE_ENV=production
RUN apk update && \
apk add --no-cache --update \
RUN apk add --no-cache --update \
curl \
tzdata \
ffmpeg \
make \
python3 \
g++ \
tini \
unzip
COPY --from=build /client/dist /client/dist
COPY index.js package* /
COPY server server
ARG TARGETPLATFORM
ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3"
ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so"
WORKDIR /server
COPY index.js package* /server
COPY /server /server/server
RUN case "$TARGETPLATFORM" in \
"linux/amd64") \
@@ -42,14 +40,34 @@ RUN case "$TARGETPLATFORM" in \
RUN npm ci --only=production
RUN apk del make python3 g++
### STAGE 2: Create minimal runtime image ###
FROM node:20-alpine
ARG NUSQLITE3_DIR
ARG NUSQLITE3_PATH
# Install only runtime dependencies
RUN apk add --no-cache --update \
tzdata \
ffmpeg \
tini
WORKDIR /app
# Copy compiled frontend and server from build stages
COPY --from=build-client /client/dist /app/client/dist
COPY --from=build-server /server /app
COPY --from=build-server ${NUSQLITE3_PATH} ${NUSQLITE3_PATH}
EXPOSE 80
ENV PORT=80
ENV NODE_ENV=production
ENV CONFIG_PATH="/config"
ENV METADATA_PATH="/metadata"
ENV SOURCE="docker"
ENV NUSQLITE3_DIR=${NUSQLITE3_DIR}
ENV NUSQLITE3_PATH=${NUSQLITE3_PATH}
ENTRYPOINT ["tini", "--"]
CMD ["node", "index.js"]
+2 -2
View File
@@ -22,7 +22,7 @@ add_user() {
declare -r descr="${4:-No description}"
declare -r shell="${5:-/bin/false}"
if ! getent passwd | grep -q "^$user:"; then
if ! getent passwd "$user" 2>&1 >/dev/null; then
echo "Creating system user: $user in $group with $descr and shell $shell"
useradd $uid_flags --gid $group --no-create-home --system --shell $shell -c "$descr" $user
fi
@@ -39,7 +39,7 @@ add_group() {
declare -r gid_flags="--gid $gid"
fi
if ! getent group | grep -q "^$group:" ; then
if ! getent group "$group" 2>&1 >/dev/null; then
echo "Creating system group: $group"
groupadd $gid_flags --system $group
fi
+85 -3
View File
@@ -1,3 +1,85 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
[role='button'],
button {
cursor: pointer;
}
}
@theme {
--spacing-0\.5e: 0.125em;
--spacing-1e: 0.25em;
--spacing-1\.5e: 0.375em;
--spacing-2e: 0.5em;
--spacing-2\.5e: 0.625em;
--spacing-3e: 0.75em;
--spacing-3\.5e: 0.875em;
--spacing-4e: 1em;
--spacing-5e: 1.25em;
--spacing-6e: 1.5em;
--spacing-7e: 1.75em;
--spacing-8e: 2em;
--spacing-9e: 2.25em;
--spacing-10e: 2.5em;
--spacing-11e: 2.75em;
--spacing-12e: 3em;
--spacing-14e: 3.5em;
--spacing-16e: 4em;
--spacing-20e: 5em;
--spacing-24e: 6em;
--spacing-28e: 7em;
--spacing-32e: 8em;
--spacing-36e: 9em;
--spacing-40e: 10em;
--spacing-44e: 11em;
--spacing-48e: 12em;
--spacing-52e: 13em;
--spacing-56e: 14em;
--spacing-60e: 15em;
--spacing-64e: 16em;
--spacing-72e: 18em;
--spacing-80e: 20em;
--spacing-96e: 24em;
--color-bg: #373838;
--color-primary: #232323;
--color-accent: #1ad691;
--color-error: #ff5252;
--color-info: #2196f3;
--color-success: #4caf50;
--color-warning: #fb8c00;
--color-darkgreen: rgb(34, 127, 35);
--color-black-50: #bbbbbb;
--color-black-100: #666666;
--color-black-200: #555555;
--color-black-300: #444444;
--color-black-400: #333333;
--color-black-500: #222222;
--color-black-600: #111111;
--color-black-700: #101010;
--font-sans: 'Source Sans Pro';
--font-mono: 'Ubuntu Mono';
--text-xxs: 0.625rem;
--text-1\.5xl: 1.375rem;
--text-2\.5xl: 1.6875rem;
--text-4\.5xl: 2.625rem;
}
+24 -7
View File
@@ -13,10 +13,10 @@
<ui-libraries-dropdown class="mr-2" />
<controls-global-search v-if="currentLibrary" class="mr-1 sm:mr-0" />
<div class="flex-grow" />
<div class="grow" />
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
<span class="material-symbols text-2xl text-warning text-opacity-50"> cast </span>
<span class="material-symbols text-2xl text-warning/50"> cast </span>
</ui-tooltip>
<div v-if="isChromecastInitialized" class="w-6 min-w-6 h-6 ml-2 mr-1 sm:mx-2 cursor-pointer">
<google-cast-launcher></google-cast-launcher>
@@ -42,7 +42,7 @@
</ui-tooltip>
</nuxt-link>
<nuxt-link to="/account" class="relative w-9 h-9 md:w-32 bg-fg border border-gray-500 rounded shadow-sm ml-1.5 sm:ml-3 md:ml-5 md:pl-3 md:pr-10 py-2 text-left sm:text-sm cursor-pointer hover:bg-bg hover:bg-opacity-40" aria-haspopup="listbox" aria-expanded="true">
<nuxt-link to="/account" class="relative w-9 h-9 md:w-32 bg-fg border border-gray-500 rounded-sm shadow-xs ml-1.5 sm:ml-3 md:ml-5 md:pl-3 md:pr-10 py-2 text-left sm:text-sm cursor-pointer hover:bg-bg/40" aria-haspopup="listbox" aria-expanded="true">
<span class="items-center hidden md:flex">
<span class="block truncate">{{ username }}</span>
</span>
@@ -53,8 +53,8 @@
</div>
<div v-show="numMediaItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center">
<h1 class="text-lg md:text-2xl px-4">{{ $getString('MessageItemsSelected', [numMediaItemsSelected]) }}</h1>
<div class="flex-grow" />
<ui-btn v-if="!isPodcastLibrary && selectedMediaItemsArePlayable" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems">
<div class="grow" />
<ui-btn v-if="!isPodcastLibrary && selectedMediaItemsArePlayable" color="bg-success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems">
<span class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
{{ $strings.ButtonPlay }}
</ui-btn>
@@ -66,11 +66,11 @@
</ui-tooltip>
<template v-if="userCanUpdate">
<ui-tooltip :text="$strings.LabelEdit" direction="bottom">
<ui-icon-btn :disabled="processingBatch" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" />
<ui-icon-btn :disabled="processingBatch" icon="edit" bg-color="bg-warning" class="mx-1.5" @click="batchEditClick" />
</ui-tooltip>
</template>
<ui-tooltip v-if="userCanDelete" :text="$strings.ButtonRemove" direction="bottom">
<ui-icon-btn :disabled="processingBatch" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" />
<ui-icon-btn :disabled="processingBatch" icon="delete" bg-color="bg-error" class="mx-1.5" @click="batchDeleteClick" />
</ui-tooltip>
<ui-context-menu-dropdown v-if="contextMenuItems.length && !processingBatch" :items="contextMenuItems" class="ml-1" @action="contextMenuAction" />
@@ -180,6 +180,15 @@ export default {
action: 'rescan'
})
// The limit of 50 is introduced because of the URL length. Each id has 36 chars, so 36 * 40 = 1440
// + 40 , separators = 1480 chars + base path 280 chars = 1760 chars. This keeps the URL under 2000 chars even with longer domains
if (this.selectedMediaItems.length <= 40) {
options.push({
text: this.$strings.LabelDownload,
action: 'download'
})
}
return options
}
},
@@ -187,6 +196,7 @@ export default {
requestBatchQuickEmbed() {
const payload = {
message: this.$strings.MessageConfirmQuickEmbed,
allowHtml: true,
callback: (confirmed) => {
if (confirmed) {
this.$axios
@@ -215,6 +225,8 @@ export default {
this.batchAutoMatchClick()
} else if (action === 'rescan') {
this.batchRescan()
} else if (action === 'download') {
this.batchDownload()
}
},
async batchRescan() {
@@ -241,6 +253,11 @@ export default {
}
this.$store.commit('globals/setConfirmPrompt', payload)
},
async batchDownload() {
const libraryItemIds = this.selectedMediaItems.map((i) => i.id)
console.log('Downloading library items', libraryItemIds)
this.$downloadFile(`/api/libraries/${this.$store.state.libraries.currentLibraryId}/download?token=${this.$store.getters['user/getToken']}&ids=${libraryItemIds.join(',')}`)
},
async playSelectedItems() {
this.$store.commit('setProcessingBatch', true)
+14 -2
View File
@@ -6,8 +6,8 @@
<div v-if="loaded && !shelves.length && !search" class="w-full flex flex-col items-center justify-center py-12">
<p class="text-center text-2xl mb-4 py-4">{{ $getString('MessageXLibraryIsEmpty', [libraryName]) }}</p>
<div v-if="userIsAdminOrUp" class="flex">
<ui-btn to="/config" color="primary" class="w-52 mr-2">{{ $strings.ButtonConfigureScanner }}</ui-btn>
<ui-btn color="success" class="w-52" :loading="isScanningLibrary || tempIsScanning" @click="scan">{{ $strings.ButtonScanLibrary }}</ui-btn>
<ui-btn to="/config" color="bg-primary" class="w-52 mr-2">{{ $strings.ButtonConfigureScanner }}</ui-btn>
<ui-btn color="bg-success" class="w-52" :loading="isScanningLibrary || tempIsScanning" @click="scan">{{ $strings.ButtonScanLibrary }}</ui-btn>
</div>
</div>
<div v-else-if="loaded && !shelves.length && search" class="w-full h-40 flex items-center justify-center">
@@ -217,6 +217,16 @@ export default {
})
}
if (this.results.episodes?.length) {
shelves.push({
id: 'episodes',
label: 'Episodes',
labelStringKey: 'LabelEpisodes',
type: 'episode',
entities: this.results.episodes.map((res) => res.libraryItem)
})
}
if (this.results.series?.length) {
shelves.push({
id: 'series',
@@ -290,6 +300,8 @@ export default {
})
},
userUpdated(user) {
if (user.id !== this.$store.state.user.user.id) return
if (user.seriesHideFromContinueListening && user.seriesHideFromContinueListening.length) {
this.removeAllSeriesFromContinueSeries(user.seriesHideFromContinueListening)
}
+4 -3
View File
@@ -36,7 +36,7 @@
</div>
<div class="relative">
<div class="relative text-center categoryPlacard transform z-30 top-0 left-4e md:left-8e w-44e rounded-md">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-sm border" :style="{ padding: `0em 0.5em` }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-xs border" :style="{ padding: `0em 0.5em` }">
<h2 :style="{ fontSize: 0.9 + 'em' }">{{ $strings[shelf.labelStringKey] }}</h2>
</div>
</div>
@@ -93,12 +93,13 @@ export default {
editAuthor(author) {
this.$store.commit('globals/showEditAuthorModal', author)
},
editItem(libraryItem) {
editItem(libraryItem, tab = 'details') {
var itemIds = this.shelf.entities.map((e) => e.id)
this.$store.commit('setBookshelfBookIds', itemIds)
this.$store.commit('showEditModal', libraryItem)
this.$store.commit('showEditModalOnTab', { libraryItem, tab: tab || 'details' })
},
editEpisode({ libraryItem, episode }) {
this.$store.commit('setEpisodeTableEpisodeIds', [episode.id])
this.$store.commit('setSelectedLibraryItem', libraryItem)
this.$store.commit('globals/setSelectedEpisode', episode)
this.$store.commit('globals/setShowEditPodcastEpisodeModal', true)
+23 -39
View File
@@ -1,48 +1,37 @@
<template>
<div class="w-full h-20 md:h-10 relative">
<div class="flex md:hidden h-10 items-center">
<nuxt-link :to="`/library/${currentLibraryId}`" class="flex-grow h-full flex justify-center items-center" :class="isHomePage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link :to="`/library/${currentLibraryId}`" class="grow h-full flex justify-center items-center" :class="isHomePage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isHomePage || isPodcastLibrary" class="text-sm">{{ $strings.ButtonHome }}</p>
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span v-else class="material-symbols text-lg">home</span>
</nuxt-link>
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf`" class="flex-grow h-full flex justify-center items-center" :class="isLibraryPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf`" class="grow h-full flex justify-center items-center" :class="isLibraryPage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isLibraryPage || isPodcastLibrary" class="text-sm">{{ $strings.ButtonLibrary }}</p>
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
<span v-else class="material-symbols text-lg">import_contacts</span>
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary" :to="`/library/${currentLibraryId}/podcast/latest`" class="flex-grow h-full flex justify-center items-center" :class="isPodcastLatestPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isPodcastLibrary" :to="`/library/${currentLibraryId}/podcast/latest`" class="grow h-full flex justify-center items-center" :class="isPodcastLatestPage ? 'bg-primary/80' : 'bg-primary/40'">
<p class="text-sm">{{ $strings.ButtonLatest }}</p>
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/series`" class="flex-grow h-full flex justify-center items-center" :class="isSeriesPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/series`" class="grow h-full flex justify-center items-center" :class="isSeriesPage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isSeriesPage" class="text-sm">{{ $strings.ButtonSeries }}</p>
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
</svg>
<span v-else class="material-symbols text-lg">view_column</span>
</nuxt-link>
<nuxt-link v-if="showPlaylists" :to="`/library/${currentLibraryId}/bookshelf/playlists`" class="flex-grow h-full flex justify-center items-center" :class="isPlaylistsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="showPlaylists" :to="`/library/${currentLibraryId}/bookshelf/playlists`" class="grow h-full flex justify-center items-center" :class="isPlaylistsPage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isPlaylistsPage || isPodcastLibrary" class="text-sm">{{ $strings.ButtonPlaylists }}</p>
<span v-else class="material-symbols text-lg">&#xe03d;</span>
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="flex-grow h-full flex justify-center items-center" :class="isCollectionsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="grow h-full flex justify-center items-center" :class="isCollectionsPage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
<span v-else class="material-symbols text-lg">&#xe431;</span>
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary/80' : 'bg-primary/40'">
<p v-if="isAuthorsPage" class="text-sm">{{ $strings.ButtonAuthors }}</p>
<svg v-else class="w-5 h-5" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z"
/>
</svg>
<span v-else class="material-symbols text-lg">groups</span>
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/search`" class="flex-grow h-full flex justify-center items-center" :class="isPodcastSearchPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/search`" class="grow h-full flex justify-center items-center" :class="isPodcastSearchPage ? 'bg-primary/80' : 'bg-primary/40'">
<p class="text-sm">{{ $strings.ButtonAdd }}</p>
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" class="flex-grow h-full flex justify-center items-center" :class="isPodcastDownloadQueuePage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" class="grow h-full flex justify-center items-center" :class="isPodcastDownloadQueuePage ? 'bg-primary/80' : 'bg-primary/40'">
<p class="text-sm">{{ $strings.ButtonDownloadQueue }}</p>
</nuxt-link>
</div>
@@ -52,14 +41,14 @@
<p class="pl-2 text-base md:text-lg">
{{ seriesName }}
</p>
<div class="w-6 h-6 rounded-full bg-black bg-opacity-30 flex items-center justify-center ml-3">
<div class="w-6 h-6 rounded-full bg-black/30 flex items-center justify-center ml-3">
<span class="font-mono">{{ $formatNumber(numShowing) }}</span>
</div>
<div class="flex-grow" />
<div class="grow" />
<!-- RSS feed -->
<ui-tooltip v-if="seriesRssFeed" :text="$strings.LabelOpenRSSFeed" direction="top">
<ui-icon-btn icon="rss_feed" class="mx-0.5" :size="7" icon-font-size="1.2rem" bg-color="success" outlined @click="showOpenSeriesRSSFeed" />
<ui-icon-btn icon="rss_feed" class="mx-0.5" :size="7" icon-font-size="1.2rem" bg-color="bg-success" outlined @click="showOpenSeriesRSSFeed" />
</ui-tooltip>
<ui-context-menu-dropdown v-if="!isBatchSelecting && seriesContextMenuItems.length" :items="seriesContextMenuItems" class="mx-px" @action="seriesContextMenuAction" />
@@ -68,7 +57,7 @@
<template v-else-if="page !== 'search' && page !== 'podcast-search' && page !== 'recent-episodes' && !isHome && !isAuthorsPage">
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
<div class="flex-grow hidden sm:inline-block" />
<div class="grow hidden sm:inline-block" />
<!-- library filter select -->
<controls-library-filter-select v-if="isLibraryPage && !isBatchSelecting" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" />
@@ -83,30 +72,30 @@
<controls-sort-select v-if="isSeriesPage && !isBatchSelecting" v-model="settings.seriesSortBy" :descending.sync="settings.seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" />
<!-- issues page remove all button -->
<ui-btn v-if="isIssuesFilter && userCanDelete && !isBatchSelecting" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">{{ $strings.ButtonRemoveAll }} {{ $formatNumber(numShowing) }} {{ entityName }}</ui-btn>
<ui-btn v-if="isIssuesFilter && userCanDelete && !isBatchSelecting" :loading="processingIssues" color="bg-error" small class="ml-4" @click="removeAllIssues">{{ $strings.ButtonRemoveAll }} {{ $formatNumber(numShowing) }} {{ entityName }}</ui-btn>
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
<!-- search page -->
<template v-else-if="page === 'search'">
<div class="flex-grow" />
<div class="grow" />
<p>{{ $strings.MessageSearchResultsFor }} "{{ searchQuery }}"</p>
<div class="flex-grow" />
<div class="grow" />
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
<!-- authors page -->
<template v-else-if="isAuthorsPage">
<p class="hidden md:block">{{ $formatNumber(numShowing) }} {{ entityName }}</p>
<div class="flex-grow hidden sm:inline-block" />
<ui-btn v-if="userCanUpdate && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<div class="grow hidden sm:inline-block" />
<ui-btn v-if="userCanUpdate && !isBatchSelecting" :loading="processingAuthors" color="bg-primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<!-- author sort select -->
<controls-sort-select v-model="settings.authorSortBy" :descending.sync="settings.authorSortDesc" :items="authorSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateAuthorSort" />
</template>
<!-- home page -->
<template v-else-if="isHome">
<div class="flex-grow" />
<div class="grow" />
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
</div>
@@ -274,15 +263,10 @@ export default {
isAuthorsPage() {
return this.page === 'authors'
},
isAlbumsPage() {
return this.page === 'albums'
},
numShowing() {
return this.totalEntities
},
entityName() {
if (this.isAlbumsPage) return 'Albums'
if (this.isPodcastLibrary) return this.$strings.LabelPodcasts
if (!this.page) return this.$strings.LabelBooks
if (this.isSeriesPage) return this.$strings.LabelSeries
+8 -3
View File
@@ -1,11 +1,11 @@
<template>
<div role="toolbar" aria-orientation="vertical" aria-label="Config Sidebar">
<div role="navigation" aria-label="Config Navigation" class="w-44 fixed left-0 top-16 bg-bg bg-opacity-100 md:bg-opacity-70 shadow-lg border-r border-white border-opacity-5 py-3 transform transition-transform mb-12 overflow-y-auto" :class="wrapperClass + ' ' + (streamLibraryItem ? 'h-[calc(100%-270px)]' : 'h-[calc(100%-110px)]')" v-click-outside="clickOutside">
<div role="navigation" aria-label="Config Navigation" class="w-44 fixed left-0 top-16 bg-bg/100 md:bg-bg/70 shadow-lg border-r border-white/5 py-3 transform transition-transform mb-12 overflow-y-auto" :class="wrapperClass + ' ' + (streamLibraryItem ? 'h-[calc(100%-270px)]' : 'h-[calc(100%-110px)]')" v-click-outside="clickOutside">
<div v-show="isMobilePortrait" class="flex items-center justify-end pb-2 px-4 mb-1" @click="closeDrawer">
<span class="material-symbols text-2xl">arrow_back</span>
</div>
<nuxt-link v-for="route in configRoutes" :key="route.id" :to="route.path" class="w-full px-3 h-12 border-b border-primary border-opacity-30 flex items-center cursor-pointer relative" :class="routeName === route.id ? 'bg-primary bg-opacity-70' : 'hover:bg-primary hover:bg-opacity-30'">
<nuxt-link v-for="route in configRoutes" :key="route.id" :to="route.path" class="w-full px-3 h-12 border-b border-primary/30 flex items-center cursor-pointer relative" :class="routeName === route.id ? 'bg-primary/70' : 'hover:bg-primary/30'">
<p class="leading-4">{{ route.title }}</p>
<div v-show="routeName === route.iod" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
@@ -13,7 +13,7 @@
<modals-changelog-view-modal v-model="showChangelogModal" :versionData="versionData" />
</div>
<div class="w-44 h-12 px-4 border-t bg-bg border-black border-opacity-20 fixed left-0 flex flex-col justify-center" :class="wrapperClass" :style="{ bottom: streamLibraryItem ? '160px' : '0px' }">
<div class="w-44 h-12 px-4 border-t bg-bg border-black/20 fixed left-0 flex flex-col justify-center" :class="wrapperClass" :style="{ bottom: streamLibraryItem ? '160px' : '0px' }">
<div class="flex items-center justify-between">
<button type="button" class="underline font-mono text-sm" @click="clickChangelog">v{{ $config.version }}</button>
@@ -70,6 +70,11 @@ export default {
title: this.$strings.HeaderUsers,
path: '/config/users'
},
{
id: 'config-api-keys',
title: this.$strings.HeaderApiKeys,
path: '/config/api-keys'
},
{
id: 'config-sessions',
title: this.$strings.HeaderListeningSessions,
+37 -16
View File
@@ -4,7 +4,7 @@
<div :key="shelf" :id="`shelf-${shelf - 1}`" class="w-full px-4e sm:px-8e relative" :class="{ bookshelfRow: !isAlternativeBookshelfView }" :style="{ height: shelfHeight + 'px' }">
<!-- Card skeletons -->
<template v-for="entityIndex in entitiesInShelf(shelf)">
<div :key="entityIndex" class="w-full h-full absolute rounded z-5 top-0 left-0 bg-primary box-shadow-book" :style="{ transform: entityTransform(entityIndex), width: cardWidth + 'px', height: coverHeight + 'px' }" />
<div :key="entityIndex" class="w-full h-full absolute rounded-sm z-5 top-0 left-0 bg-primary box-shadow-book" :style="{ transform: entityTransform(entityIndex), width: cardWidth + 'px', height: coverHeight + 'px' }" />
</template>
<div v-if="!isAlternativeBookshelfView" class="bookshelfDivider w-full absolute bottom-0 left-0 right-0 z-20 h-6e" />
</div>
@@ -13,8 +13,8 @@
<div v-if="initialized && !totalShelves && !hasFilter && entityName === 'items'" class="w-full flex flex-col items-center justify-center py-12">
<p class="text-center text-2xl mb-4 py-4">{{ $getString('MessageXLibraryIsEmpty', [libraryName]) }}</p>
<div v-if="userIsAdminOrUp" class="flex">
<ui-btn to="/config" color="primary" class="w-52 mr-2">{{ $strings.ButtonConfigureScanner }}</ui-btn>
<ui-btn color="success" class="w-52" :loading="isScanningLibrary || tempIsScanning" @click="scan">{{ $strings.ButtonScanLibrary }}</ui-btn>
<ui-btn to="/config" color="bg-primary" class="w-52 mr-2">{{ $strings.ButtonConfigureScanner }}</ui-btn>
<ui-btn color="bg-success" class="w-52" :loading="isScanningLibrary || tempIsScanning" @click="scan">{{ $strings.ButtonScanLibrary }}</ui-btn>
</div>
</div>
<div v-else-if="!totalShelves && initialized" class="w-full py-16">
@@ -29,7 +29,7 @@
</div>
<!-- Clear filter only available on Library bookshelf -->
<div v-if="entityName === 'items'" class="flex justify-center mt-2">
<ui-btn v-if="hasFilter" color="primary" @click="clearFilter">{{ $strings.ButtonClearFilter }}</ui-btn>
<ui-btn v-if="hasFilter" color="bg-primary" @click="clearFilter">{{ $strings.ButtonClearFilter }}</ui-btn>
</div>
</div>
@@ -232,11 +232,11 @@ export default {
clearFilter() {
this.$store.dispatch('user/updateUserSettings', { filterBy: 'all' })
},
editEntity(entity) {
editEntity(entity, tab = 'details') {
if (this.entityName === 'items' || this.entityName === 'series-books') {
const bookIds = this.entities.map((e) => e.id)
this.$store.commit('setBookshelfBookIds', bookIds)
this.$store.commit('showEditModal', entity)
this.$store.commit('showEditModalOnTab', { libraryItem: entity, tab: tab || 'details' })
} else if (this.entityName === 'collections') {
this.$store.commit('globals/setEditCollection', entity)
} else if (this.entityName === 'playlists') {
@@ -419,7 +419,7 @@ export default {
this.postScrollTimeout = setTimeout(this.postScroll, 500)
},
async resetEntities() {
async resetEntities(scrollPositionToRestore) {
if (this.isFetchingEntities) {
this.pendingReset = true
return
@@ -437,6 +437,12 @@ export default {
await this.loadPage(0)
var lastBookIndex = Math.min(this.totalEntities, this.shelvesPerPage * this.entitiesPerShelf)
this.mountEntities(0, lastBookIndex)
if (scrollPositionToRestore) {
if (window.bookshelf) {
window.bookshelf.scrollTop = scrollPositionToRestore
}
}
},
async rebuild() {
this.initSizeData()
@@ -444,9 +450,8 @@ export default {
var lastBookIndex = Math.min(this.totalEntities, this.booksPerFetch)
this.destroyEntityComponents()
await this.loadPage(0)
var bookshelfEl = document.getElementById('bookshelf')
if (bookshelfEl) {
bookshelfEl.scrollTop = 0
if (window.bookshelf) {
window.bookshelf.scrollTop = 0
}
this.mountEntities(0, lastBookIndex)
},
@@ -547,6 +552,15 @@ export default {
if (this.entityName === 'items' || this.entityName === 'series-books') {
var indexOf = this.entities.findIndex((ent) => ent && ent.id === libraryItem.id)
if (indexOf >= 0) {
if (this.entityName === 'items' && this.orderBy === 'media.metadata.title') {
const curTitle = this.entities[indexOf].media.metadata?.title
const newTitle = libraryItem.media.metadata?.title
if (curTitle != newTitle) {
console.log('Title changed. Re-sorting...')
this.resetEntities(this.currScrollTop)
return
}
}
this.entities[indexOf] = libraryItem
if (this.entityComponentRefs[indexOf]) {
this.entityComponentRefs[indexOf].setEntity(libraryItem)
@@ -554,6 +568,18 @@ export default {
}
}
},
routeToBookshelfIfLastIssueRemoved() {
if (this.totalEntities === 0) {
const currentRouteQuery = this.$route.query
if (currentRouteQuery?.filter && currentRouteQuery.filter === 'issues') {
this.$nextTick(() => {
console.log('Last issue removed. Redirecting to library bookshelf')
this.$router.push(`/library/${this.currentLibraryId}/bookshelf`)
this.$store.dispatch('libraries/fetch', this.currentLibraryId)
})
}
}
},
libraryItemRemoved(libraryItem) {
if (this.entityName === 'items' || this.entityName === 'series-books') {
var indexOf = this.entities.findIndex((ent) => ent && ent.id === libraryItem.id)
@@ -564,6 +590,7 @@ export default {
this.executeRebuild()
}
}
this.routeToBookshelfIfLastIssueRemoved()
},
libraryItemsAdded(libraryItems) {
console.log('items added', libraryItems)
@@ -751,10 +778,6 @@ export default {
windowResize() {
this.executeRebuild()
},
socketInit() {
// Server settings are set on socket init
this.executeRebuild()
},
initListeners() {
window.addEventListener('resize', this.windowResize)
@@ -767,7 +790,6 @@ export default {
})
this.$eventBus.$on('bookshelf_clear_selection', this.clearSelectedEntities)
this.$eventBus.$on('socket_init', this.socketInit)
this.$eventBus.$on('user-settings', this.settingsUpdated)
if (this.$root.socket) {
@@ -799,7 +821,6 @@ export default {
}
this.$eventBus.$off('bookshelf_clear_selection', this.clearSelectedEntities)
this.$eventBus.$off('socket_init', this.socketInit)
this.$eventBus.$off('user-settings', this.settingsUpdated)
if (this.$root.socket) {
+15 -10
View File
@@ -13,7 +13,7 @@
</div>
<div class="text-gray-400 flex items-center w-1/2 sm:w-4/5 lg:w-2/5">
<span class="material-symbols text-sm">person</span>
<div v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ podcastAuthor }}</div>
<div v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base truncate">{{ podcastAuthor }}</div>
<div v-else-if="authors.length" class="pl-1 sm:pl-1.5 text-xs sm:text-base truncate">
<nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">,&nbsp;</span></nuxt-link>
</div>
@@ -25,7 +25,7 @@
<p class="font-mono text-xs sm:text-sm pl-1 sm:pl-1.5 pb-px">{{ totalDurationPretty }}</p>
</div>
</div>
<div class="flex-grow" />
<div class="grow" />
<ui-tooltip direction="top" :text="$strings.LabelClosePlayer">
<button :aria-label="$strings.LabelClosePlayer" class="material-symbols sm:px-2 py-1 lg:p-4 cursor-pointer text-xl sm:text-2xl" @click="closePlayer">close</button>
</ui-tooltip>
@@ -85,7 +85,8 @@ export default {
displayTitle: null,
currentPlaybackRate: 1,
syncFailedToast: null,
coverAspectRatio: 1
coverAspectRatio: 1,
lastChapterId: null
}
},
computed: {
@@ -155,7 +156,7 @@ export default {
return this.mediaMetadata.authors || []
},
libraryId() {
return this.streamLibraryItem ? this.streamLibraryItem.libraryId : null
return this.streamLibraryItem?.libraryId || null
},
totalDurationPretty() {
// Adjusted by playback rate
@@ -236,12 +237,16 @@ export default {
}
}, 1000)
},
checkChapterEnd(time) {
checkChapterEnd() {
if (!this.currentChapter) return
const chapterEndTime = this.currentChapter.end
const tolerance = 0.75
if (time >= chapterEndTime - tolerance) {
this.sleepTimerEnd()
// Track chapter transitions by comparing current chapter with last chapter
if (this.lastChapterId !== this.currentChapter.id) {
// Chapter changed - if we had a previous chapter, this means we crossed a boundary
if (this.lastChapterId) {
this.sleepTimerEnd()
}
this.lastChapterId = this.currentChapter.id
}
},
sleepTimerEnd() {
@@ -301,7 +306,7 @@ export default {
}
if (this.sleepTimerType === this.$constants.SleepTimerTypes.CHAPTER && this.sleepTimerSet) {
this.checkChapterEnd(time)
this.checkChapterEnd()
}
},
setDuration(duration) {
+2 -2
View File
@@ -1,5 +1,5 @@
<template>
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-2 sm:p-4 mb-8">
<div class="bg-bg rounded-md shadow-lg border border-white/5 p-2 sm:p-4 mb-8">
<div class="flex items-center mb-2">
<slot name="header-prefix"></slot>
<h1 class="text-xl">{{ headerText }}</h1>
@@ -39,4 +39,4 @@ export default {
color: white;
padding: 2px 4px;
}
</style>
</style>
+18 -29
View File
@@ -4,17 +4,15 @@
<div v-if="isShowingBookshelfToolbar" class="absolute top-0 -right-4 w-4 bg-bg h-10 pointer-events-none" />
<div id="siderail-buttons-container" role="navigation" aria-label="Library Navigation" :class="{ 'player-open': streamLibraryItem }" class="w-full overflow-y-auto overflow-x-hidden">
<nuxt-link :to="`/library/${currentLibraryId}`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="homePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<nuxt-link :to="`/library/${currentLibraryId}`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="homePage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">home</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonHome }}</p>
<div v-show="homePage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary" :to="`/library/${currentLibraryId}/podcast/latest`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastLatestPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isPodcastLibrary" :to="`/library/${currentLibraryId}/podcast/latest`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isPodcastLatestPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">&#xe241;</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonLatest }}</p>
@@ -22,27 +20,23 @@
<div v-show="isPodcastLatestPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="showLibrary ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
</svg>
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf`" class="w-full h-20 flex flex-col items-center justify-center text-white border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="showLibrary ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">import_contacts</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonLibrary }}</p>
<div v-show="showLibrary" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/series`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isSeriesPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" />
</svg>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/series`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isSeriesPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">view_column</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonSeries }}</p>
<div v-show="isSeriesPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="paramId === 'collections' ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/collections`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="paramId === 'collections' ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">&#xe431;</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonCollections }}</p>
@@ -50,7 +44,7 @@
<div v-show="paramId === 'collections'" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="showPlaylists" :to="`/library/${currentLibraryId}/bookshelf/playlists`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPlaylistsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="showPlaylists" :to="`/library/${currentLibraryId}/bookshelf/playlists`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isPlaylistsPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2.5xl">&#xe03d;</span>
<p class="pt-0.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonPlaylists }}</p>
@@ -58,20 +52,15 @@
<div v-show="isPlaylistsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<svg class="w-6 h-6" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z"
/>
</svg>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/bookshelf/authors`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isAuthorsPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">groups</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonAuthors }}</p>
<div v-show="isAuthorsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/narrators`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isNarratorsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/narrators`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isNarratorsPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">&#xe91f;</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.LabelNarrators }}</p>
@@ -79,7 +68,7 @@
<div v-show="isNarratorsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isBookLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/stats`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isStatsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isBookLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/stats`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isStatsPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">&#xf190;</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonStats }}</p>
@@ -87,7 +76,7 @@
<div v-show="isStatsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/search`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastSearchPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/search`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isPodcastSearchPage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="abs-icons icon-podcast text-xl"></span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonAdd }}</p>
@@ -95,7 +84,7 @@
<div v-show="isPodcastSearchPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastDownloadQueuePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-primary cursor-pointer relative" :class="isPodcastDownloadQueuePage ? 'bg-primary/80' : 'bg-bg/60'">
<span class="material-symbols text-2xl">&#xf090;</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonDownloadQueue }}</p>
@@ -103,20 +92,20 @@
<div v-show="isPodcastDownloadQueuePage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<nuxt-link v-if="numIssues" :to="`/library/${currentLibraryId}/bookshelf?filter=issues`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-opacity-40 cursor-pointer relative" :class="showingIssues ? 'bg-error bg-opacity-40' : 'bg-error bg-opacity-20'">
<nuxt-link v-if="numIssues" :to="`/library/${currentLibraryId}/bookshelf?filter=issues`" class="w-full h-20 flex flex-col items-center justify-center text-white/80 border-b border-primary/70 hover:bg-error/40 cursor-pointer relative" :class="showingIssues ? 'bg-error/40' : 'bg-error/20'">
<span class="material-symbols text-2xl">warning</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 1rem">{{ $strings.ButtonIssues }}</p>
<div v-show="showingIssues" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
<div class="absolute top-1 right-1 w-4 h-4 rounded-full bg-white bg-opacity-30 flex items-center justify-center">
<div class="absolute top-1 right-1 w-4 h-4 rounded-full bg-white/30 flex items-center justify-center">
<p class="text-xs font-mono pb-0.5">{{ numIssues }}</p>
</div>
</nuxt-link>
</div>
<div class="w-full h-12 px-1 py-2 border-t border-black/20 bg-bg absolute left-0" :style="{ bottom: streamLibraryItem ? '224px' : '65px' }">
<p class="underline font-mono text-xs text-center text-gray-300 leading-3 mb-1" @click="clickChangelog">v{{ $config.version }}</p>
<p class="underline font-mono text-xs text-center text-gray-300 leading-3 mb-1 cursor-pointer" @click="clickChangelog">v{{ $config.version }}</p>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xxs text-center block leading-3">Update</a>
<p v-else class="text-xxs text-gray-400 leading-3 text-center italic">{{ Source }}</p>
</div>
+2 -5
View File
@@ -7,7 +7,7 @@
<covers-author-image :author="author" />
<!-- Author name & num books overlay -->
<div cy-id="textInline" v-show="!searching && !nameBelow" class="absolute bottom-0 left-0 w-full py-1e bg-black bg-opacity-60 px-2e">
<div cy-id="textInline" v-show="!searching && !nameBelow" class="absolute bottom-0 left-0 w-full py-1e bg-black/60 px-2e">
<p class="text-center font-semibold truncate" :style="{ fontSize: 0.75 + 'em' }">{{ name }}</p>
<p class="text-center text-gray-200" :style="{ fontSize: 0.65 + 'em' }">{{ numBooks }} {{ $strings.LabelBooks }}</p>
</div>
@@ -25,7 +25,7 @@
</div>
<!-- Loading spinner -->
<div cy-id="spinner" v-show="searching" class="absolute top-0 left-0 z-10 w-full h-full bg-black bg-opacity-50 flex items-center justify-center">
<div cy-id="spinner" v-show="searching" class="absolute top-0 left-0 z-10 w-full h-full bg-black/50 flex items-center justify-center">
<widgets-loading-spinner size="" />
</div>
</div>
@@ -71,9 +71,6 @@ export default {
coverHeight() {
return this.cardHeight
},
userToken() {
return this.store.getters['user/getToken']
},
_author() {
return this.author || {}
},
+2 -2
View File
@@ -1,9 +1,9 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<div class="overflow-hidden bg-primary rounded" style="height: 50px; width: 40px">
<div class="overflow-hidden bg-primary rounded-sm" style="height: 50px; width: 40px">
<covers-author-image :author="author" />
</div>
<div class="flex-grow px-2 authorSearchCardContent h-full">
<div class="grow px-2 authorSearchCardContent h-full">
<p class="truncate text-sm">{{ name }}</p>
<p class="text-xs text-gray-400">{{ $getString('LabelXBooks', [numBooks]) }}</p>
</div>
+16 -8
View File
@@ -1,23 +1,31 @@
<template>
<div v-if="book" class="w-full border-b border-gray-700 pb-2">
<div class="flex py-1 hover:bg-gray-300 hover:bg-opacity-10 cursor-pointer" @click="selectMatch">
<div class="flex py-1 hover:bg-gray-300/10 cursor-pointer" @click="selectMatch">
<div class="min-w-12 max-w-12 md:min-w-20 md:max-w-20">
<div class="w-full bg-primary">
<img v-if="selectedCover" :src="selectedCover" class="h-full w-full object-contain" />
<div v-else class="w-12 h-12 md:w-20 md:h-20 bg-primary" />
</div>
</div>
<div v-if="!isPodcast" class="px-2 md:px-4 flex-grow">
<div v-if="!isPodcast" class="px-2 md:px-4 grow">
<div class="flex items-center">
<h1 class="text-sm md:text-base">{{ book.title }}</h1>
<div class="flex-grow" />
<div class="grow" />
<p class="text-sm md:text-base">{{ book.publishedYear }}</p>
</div>
<p v-if="book.author" class="text-gray-300 text-xs md:text-sm">{{ $getString('LabelByAuthor', [book.author]) }}</p>
<p v-if="book.narrator" class="text-gray-400 text-xs">{{ $strings.LabelNarrators }}: {{ book.narrator }}</p>
<p v-if="book.duration" class="text-gray-400 text-xs">{{ $strings.LabelDuration }}: {{ $elapsedPrettyExtended(bookDuration, false) }} {{ bookDurationComparison }}</p>
<div class="flex items-center">
<div>
<p v-if="book.author" class="text-gray-300 text-xs md:text-sm">{{ $getString('LabelByAuthor', [book.author]) }}</p>
<p v-if="book.narrator" class="text-gray-400 text-xs">{{ $strings.LabelNarrators }}: {{ book.narrator }}</p>
<p v-if="book.duration" class="text-gray-400 text-xs">{{ $strings.LabelDuration }}: {{ $elapsedPrettyExtended(bookDuration, false) }} {{ bookDurationComparison }}</p>
</div>
<div class="grow" />
<div v-if="book.matchConfidence" class="rounded-full px-2 py-1 text-xs whitespace-nowrap text-white" :class="book.matchConfidence > 0.95 ? 'bg-success/80' : 'bg-info/80'">{{ $strings.LabelMatchConfidence }}: {{ (book.matchConfidence * 100).toFixed(0) }}%</div>
</div>
<div v-if="book.series?.length" class="flex py-1 -mx-1">
<div v-for="(series, index) in book.series" :key="index" class="bg-white bg-opacity-10 rounded-full px-1 py-0.5 mx-1">
<div v-for="(series, index) in book.series" :key="index" class="bg-white/10 rounded-full px-1 py-0.5 mx-1">
<p class="leading-3 text-xs text-gray-400">
{{ series.series }}<span v-if="series.sequence">&nbsp;#{{ series.sequence }}</span>
</p>
@@ -27,7 +35,7 @@
<p class="text-gray-500 text-xs">{{ book.descriptionPlain }}</p>
</div>
</div>
<div v-else class="px-4 flex-grow">
<div v-else class="px-4 grow">
<h1>
<div class="flex items-center">{{ book.title }}<widgets-explicit-indicator v-if="book.explicit" /></div>
</h1>
@@ -0,0 +1,60 @@
<template>
<div class="flex items-center h-full px-1 overflow-hidden">
<covers-book-cover :library-item="libraryItem" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="grow px-2 episodeSearchCardContent">
<p class="truncate text-sm">{{ episodeTitle }}</p>
<p class="text-xs text-gray-200 truncate">{{ podcastTitle }}</p>
</div>
</div>
</template>
<script>
export default {
props: {
libraryItem: {
type: Object,
default: () => {}
},
episode: {
type: Object,
default: () => {}
}
},
data() {
return {}
},
computed: {
bookCoverAspectRatio() {
return this.$store.getters['libraries/getBookCoverAspectRatio']
},
coverWidth() {
if (this.bookCoverAspectRatio === 1) return 50 * 1.2
return 50
},
media() {
return this.libraryItem?.media || {}
},
mediaMetadata() {
return this.media.metadata || {}
},
episodeTitle() {
return this.episode.title || 'No Title'
},
podcastTitle() {
return this.mediaMetadata.title || 'No Title'
}
},
methods: {},
mounted() {}
}
</script>
<style>
.episodeSearchCardContent {
width: calc(100% - 80px);
height: 75px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-symbols text-2xl text-gray-200">category</span>
</div>
<div class="flex-grow px-2 tagSearchCardContent h-full">
<div class="grow px-2 tagSearchCardContent h-full">
<p class="truncate text-sm">{{ genre }}</p>
<p class="text-xs text-gray-400">{{ $getString('LabelXItems', [numItems]) }}</p>
</div>
+2 -2
View File
@@ -1,11 +1,11 @@
<template>
<div class="relative">
<div class="rounded-sm h-full relative" :style="{ width: cardWidth + 'px', height: cardHeight + 'px' }" @mouseover="mouseoverCard" @mouseleave="mouseleaveCard" @click="clickCard">
<div class="rounded-xs h-full relative" :style="{ width: cardWidth + 'px', height: cardHeight + 'px' }" @mouseover="mouseoverCard" @mouseleave="mouseleaveCard" @click="clickCard">
<nuxt-link :to="groupTo" class="cursor-pointer">
<div class="w-full h-full relative" :class="isHovering ? 'bg-black-400' : 'bg-primary'">
<covers-group-cover ref="groupcover" :id="groupEncode" :name="groupName" :type="groupType" :book-items="bookItems" :width="cardWidth" :height="cardHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div v-if="hasValidCovers" class="bg-black bg-opacity-60 absolute top-0 left-0 w-full h-full flex items-center justify-center text-center transition-opacity z-30" :class="isHovering ? '' : 'opacity-0'" :style="{ padding: `${sizeMultiplier}rem` }">
<div v-if="hasValidCovers" class="bg-black/60 absolute top-0 left-0 w-full h-full flex items-center justify-center text-center transition-opacity z-30" :class="isHovering ? '' : 'opacity-0'" :style="{ padding: `${sizeMultiplier}rem` }">
<p :style="{ fontSize: 1.2 * sizeMultiplier + 'rem' }">{{ groupName }}</p>
</div>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div class="flex items-center h-full px-1 overflow-hidden">
<covers-book-cover :library-item="libraryItem" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="flex-grow px-2 audiobookSearchCardContent">
<div class="grow px-2 audiobookSearchCardContent">
<p class="truncate text-sm">{{ title }}</p>
<p v-if="subtitle" class="truncate text-xs text-gray-300">{{ subtitle }}</p>
<p class="text-xs text-gray-200 truncate">{{ $getString('LabelByAuthor', [authorName]) }}</p>
@@ -4,7 +4,7 @@
<span v-if="isFinished" :class="taskIconStatus" class="material-symbols text-base">{{ actionIcon }}</span>
<widgets-loading-spinner v-else />
</div>
<div class="flex-grow px-2 taskRunningCardContent">
<div class="grow px-2 taskRunningCardContent">
<p class="truncate text-sm">{{ title }}</p>
<p class="truncate text-xs text-gray-300">{{ description }}</p>
@@ -13,7 +13,7 @@
<p v-if="isFailed && failedMessage" class="text-xs truncate text-red-500">{{ failedMessage }}</p>
<p v-else-if="!isFinished && cancelingScan" class="text-xs truncate">Canceling...</p>
</div>
<ui-btn v-if="userIsAdminOrUp && !isFinished && isLibraryScan && !cancelingScan" color="primary" :padding-y="1" :padding-x="1" class="text-xs w-16 max-w-16 truncate mr-1" @click.stop="cancelScan">{{ this.$strings.ButtonCancel }}</ui-btn>
<ui-btn v-if="userIsAdminOrUp && !isFinished && isLibraryScan && !cancelingScan" color="bg-primary" :padding-y="1" :padding-x="1" class="text-xs w-16 max-w-16 truncate mr-1" @click.stop="cancelScan">{{ this.$strings.ButtonCancel }}</ui-btn>
</div>
</template>
+57 -12
View File
@@ -1,11 +1,11 @@
<template>
<div class="relative w-full py-4 px-6 border border-white border-opacity-10 shadow-lg rounded-md my-6">
<div class="absolute -top-3 -left-3 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full">
<p class="text-base text-white text-opacity-80 font-mono">#{{ item.index }}</p>
<div class="relative w-full py-4 px-6 border border-white/10 shadow-lg rounded-md my-6">
<div class="absolute -top-3 -left-3 w-8 h-8 bg-bg border border-white/10 flex items-center justify-center rounded-full">
<p class="text-base text-white/80 font-mono">#{{ item.index }}</p>
</div>
<div v-if="!processing && !uploadFailed && !uploadSuccess" class="absolute -top-3 -right-3 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full hover:bg-error cursor-pointer" @click="$emit('remove')">
<span class="text-base text-white text-opacity-80 font-mono material-symbols">close</span>
<div v-if="!processing && !uploadFailed && !uploadSuccess" class="absolute -top-3 -right-3 w-8 h-8 bg-bg border border-white/10 flex items-center justify-center rounded-full hover:bg-error cursor-pointer" @click="$emit('remove')">
<span class="text-base text-white/80 font-mono material-symbols">close</span>
</div>
<template v-if="!uploadSuccess && !uploadFailed">
@@ -20,10 +20,10 @@
<div class="w-1/2 px-2">
<div v-if="!isPodcast" class="flex items-end">
<ui-text-input-with-label v-model.trim="itemData.author" :disabled="processing" :label="$strings.LabelAuthor" />
<ui-tooltip :text="$strings.LabelUploaderItemFetchMetadataHelp">
<div class="ml-2 mb-1 w-8 h-8 bg-bg border border-white border-opacity-10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer" @click="fetchMetadata">
<span class="text-base text-white text-opacity-80 font-mono material-symbols">sync</span>
</div>
<ui-tooltip direction="top" :text="$strings.LabelUploaderItemFetchMetadataHelp">
<button type="button" class="ml-2 mb-1 w-8 h-8 bg-bg border border-white/10 flex items-center justify-center rounded-full hover:bg-primary cursor-pointer" @click="fetchMetadata">
<span class="text-base text-white/80 font-mono material-symbols">sync</span>
</button>
</ui-tooltip>
</div>
<div v-else class="w-full">
@@ -61,8 +61,25 @@
<p class="text-base">"{{ itemData.title }}" {{ $strings.MessageUploaderItemFailed }}</p>
</widgets-alert>
<div v-if="isNonInteractable" class="absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 flex items-center justify-center z-20">
<ui-loading-indicator :text="nonInteractionLabel" />
<div v-if="isNonInteractable" class="absolute top-0 left-0 w-full h-full bg-black/50 flex items-center justify-center z-20">
<ui-loading-indicator>
<div class="mb-4">
<span class="text-lg font-medium text-white">
{{ nonInteractionLabel }}
</span>
</div>
<div v-if="isUploading" class="w-64 mx-auto mb-2">
<div class="flex items-center justify-center mb-2">
<span class="text-sm font-medium text-white/60 text-center w-full">
{{ uploadProgressText }}
</span>
</div>
<div class="w-full bg-primary/20 rounded-full h-2.5">
<div class="bg-green-500 h-2.5 rounded-full transition-all duration-300 ease-out" :style="{ width: uploadProgressPercent + '%' }"></div>
</div>
</div>
</ui-loading-indicator>
</div>
</div>
</template>
@@ -91,7 +108,11 @@ export default {
isUploading: false,
uploadFailed: false,
uploadSuccess: false,
isFetchingMetadata: false
isFetchingMetadata: false,
uploadProgress: {
loaded: 0,
total: 0
}
}
},
computed: {
@@ -116,6 +137,15 @@ export default {
} else if (this.isFetchingMetadata) {
return this.$strings.LabelFetchingMetadata
}
},
uploadProgressPercent() {
if (this.uploadProgress.total === 0) return 0
return Math.min(100, Math.round((this.uploadProgress.loaded / this.uploadProgress.total) * 100))
},
uploadProgressText() {
const loaded = this.$bytesPretty(this.uploadProgress.loaded)
const total = this.$bytesPretty(this.uploadProgress.total)
return `${this.uploadProgressPercent}% (${loaded} / ${total})`
}
},
methods: {
@@ -123,6 +153,21 @@ export default {
this.isUploading = status === 'uploading'
this.uploadFailed = status === 'failed'
this.uploadSuccess = status === 'success'
if (status !== 'uploading') {
this.uploadProgress = {
loaded: 0,
total: 0
}
}
},
setUploadProgress(progress) {
if (this.isUploading && progress) {
this.uploadProgress = {
loaded: progress.loaded || 0,
total: progress.total || 0
}
}
},
titleUpdated() {
this.error = ''
-142
View File
@@ -1,142 +0,0 @@
<template>
<div ref="card" :id="`album-card-${index}`" :style="{ width: cardWidth + 'px' }" class="absolute top-0 left-0 rounded-sm z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div class="relative" :style="{ height: coverHeight + 'px' }">
<div class="absolute top-0 left-0 w-full box-shadow-book shadow-height" />
<div class="w-full h-full bg-primary relative rounded overflow-hidden">
<covers-preview-cover ref="cover" :src="coverSrc" :width="cardWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
</div>
<div class="relative w-full">
<div v-if="!isAlternativeBookshelfView" class="categoryPlacard absolute z-30 left-0 right-0 mx-auto -bottom-6e h-6e rounded-md text-center" :style="{ width: Math.min(200, cardWidth) + 'px' }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-sm border" :style="{ padding: `0em ${0.5}em` }">
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
</div>
</div>
<div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8e h-8e py-1e rounded-md text-center">
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
<p class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ artist || '&nbsp;' }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
index: Number,
width: Number,
height: {
type: Number,
default: 192
},
bookshelfView: {
type: Number,
default: 0
},
albumMount: {
type: Object,
default: () => null
}
},
data() {
return {
album: null,
isSelectionMode: false,
selected: false,
isHovering: false
}
},
computed: {
bookCoverAspectRatio() {
return this.store.getters['libraries/getBookCoverAspectRatio']
},
cardWidth() {
return this.width || this.coverHeight
},
coverHeight() {
return this.height * this.sizeMultiplier
},
/*
cardHeight() {
return this.coverHeight + this.bottomTextHeight
},
bottomTextHeight() {
if (!this.isAlternativeBookshelfView) return 0
const lineHeight = 1.5
const remSize = 16
const baseHeight = this.sizeMultiplier * lineHeight * remSize
const titleHeight = this.labelFontSize * baseHeight
const paddingHeight = 4 * 2 * this.sizeMultiplier // py-1
return titleHeight + paddingHeight
},
*/
coverSrc() {
const config = this.$config || this.$nuxt.$config
if (!this.album || !this.album.libraryItemId) return `${config.routerBasePath}/book_placeholder.jpg`
return this.store.getters['globals/getLibraryItemCoverSrcById'](this.album.libraryItemId)
},
labelFontSize() {
if (this.width < 160) return 0.75
return 0.9
},
sizeMultiplier() {
return this.store.getters['user/getSizeMultiplier']
},
title() {
return this.album ? this.album.title : ''
},
artist() {
return this.album ? this.album.artist : ''
},
store() {
return this.$store || this.$nuxt.$store
},
currentLibraryId() {
return this.store.state.libraries.currentLibraryId
},
isAlternativeBookshelfView() {
const constants = this.$constants || this.$nuxt.$constants
return this.bookshelfView == constants.BookshelfView.DETAIL
}
},
methods: {
setEntity(album) {
this.album = album
},
setSelectionMode(val) {
this.isSelectionMode = val
},
mouseover() {
this.isHovering = true
},
mouseleave() {
this.isHovering = false
},
clickCard() {
if (!this.album) return
// const router = this.$router || this.$nuxt.$router
// router.push(`/album/${this.$encode(this.title)}`)
},
clickEdit() {
this.$emit('edit', this.album)
},
destroy() {
// destroy the vue listeners, etc
this.$destroy()
// remove the element from the DOM
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el)
} else if (this.$el && this.$el.remove) {
this.$el.remove()
}
}
},
mounted() {
if (this.albumMount) {
this.setEntity(this.albumMount)
}
}
}
</script>
+50 -23
View File
@@ -1,19 +1,19 @@
<template>
<div ref="card" :id="`book-card-${index}`" tabindex="0" :style="{ minWidth: coverWidth + 'px', maxWidth: coverWidth + 'px' }" class="absolute rounded-sm z-10 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div :id="`cover-area-${index}`" class="relative w-full top-0 left-0 rounded overflow-hidden z-10 bg-primary box-shadow-book" :style="{ height: coverHeight + 'px ' }">
<div ref="card" :id="`book-card-${index}`" tabindex="0" :style="{ minWidth: coverWidth + 'px', maxWidth: coverWidth + 'px' }" class="absolute rounded-xs z-10 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div :id="`cover-area-${index}`" class="relative w-full top-0 left-0 rounded-sm overflow-hidden z-10 bg-primary box-shadow-book" :style="{ height: coverHeight + 'px ' }">
<!-- When cover image does not fill -->
<div cy-id="coverBg" v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
<div cy-id="coverBg" v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-xs bg-primary">
<div class="absolute cover-bg" ref="coverBg" />
</div>
<div cy-id="seriesSequenceList" v-if="seriesSequenceList" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-20 text-right" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #78350f">
<div cy-id="seriesSequenceList" v-if="seriesSequenceList" class="absolute rounded-lg bg-black/90 box-shadow-md z-20 text-right" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #78350f">
<p :style="{ fontSize: 0.8 + 'em' }">#{{ seriesSequenceList }}</p>
</div>
<div cy-id="booksInSeries" v-else-if="booksInSeries" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-20" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #cd9d49dd">
<div cy-id="booksInSeries" v-else-if="booksInSeries" class="absolute rounded-lg bg-black/90 box-shadow-md z-20" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #cd9d49dd">
<p :style="{ fontSize: 0.8 + 'em' }">{{ booksInSeries }}</p>
</div>
<div class="w-full h-full absolute top-0 left-0 rounded overflow-hidden z-10">
<div class="w-full h-full absolute top-0 left-0 rounded-sm overflow-hidden z-10">
<div cy-id="titleImageNotReady" v-show="libraryItem && !imageReady" aria-hidden="true" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: 0.5 + 'em' }">
<p :style="{ fontSize: 0.8 + 'em' }" class="text-gray-300 text-center">{{ title }}</p>
</div>
@@ -35,7 +35,7 @@
<div cy-id="progressBar" v-if="!isPodcast || episodeProgress" class="absolute bottom-0 left-0 h-1e max-w-full z-20 rounded-b box-shadow-progressbar" :class="itemIsFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: coverWidth * userProgressPercent + 'px' }"></div>
<!-- Overlay is not shown if collapsing series in library -->
<div cy-id="overlay" v-show="!booksInSeries && libraryItem && (isHovering || isSelectionMode || isMoreMenuOpen) && !processing" class="w-full h-full absolute top-0 left-0 z-10 bg-black rounded md:block" :class="overlayWrapperClasslist">
<div cy-id="overlay" v-show="!booksInSeries && libraryItem && (isHovering || isSelectionMode || isMoreMenuOpen) && !processing" class="w-full h-full absolute top-0 left-0 z-10 bg-black rounded-sm md:block" :class="overlayWrapperClasslist">
<div cy-id="playButton" v-show="showPlayButton" class="h-full flex items-center justify-center pointer-events-none">
<div class="hover:text-white text-gray-200 hover:scale-110 transform duration-200 pointer-events-auto" @click.stop.prevent="play">
<span class="material-symbols fill" :style="{ fontSize: playIconFontSize + 'em' }">play_arrow</span>
@@ -68,17 +68,17 @@
</div>
<!-- Processing/loading spinner overlay -->
<div cy-id="loadingSpinner" v-if="processing" class="w-full h-full absolute top-0 left-0 z-10 bg-black bg-opacity-40 rounded flex items-center justify-center">
<div cy-id="loadingSpinner" v-if="processing" class="w-full h-full absolute top-0 left-0 z-10 bg-black/40 rounded-sm flex items-center justify-center">
<widgets-loading-spinner size="la-lg" />
</div>
<!-- Series name overlay -->
<div cy-id="seriesNameOverlay" v-if="booksInSeries && libraryItem && isHovering" class="w-full h-full absolute top-0 left-0 z-10 bg-black bg-opacity-60 rounded flex items-center justify-center" :style="{ padding: 1 + 'em' }">
<div cy-id="seriesNameOverlay" v-if="booksInSeries && libraryItem && isHovering" class="w-full h-full absolute top-0 left-0 z-10 bg-black/60 rounded-sm flex items-center justify-center" :style="{ padding: 1 + 'em' }">
<p v-if="seriesName" class="text-gray-200 text-center" :style="{ fontSize: 1.1 + 'em' }">{{ seriesName }}</p>
</div>
<!-- Error widget -->
<ui-tooltip cy-id="ErrorTooltip" v-if="showError" :text="errorText" class="absolute bottom-4e left-0 z-10">
<ui-tooltip cy-id="ErrorTooltip" v-if="showError" :text="errorText" plaintext class="absolute bottom-4e left-0 z-10">
<div :style="{ height: 1.5 + 'em', width: 2.5 + 'em' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<span class="material-symbols text-red-100 pr-1e" :style="{ fontSize: 0.875 + 'em' }">priority_high</span>
</div>
@@ -94,19 +94,20 @@
</div>
<!-- Series sequence -->
<div cy-id="seriesSequence" v-if="seriesSequence && !isHovering && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `${0.1}em ${0.25}em` }">
<div cy-id="seriesSequence" v-if="seriesSequence && !isHovering && !isSelectionMode" class="absolute rounded-lg bg-black/90 box-shadow-md z-10" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `${0.1}em ${0.25}em` }">
<p :style="{ fontSize: 0.8 + 'em' }">#{{ seriesSequence }}</p>
</div>
<!-- Podcast Episode # -->
<div cy-id="podcastEpisodeNumber" v-if="recentEpisodeNumber !== null && !isHovering && !isSelectionMode && !processing" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `${0.1}em ${0.25}em` }">
<div cy-id="podcastEpisodeNumber" v-if="recentEpisodeNumber !== null && !isHovering && !isSelectionMode && !processing" class="absolute rounded-lg bg-black/90 box-shadow-md z-10" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `${0.1}em ${0.25}em` }">
<p :style="{ fontSize: 0.8 + 'em' }">
Episode<span v-if="recentEpisodeNumber"> #{{ recentEpisodeNumber }}</span>
Episode
<span v-if="recentEpisodeNumber">#{{ recentEpisodeNumber }}</span>
</p>
</div>
<!-- Podcast Num Episodes -->
<div cy-id="numEpisodes" v-else-if="!numEpisodesIncomplete && numEpisodes && !isHovering && !isSelectionMode" class="absolute rounded-full bg-black bg-opacity-90 box-shadow-md z-10 flex items-center justify-center" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', width: 1.25 + 'em', height: 1.25 + 'em' }">
<div cy-id="numEpisodes" v-else-if="!numEpisodesIncomplete && numEpisodes && !isHovering && !isSelectionMode" class="absolute rounded-full bg-black/90 box-shadow-md z-10 flex items-center justify-center" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', width: 1.25 + 'em', height: 1.25 + 'em' }">
<p :style="{ fontSize: 0.8 + 'em' }" role="status" :aria-label="$strings.LabelNumberOfEpisodes">{{ numEpisodes }}</p>
</div>
@@ -120,12 +121,12 @@
<!-- Alternative bookshelf title/author/sort -->
<div cy-id="detailBottom" :id="`description-area-${index}`" v-if="isAlternativeBookshelfView || isAuthorBookshelfView" dir="auto" class="relative mt-2e mb-2e left-0 z-50 w-full">
<div :style="{ fontSize: 0.9 + 'em' }">
<ui-tooltip v-if="displayTitle" :text="displayTitle" :disabled="!displayTitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<ui-tooltip v-if="displayTitle" :text="displayTitle" plaintext :disabled="!displayTitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<p cy-id="title" ref="displayTitle" class="truncate">{{ displayTitle }}</p>
<widgets-explicit-indicator cy-id="explicitIndicator" v-if="isExplicit" />
</ui-tooltip>
</div>
<ui-tooltip v-if="showSubtitles" :text="displaySubtitle" :disabled="!displaySubtitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<ui-tooltip v-if="showSubtitles" :text="displaySubtitle" plaintext :disabled="!displaySubtitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<p cy-id="subtitle" class="truncate" ref="displaySubtitle" :style="{ fontSize: 0.6 + 'em' }">{{ displaySubtitle }}</p>
</ui-tooltip>
<p cy-id="line2" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displayLineTwo || '&nbsp;' }}</p>
@@ -198,7 +199,10 @@ export default {
return this.store.getters['user/getSizeMultiplier']
},
dateFormat() {
return this.store.state.serverSettings.dateFormat
return this.store.getters['getServerSetting']('dateFormat')
},
timeFormat() {
return this.store.getters['getServerSetting']('timeFormat')
},
_libraryItem() {
return this.libraryItem || {}
@@ -223,8 +227,7 @@ export default {
return this.mediaMetadata.explicit || false
},
placeholderUrl() {
const config = this.$config || this.$nuxt.$config
return `${config.routerBasePath}/book_placeholder.jpg`
return this.store.getters['globals/getPlaceholderCoverSrc']
},
bookCoverSrc() {
return this.store.getters['globals/getLibraryItemCoverSrc'](this._libraryItem, this.placeholderUrl)
@@ -346,6 +349,18 @@ export default {
if (this.mediaMetadata.publishedYear) return this.$getString('LabelPublishedDate', [this.mediaMetadata.publishedYear])
return '\u00A0'
}
if (this.orderBy === 'progress') {
if (!this.userProgressLastUpdated) return '\u00A0'
return this.$getString('LabelLastProgressDate', [this.$formatDatetime(this.userProgressLastUpdated, this.dateFormat, this.timeFormat)])
}
if (this.orderBy === 'progress.createdAt') {
if (!this.userProgressStartedDate) return '\u00A0'
return this.$getString('LabelStartedDate', [this.$formatDatetime(this.userProgressStartedDate, this.dateFormat, this.timeFormat)])
}
if (this.orderBy === 'progress.finishedAt') {
if (!this.userProgressFinishedDate) return '\u00A0'
return this.$getString('LabelFinishedDate', [this.$formatDatetime(this.userProgressFinishedDate, this.dateFormat, this.timeFormat)])
}
return null
},
episodeProgress() {
@@ -378,6 +393,18 @@ export default {
let progressPercent = this.itemIsFinished ? 1 : this.booksInSeries ? this.seriesProgressPercent : this.useEBookProgress ? this.userProgress?.ebookProgress || 0 : this.userProgress?.progress || 0
return Math.max(Math.min(1, progressPercent), 0)
},
userProgressLastUpdated() {
if (!this.userProgress) return null
return this.userProgress.lastUpdate
},
userProgressStartedDate() {
if (!this.userProgress) return null
return this.userProgress.startedAt
},
userProgressFinishedDate() {
if (!this.userProgress) return null
return this.userProgress.finishedAt
},
itemIsFinished() {
if (this.booksInSeries) return this.seriesIsFinished
return this.userProgress ? !!this.userProgress.isFinished : false
@@ -430,8 +457,8 @@ export default {
},
overlayWrapperClasslist() {
const classes = []
if (this.isSelectionMode) classes.push('bg-opacity-60')
else classes.push('bg-opacity-40')
if (this.isSelectionMode) classes.push('bg-black/60')
else classes.push('bg-black/40')
if (this.selected) {
classes.push('border-2 border-yellow-400')
}
@@ -761,11 +788,11 @@ export default {
},
showEditModalFiles() {
// More menu func
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'files' })
this.$emit('edit', this.libraryItem, 'files')
},
showEditModalMatch() {
// More menu func
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'match' })
this.$emit('edit', this.libraryItem, 'match')
},
sendToDevice(deviceName) {
// More menu func
@@ -1,13 +1,13 @@
<template>
<div ref="card" :id="`collection-card-${index}`" role="button" :style="{ width: cardWidth + 'px' }" class="absolute top-0 left-0 rounded-sm z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div ref="card" :id="`collection-card-${index}`" role="button" :style="{ width: cardWidth + 'px' }" class="absolute top-0 left-0 rounded-xs z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div class="relative" :style="{ height: coverHeight + 'px' }">
<div class="absolute top-0 left-0 w-full box-shadow-book shadow-height" />
<div class="w-full h-full bg-primary relative rounded overflow-hidden">
<div class="w-full h-full bg-primary relative rounded-sm overflow-hidden">
<covers-collection-cover ref="cover" :book-items="books" :width="cardWidth" :height="coverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div v-show="isHovering && userCanUpdate" class="w-full h-full absolute top-0 left-0 z-10 bg-black bg-opacity-40 pointer-events-none">
<div v-show="isHovering && userCanUpdate" class="w-full h-full absolute top-0 left-0 z-10 bg-black/40 pointer-events-none">
<div class="absolute pointer-events-auto" :style="{ top: 0.5 + 'em', right: 0.5 + 'em' }" @click.stop.prevent="clickEdit">
<span class="material-symbols text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
<span class="material-symbols text-white/75 hover:text-white/100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
</div>
</div>
@@ -15,7 +15,7 @@
</div>
<div v-if="!isAlternativeBookshelfView" class="categoryPlacard absolute z-30 left-0 right-0 mx-auto -bottom-6e h-6e rounded-md text-center" :style="{ width: Math.min(200, cardWidth) + 'px' }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-sm border" :style="{ padding: `0em ${0.5}em` }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-xs border" :style="{ padding: `0em ${0.5}em` }">
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
</div>
</div>
+5 -5
View File
@@ -1,19 +1,19 @@
<template>
<div ref="card" :id="`playlist-card-${index}`" role="button" :style="{ width: cardWidth + 'px', fontSize: sizeMultiplier + 'rem' }" class="absolute top-0 left-0 rounded-sm z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div ref="card" :id="`playlist-card-${index}`" role="button" :style="{ width: cardWidth + 'px', fontSize: sizeMultiplier + 'rem' }" class="absolute top-0 left-0 rounded-xs z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div class="relative" :style="{ height: coverHeight + 'px' }">
<div class="absolute top-0 left-0 w-full box-shadow-book shadow-height" />
<div class="w-full h-full bg-primary relative rounded overflow-hidden">
<div class="w-full h-full bg-primary relative rounded-sm overflow-hidden">
<covers-playlist-cover ref="cover" :items="items" :width="cardWidth" :height="coverHeight" />
</div>
<div v-show="isHovering && userCanUpdate" class="w-full h-full absolute top-0 left-0 z-10 bg-black bg-opacity-40 pointer-events-none">
<div v-show="isHovering && userCanUpdate" class="w-full h-full absolute top-0 left-0 z-10 bg-black/40 pointer-events-none">
<div class="absolute pointer-events-auto" :style="{ top: 0.5 + 'em', right: 0.5 + 'em' }" @click.stop.prevent="clickEdit">
<span class="material-symbols text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
<span class="material-symbols text-white/75 hover:text-white/100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
</div>
</div>
</div>
<div v-if="!isAlternativeBookshelfView" class="categoryPlacard absolute z-30 -bottom-6e left-0 right-0 mx-auto h-6e rounded-md text-center" :style="{ width: Math.min(200, width) + 'px' }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-sm border" :style="{ padding: `0em ${0.5}em` }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-xs border" :style="{ padding: `0em ${0.5}em` }">
<p class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ title }}</p>
</div>
</div>
+7 -7
View File
@@ -1,18 +1,18 @@
<template>
<div cy-id="card" ref="card" :id="`series-card-${index}`" tabindex="0" :style="{ width: cardWidth + 'px' }" class="absolute rounded-sm z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div cy-id="card" ref="card" :id="`series-card-${index}`" tabindex="0" :style="{ width: cardWidth + 'px' }" class="absolute rounded-xs z-30 cursor-pointer" @mousedown.prevent @mouseup.prevent @mousemove.prevent @mouseover="mouseover" @mouseleave="mouseleave" @click="clickCard">
<div cy-id="covers-area" class="relative" :style="{ height: coverHeight + 'px' }">
<div class="absolute top-0 left-0 w-full box-shadow-book shadow-height" />
<div class="w-full h-full bg-primary relative rounded overflow-hidden z-0">
<div class="w-full h-full bg-primary relative rounded-sm overflow-hidden z-0">
<covers-group-cover v-if="series" ref="cover" :id="seriesId" :name="displayTitle" :book-items="books" :width="cardWidth" :height="coverHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div cy-id="seriesLengthMarker" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-20" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #cd9d49dd">
<div cy-id="seriesLengthMarker" class="absolute rounded-lg bg-black/90 box-shadow-md z-20" :style="{ top: 0.375 + 'em', right: 0.375 + 'em', padding: `0.1em 0.25em` }" style="background-color: #cd9d49dd">
<p :style="{ fontSize: 0.8 + 'em' }" role="status" :aria-label="$strings.LabelNumberOfBooks">{{ books.length }}</p>
</div>
<div cy-id="seriesProgressBar" v-if="seriesPercentInProgress > 0" class="absolute bottom-0 left-0 h-1e shadow-sm max-w-full z-10 rounded-b w-full box-shadow-progressbar" :class="isSeriesFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: seriesPercentInProgress * 100 + '%' }" />
<div cy-id="seriesProgressBar" v-if="seriesPercentInProgress > 0" class="absolute bottom-0 left-0 h-1e shadow-xs max-w-full z-10 rounded-b w-full box-shadow-progressbar" :class="isSeriesFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: seriesPercentInProgress * 100 + '%' }" />
<div cy-id="hoveringDisplayTitle" v-if="hasValidCovers" aria-hidden="true" class="bg-black bg-opacity-60 absolute top-0 left-0 w-full h-full flex items-center justify-center text-center transition-opacity" :class="isHovering ? '' : 'opacity-0'" :style="{ padding: '1em' }">
<div cy-id="hoveringDisplayTitle" v-if="hasValidCovers" aria-hidden="true" class="bg-black/60 absolute top-0 left-0 w-full h-full flex items-center justify-center text-center transition-opacity" :class="isHovering ? '' : 'opacity-0'" :style="{ padding: '1em' }">
<p :style="{ fontSize: 1.2 + 'em' }">{{ displayTitle }}</p>
</div>
@@ -20,7 +20,7 @@
</div>
<div cy-id="standardBottomText" v-if="!isAlternativeBookshelfView" class="categoryPlacard absolute z-10 left-0 right-0 mx-auto -bottom-6e h-6e rounded-md text-center" :style="{ width: Math.min(200, cardWidth) + 'px' }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-sm border" :style="{ padding: `0em 0.5em` }">
<div class="w-full h-full shinyBlack flex items-center justify-center rounded-xs border" :style="{ padding: `0em 0.5em` }">
<p cy-id="standardBottomDisplayTitle" class="truncate" :style="{ fontSize: labelFontSize + 'em' }">{{ displayTitle }}</p>
</div>
</div>
@@ -71,7 +71,7 @@ export default {
return this.height * this.sizeMultiplier
},
dateFormat() {
return this.store.state.serverSettings.dateFormat
return this.store.getters['getServerSetting']('dateFormat')
},
labelFontSize() {
if (this.width < 160) return 0.75
+1 -1
View File
@@ -7,7 +7,7 @@
</div>
<!-- Narrator name & num books overlay -->
<div class="absolute bottom-0 left-0 w-full py-1e bg-black bg-opacity-60 px-2e">
<div class="absolute bottom-0 left-0 w-full py-1e bg-black/60 px-2e">
<p cy-id="name" class="text-center font-semibold truncate text-gray-200" :style="{ fontSize: 0.75 + 'em' }">{{ name }}</p>
<p cy-id="numBooks" class="text-center text-gray-200" :style="{ fontSize: 0.65 + 'em' }">{{ numBooks }} Book{{ numBooks === 1 ? '' : 's' }}</p>
</div>
@@ -3,7 +3,7 @@
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-symbols text-2xl text-gray-200">&#xe91f;</span>
</div>
<div class="flex-grow px-2 narratorSearchCardContent h-full">
<div class="grow px-2 narratorSearchCardContent h-full">
<p class="truncate text-sm">{{ narrator }}</p>
<p class="text-xs text-gray-400">{{ $getString('LabelXBooks', [numBooks]) }}</p>
</div>
+5 -5
View File
@@ -1,17 +1,17 @@
<template>
<div class="w-full border border-white border-opacity-10 rounded-xl p-4 my-2" :class="notification.enabled ? 'bg-primary bg-opacity-25' : 'bg-error bg-opacity-5'">
<div class="w-full border border-white/10 rounded-xl p-4 my-2" :class="notification.enabled ? 'bg-primary/25' : 'bg-error/5'">
<div class="flex flex-wrap items-center">
<p class="text-base md:text-lg font-semibold pr-4">{{ eventName }}</p>
<div class="flex-grow" />
<div class="grow" />
<ui-btn v-if="eventName === 'onTest' && notification.enabled" :loading="testing" small class="mr-2" @click.stop="fireTestEventAndSucceed">{{ this.$strings.ButtonFireOnTest }}</ui-btn>
<ui-btn v-if="eventName === 'onTest' && notification.enabled" :loading="testing" small class="mr-2" color="red-600" @click.stop="fireTestEventAndFail">{{ this.$strings.ButtonFireAndFail }}</ui-btn>
<ui-btn v-if="eventName === 'onTest' && notification.enabled" :loading="testing" small class="mr-2" color="bg-red-600" @click.stop="fireTestEventAndFail">{{ this.$strings.ButtonFireAndFail }}</ui-btn>
<!-- <ui-btn v-if="eventName === 'onTest' && notification.enabled" :loading="testing" small class="mr-2" @click.stop="rapidFireTestEvents">Rapid Fire</ui-btn> -->
<ui-btn v-else-if="notification.enabled" :loading="sendingTest" small class="mr-2" @click.stop="sendTestClick">{{ this.$strings.ButtonTest }}</ui-btn>
<ui-btn v-else :loading="enabling" small color="success" class="mr-2" @click="enableNotification">{{ this.$strings.ButtonEnable }}</ui-btn>
<ui-btn v-else :loading="enabling" small color="bg-success" class="mr-2" @click="enableNotification">{{ this.$strings.ButtonEnable }}</ui-btn>
<ui-icon-btn :size="7" icon-font-size="1.1rem" icon="edit" class="mr-2" @click="editNotification" />
<ui-icon-btn bg-color="error" :size="7" icon-font-size="1.2rem" icon="delete" @click="deleteNotificationClick" />
<ui-icon-btn bg-color="bg-error" :size="7" icon-font-size="1.2rem" icon="delete" @click="deleteNotificationClick" />
</div>
<div class="pt-4">
<p class="text-gray-300 text-xs md:text-sm mb-2">{{ notification.urls.join(', ') }}</p>
@@ -1,5 +1,5 @@
<template>
<div ref="wrapper" class="w-full p-2 border border-white border-opacity-10 rounded">
<div ref="wrapper" class="w-full p-2 border border-white/10 rounded-sm">
<div class="flex">
<div class="w-16 min-w-16">
<div class="w-full h-16 bg-primary">
@@ -7,7 +7,7 @@
</div>
<p class="text-gray-400 text-xxs pt-1 text-center">{{ numEpisodes }} {{ $strings.HeaderEpisodes }}</p>
</div>
<div class="flex-grow pl-2" :style="{ maxWidth: detailsWidth + 'px' }">
<div class="grow pl-2" :style="{ maxWidth: detailsWidth + 'px' }">
<p class="mb-1">{{ title }}</p>
<p class="text-xs mb-1 text-gray-300">{{ author }}</p>
<p class="text-xs mb-2 text-gray-200">{{ description }}</p>
@@ -68,4 +68,4 @@ export default {
this.width = this.$refs.wrapper.clientWidth
}
}
</script>
</script>
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<covers-group-cover :name="name" :book-items="bookItems" :width="60" :height="60" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="flex-grow px-2 seriesSearchCardContent h-full">
<div class="grow px-2 seriesSearchCardContent h-full">
<p class="truncate text-sm">{{ name }}</p>
</div>
</div>
+1 -1
View File
@@ -3,7 +3,7 @@
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-symbols text-2xl text-gray-200">local_offer</span>
</div>
<div class="flex-grow px-2 tagSearchCardContent h-full">
<div class="grow px-2 tagSearchCardContent h-full">
<p class="truncate text-sm">{{ tag }}</p>
<p class="text-xs text-gray-400">{{ $getString('LabelXItems', [numItems]) }}</p>
</div>
@@ -1,45 +1,45 @@
<template>
<div>
<div v-if="narrators?.length" class="flex py-0.5 mt-4">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelNarrators }}</span>
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelNarrators }}</span>
</div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden overflow-ellipsis">
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
<template v-for="(narrator, index) in narrators">
<nuxt-link :key="narrator" :to="`/library/${libraryId}/bookshelf?filter=narrators.${$encode(narrator)}`" class="hover:underline">{{ narrator }}</nuxt-link
><span :key="index" v-if="index < narrators.length - 1">,&nbsp;</span>
</template>
</div>
</div>
<div v-if="publishedYear" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span>
<div v-if="publishedYear" role="paragraph" class="flex py-0.5">
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublishYear }}</span>
</div>
<div>
{{ publishedYear }}
</div>
</div>
<div v-if="publisher" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelPublisher }}</span>
<div v-if="publisher" role="paragraph" class="flex py-0.5">
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPublisher }}</span>
</div>
<div>
<nuxt-link :to="`/library/${libraryId}/bookshelf?filter=publishers.${$encode(publisher)}`" class="hover:underline">{{ publisher }}</nuxt-link>
</div>
</div>
<div v-if="podcastType" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
<div v-if="podcastType" role="paragraph" class="flex py-0.5">
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelPodcastType }}</span>
</div>
<div class="capitalize">
{{ podcastType }}
</div>
</div>
<div class="flex py-0.5" v-if="genres.length">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelGenres }}</span>
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelGenres }}</span>
</div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden overflow-ellipsis">
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
<template v-for="(genre, index) in genres">
<nuxt-link :key="genre" :to="`/library/${libraryId}/bookshelf?filter=genres.${$encode(genre)}`" class="hover:underline">{{ genre }}</nuxt-link
><span :key="index" v-if="index < genres.length - 1">,&nbsp;</span>
@@ -47,10 +47,10 @@
</div>
</div>
<div class="flex py-0.5" v-if="tags.length">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelTags }}</span>
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelTags }}</span>
</div>
<div class="max-w-[calc(100vw-10rem)] overflow-hidden overflow-ellipsis">
<div class="max-w-[calc(100vw-10rem)] overflow-hidden text-ellipsis">
<template v-for="(tag, index) in tags">
<nuxt-link :key="tag" :to="`/library/${libraryId}/bookshelf?filter=tags.${$encode(tag)}`" class="hover:underline">{{ tag }}</nuxt-link
><span :key="index" v-if="index < tags.length - 1">,&nbsp;</span>
@@ -58,24 +58,24 @@
</div>
</div>
<div v-if="language" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelLanguage }}</span>
</div>
<div>
<nuxt-link :to="`/library/${libraryId}/bookshelf?filter=languages.${$encode(language)}`" class="hover:underline">{{ language }}</nuxt-link>
</div>
</div>
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
<div v-if="tracks.length || (isPodcast && totalPodcastDuration)" role="paragraph" class="flex py-0.5">
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelDuration }}</span>
</div>
<div>
{{ durationPretty }}
</div>
</div>
<div class="flex py-0.5">
<div class="w-24 min-w-24 sm:w-32 sm:min-w-32">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelSize }}</span>
<div role="paragraph" class="flex py-0.5">
<div class="w-34 min-w-34 sm:w-34 sm:min-w-34 break-words">
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelSize }}</span>
</div>
<div>
{{ sizePretty }}
+13 -11
View File
@@ -1,23 +1,25 @@
<template>
<div ref="wrapper" class="relative" v-click-outside="clickOutside">
<button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs">{{ selectedText }}</span>
</span>
<div class="relative h-9">
<button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded-sm shadow-xs pl-3 pr-3 py-0 text-left focus:outline-hidden cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs">{{ selectedText }}</span>
</span>
</button>
<span v-if="selected === 'all'" class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</span>
<div v-else class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-300" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected">
<button v-else type="button" :aria-label="$strings.ButtonClearFilter" class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-300" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected">
<span class="material-symbols" style="font-size: 1.1rem">close</span>
</div>
</button>
</button>
</div>
<div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 text-sm ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
<div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 text-sm ring-1 ring-black/5 overflow-auto focus:outline-hidden">
<ul class="h-full w-full" role="menu">
<template v-for="item in items">
<li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedOption(item)">
<li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="menuitem" @click="clickedOption(item)">
<div class="flex items-center justify-between">
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
</div>
@@ -86,4 +88,4 @@ export default {
}
}
}
</script>
</script>
+14 -2
View File
@@ -9,7 +9,7 @@
<span v-else class="material-symbols" style="font-size: 1.2rem">close</span>
</button>
</div>
<div v-show="showMenu && (lastSearch || isTyping)" class="absolute z-40 -mt-px w-full max-w-64 sm:max-w-80 sm:w-80 bg-bg border border-black-200 shadow-lg rounded-md py-1 px-2 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm globalSearchMenu" @mousedown.stop.prevent>
<div v-show="showMenu && (lastSearch || isTyping)" class="absolute z-40 -mt-px w-full max-w-64 sm:max-w-80 sm:w-80 bg-bg border border-black-200 shadow-lg rounded-md py-1 px-2 text-base ring-1 ring-black/5 overflow-auto focus:outline-hidden sm:text-sm globalSearchMenu" @mousedown.stop.prevent>
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
<li v-if="isTyping" class="py-2 px-2">
<p>{{ $strings.MessageThinking }}</p>
@@ -39,6 +39,15 @@
</li>
</template>
<p v-if="episodeResults.length" class="uppercase text-xs text-gray-400 my-1 px-1 font-semibold">{{ $strings.LabelEpisodes }}</p>
<template v-for="item in episodeResults">
<li :key="item.libraryItem.recentEpisode.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/item/${item.libraryItem.id}`">
<cards-episode-search-card :episode="item.libraryItem.recentEpisode" :library-item="item.libraryItem" />
</nuxt-link>
</li>
</template>
<p v-if="authorResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelAuthors }}</p>
<template v-for="item in authorResults">
<li :key="item.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
@@ -100,6 +109,7 @@ export default {
isFetching: false,
search: null,
podcastResults: [],
episodeResults: [],
bookResults: [],
authorResults: [],
seriesResults: [],
@@ -115,7 +125,7 @@ export default {
return this.$store.state.libraries.currentLibraryId
},
totalResults() {
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length + this.episodeResults.length
}
},
methods: {
@@ -132,6 +142,7 @@ export default {
this.search = null
this.lastSearch = null
this.podcastResults = []
this.episodeResults = []
this.bookResults = []
this.authorResults = []
this.seriesResults = []
@@ -175,6 +186,7 @@ export default {
if (!this.isFetching) return
this.podcastResults = searchResults.podcast || []
this.episodeResults = searchResults.episodes || []
this.bookResults = searchResults.book || []
this.authorResults = searchResults.authors || []
this.seriesResults = searchResults.series || []
@@ -1,7 +1,7 @@
<template>
<div ref="wrapper" class="relative" v-click-outside="clickOutside">
<div class="relative h-7">
<button type="button" class="relative w-full h-full bg-bg border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<button type="button" class="relative w-full h-full bg-bg border border-gray-500 hover:border-gray-400 rounded-sm shadow-xs pl-3 pr-3 py-0 text-left focus:outline-hidden sm:text-sm cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span>
</span>
@@ -16,7 +16,7 @@
</button>
</div>
<div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm libraryFilterMenu">
<div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg rounded-md py-1 ring-1 ring-black/5 overflow-auto focus:outline-hidden text-sm libraryFilterMenu">
<ul v-show="!sublist" class="h-full w-full" role="menu">
<template v-for="item in selectItems">
<li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="menuitem" :aria-haspopup="item.sublist ? '' : 'menu'" @click="clickedOption(item)">
@@ -94,6 +94,9 @@ export default {
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
userCanAccessExplicitContent() {
return this.$store.getters['user/getUserCanAccessExplicitContent']
},
libraryMediaType() {
return this.$store.getters['libraries/getCurrentLibraryMediaType']
},
@@ -239,6 +242,15 @@ export default {
sublist: false
}
]
if (this.userCanAccessExplicitContent) {
items.push({
text: this.$strings.LabelExplicit,
value: 'explicit',
sublist: false
})
}
if (this.userIsAdminOrUp) {
items.push({
text: this.$strings.LabelShareOpen,
@@ -249,7 +261,7 @@ export default {
return items
},
podcastItems() {
return [
const items = [
{
text: this.$strings.LabelAll,
value: 'all'
@@ -276,8 +288,23 @@ export default {
text: this.$strings.ButtonIssues,
value: 'issues',
sublist: false
},
{
text: this.$strings.LabelRSSFeedOpen,
value: 'feed-open',
sublist: false
}
]
if (this.userCanAccessExplicitContent) {
items.push({
text: this.$strings.LabelExplicit,
value: 'explicit',
sublist: false
})
}
return items
},
selectItems() {
if (this.isSeries) return this.seriesItems
@@ -311,6 +338,18 @@ export default {
const series = this.series.find((se) => se.id == decoded)
if (series) filterValue = series.name
}
} else if (parts[0] === 'progress') {
const item = this.progress.find((p) => p.id == decoded)
if (item) filterValue = item.name
} else if (parts[0] === 'tracks') {
const item = this.tracks.find((t) => t.id == decoded)
if (item) filterValue = item.name
} else if (parts[0] === 'ebooks') {
const item = this.ebooks.find((e) => e.id == decoded)
if (item) filterValue = item.name
} else if (parts[0] === 'missing') {
const item = this.missing.find((m) => m.id == decoded)
if (item) filterValue = item.name
} else {
filterValue = decoded
}
@@ -1,13 +1,13 @@
<template>
<div ref="wrapper" class="relative" v-click-outside="clickOutside">
<button type="button" class="relative w-full h-full bg-fg border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<button type="button" class="relative w-full h-full bg-fg border border-gray-500 hover:border-gray-400 rounded-sm shadow-xs pl-3 pr-3 py-0 text-left focus:outline-hidden sm:text-sm cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span>
<span class="material-symbols text-lg text-yellow-400" :aria-label="descending ? $strings.LabelSortDescending : $strings.LabelSortAscending">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</button>
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm" role="menu">
<ul v-show="showMenu" class="librarySortMenu absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 ring-1 ring-black/5 overflow-auto focus:outline-hidden text-sm" role="menu">
<template v-for="item in selectItems">
<li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="menuitem" @click="clickedOption(item.value)">
<div class="flex items-center">
@@ -130,6 +130,18 @@ export default {
text: this.$strings.LabelFileModified,
value: 'mtimeMs'
},
{
text: this.$strings.LabelLibrarySortByProgress,
value: 'progress'
},
{
text: this.$strings.LabelLibrarySortByProgressStarted,
value: 'progress.createdAt'
},
{
text: this.$strings.LabelLibrarySortByProgressFinished,
value: 'progress.finishedAt'
},
{
text: this.$strings.LabelRandomly,
value: 'random'
@@ -191,3 +203,9 @@ export default {
}
}
</script>
<style scoped>
.librarySortMenu {
max-height: calc(100vh - 125px);
}
</style>
@@ -9,7 +9,7 @@
</div>
<div class="flex items-center h-9 relative overflow-hidden rounded-lg" style="width: 220px">
<template v-for="rate in rates">
<div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border rounded-sm" :class="value === rate ? 'bg-black-100' : 'hover:bg-black hover:bg-opacity-10'" style="min-width: 44px; max-width: 44px" @click="set(rate)">
<div :key="rate" class="h-full border-black-300 w-11 cursor-pointer border rounded-xs" :class="value === rate ? 'bg-black-100' : 'hover:bg-black/10'" style="min-width: 44px; max-width: 44px" @click="set(rate)">
<div class="w-full h-full flex justify-center items-center">
<p class="text-xs text-center">{{ rate }}<span class="text-sm">x</span></p>
</div>
+3 -3
View File
@@ -1,13 +1,13 @@
<template>
<div ref="wrapper" class="relative" v-click-outside="clickOutside">
<button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded-sm shadow-xs pl-3 pr-3 py-0 text-left focus:outline-hidden cursor-pointer" aria-haspopup="menu" :aria-expanded="showMenu" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span>
<span class="material-symbols text-lg text-yellow-400" :aria-label="descending ? $strings.LabelSortDescending : $strings.LabelSortAscending">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</button>
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm" role="menu">
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 ring-1 ring-black/5 overflow-auto focus:outline-hidden text-sm" role="menu">
<template v-for="item in items">
<li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="menuitem" @click="clickedOption(item.value)">
<div class="flex items-center">
@@ -77,4 +77,4 @@ export default {
}
}
}
</script>
</script>
+2 -2
View File
@@ -4,10 +4,10 @@
<span class="material-symbols text-2xl sm:text-3xl">{{ volumeIcon }}</span>
</button>
<transition name="menux">
<div v-show="isOpen" class="volumeMenu h-28 absolute bottom-2 w-6 py-2 bg-bg shadow-sm rounded-lg" style="top: -116px">
<div v-show="isOpen" class="volumeMenu h-28 absolute bottom-2 w-6 py-2 bg-bg shadow-xs rounded-lg" style="top: -116px">
<div ref="volumeTrack" class="w-1 h-full bg-gray-500 mx-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
<div class="bg-gray-100 w-full absolute left-0 bottom-0 pointer-events-none rounded-full" :style="{ height: volume * trackHeight + 'px' }" />
<div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ bottom: cursorBottom + 'px', left: '-3px' }" />
<div class="w-2.5 h-2.5 bg-white shadow-xs rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ bottom: cursorBottom + 'px', left: '-3px' }" />
</div>
</div>
</transition>
-3
View File
@@ -39,9 +39,6 @@ export default {
}
},
computed: {
userToken() {
return this.$store.getters['user/getToken']
},
_author() {
return this.author || {}
},
+4 -4
View File
@@ -1,7 +1,7 @@
<template>
<div class="relative rounded-sm overflow-hidden" :style="{ height: height + 'px', width: width + 'px', maxWidth: width + 'px', minWidth: width + 'px' }">
<div class="relative rounded-xs overflow-hidden" :style="{ height: height + 'px', width: width + 'px', maxWidth: width + 'px', minWidth: width + 'px' }">
<div class="w-full h-full relative bg-bg">
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-xs bg-primary">
<div class="absolute cover-bg" ref="coverBg" />
</div>
@@ -96,8 +96,8 @@ export default {
return this.author
},
placeholderUrl() {
const config = this.$config || this.$nuxt.$config
return `${config.routerBasePath}/book_placeholder.jpg`
const store = this.$store || this.$nuxt.$store
return store.getters['globals/getPlaceholderCoverSrc']
},
fullCoverUrl() {
if (!this.libraryItem) return null
+10 -10
View File
@@ -1,25 +1,25 @@
<template>
<div class="relative rounded-sm overflow-hidden" :style="{ width: width + 'px', height: height + 'px' }">
<!-- <div class="absolute top-0 left-0 w-full h-full rounded-sm overflow-hidden z-10">
<div class="w-full h-full border border-white border-opacity-10" />
<div class="relative rounded-xs overflow-hidden" :style="{ width: width + 'px', height: height + 'px' }">
<!-- <div class="absolute top-0 left-0 w-full h-full rounded-xs overflow-hidden z-10">
<div class="w-full h-full border border-white/10" />
</div> -->
<div v-if="hasOwnCover" class="w-full h-full relative rounded-sm">
<div v-if="hasOwnCover" class="w-full h-full relative rounded-xs">
<div v-if="showCoverBg" class="bg-primary absolute top-0 left-0 w-full h-full">
<div class="w-full h-full z-0" ref="coverBg" />
</div>
<img ref="cover" :src="fullCoverUrl" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0" :class="showCoverBg ? 'object-contain' : 'object-cover'" />
</div>
<div v-else-if="books.length" class="flex justify-center h-full relative bg-primary bg-opacity-95 rounded-sm">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400 bg-opacity-5" />
<div v-else-if="books.length" class="flex justify-center h-full relative bg-primary/95 rounded-xs">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400/5" />
<covers-book-cover :library-item="books[0]" :width="width / 2" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<covers-book-cover v-if="books.length > 1" :library-item="books[1]" :width="width / 2" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div v-else class="relative w-full h-full flex items-center justify-center p-2 bg-primary rounded-sm">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400 bg-opacity-5" />
<div v-else class="relative w-full h-full flex items-center justify-center p-2 bg-primary rounded-xs">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400/5" />
<p class="text-white text-opacity-60 text-center" :style="{ fontSize: Math.min(1, sizeMultiplier) + 'rem' }">Empty Collection</p>
<p class="text-white/60 text-center" :style="{ fontSize: Math.min(1, sizeMultiplier) + 'rem' }">Empty Collection</p>
</div>
</div>
</template>
@@ -62,4 +62,4 @@ export default {
},
mounted() {}
}
</script>
</script>
+1 -1
View File
@@ -109,7 +109,7 @@ export default {
if (showCoverBg) {
var coverbgwrapper = document.createElement('div')
coverbgwrapper.className = 'absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary'
coverbgwrapper.className = 'absolute top-0 left-0 w-full h-full overflow-hidden rounded-xs bg-primary'
var coverbg = document.createElement('div')
coverbg.className = 'absolute cover-bg'
+6 -6
View File
@@ -1,11 +1,11 @@
<template>
<div class="relative rounded-sm overflow-hidden" :style="{ width: width + 'px', height: height + 'px' }">
<div v-if="items.length" class="flex flex-wrap justify-center h-full relative bg-primary bg-opacity-95 rounded-sm">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400 bg-opacity-5" />
<div class="relative rounded-xs overflow-hidden" :style="{ width: width + 'px', height: height + 'px' }">
<div v-if="items.length" class="flex flex-wrap justify-center h-full relative bg-primary/95 rounded-xs">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400/5" />
<covers-book-cover v-for="(li, index) in libraryItemCovers" :key="index" :library-item="li" :width="itemCoverWidth" :book-cover-aspect-ratio="1" />
</div>
<div v-else class="relative w-full h-full flex items-center justify-center p-2 bg-primary rounded-sm">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400 bg-opacity-5" />
<div v-else class="relative w-full h-full flex items-center justify-center p-2 bg-primary rounded-xs">
<div class="absolute top-0 left-0 w-full h-full bg-gray-400/5" />
</div>
</div>
</template>
@@ -48,4 +48,4 @@ export default {
methods: {},
mounted() {}
}
</script>
</script>
+7 -6
View File
@@ -1,12 +1,12 @@
<template>
<div class="relative rounded-sm" :style="{ height: width * bookCoverAspectRatio + 'px', width: width + 'px', maxWidth: width + 'px', minWidth: width + 'px' }" @mouseover="isHovering = true" @mouseleave="isHovering = false">
<div class="relative rounded-xs" :style="{ height: width * bookCoverAspectRatio + 'px', width: width + 'px', maxWidth: width + 'px', minWidth: width + 'px' }" @mouseover="isHovering = true" @mouseleave="isHovering = false">
<div class="w-full h-full relative overflow-hidden">
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-sm bg-primary">
<div v-show="showCoverBg" class="absolute top-0 left-0 w-full h-full overflow-hidden rounded-xs bg-primary">
<div class="absolute cover-bg" ref="coverBg" />
</div>
<img ref="cover" :src="cover" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0" :class="showCoverBg ? 'object-contain' : 'object-fill'" />
<a v-if="!imageFailed && showOpenNewTab && isHovering" :href="cover" @click.stop target="_blank" class="absolute bg-primary flex items-center justify-center shadow-sm rounded-full hover:scale-110 transform duration-100" :style="{ top: sizeMultiplier * 0.5 + 'rem', right: sizeMultiplier * 0.5 + 'rem', width: 2.5 * sizeMultiplier + 'rem', height: 2.5 * sizeMultiplier + 'rem' }">
<a v-if="!imageFailed && showOpenNewTab && isHovering" :href="cover" @click.stop target="_blank" class="absolute bg-primary flex items-center justify-center shadow-xs rounded-full hover:scale-110 transform duration-100" :style="{ top: sizeMultiplier * 0.5 + 'rem', right: sizeMultiplier * 0.5 + 'rem', width: 2.5 * sizeMultiplier + 'rem', height: 2.5 * sizeMultiplier + 'rem' }">
<span class="material-symbols" :style="{ fontSize: sizeMultiplier * 1.75 + 'rem' }">open_in_new</span>
</a>
</div>
@@ -18,7 +18,7 @@
</div>
</div>
<p v-if="!imageFailed && showResolution" class="absolute -bottom-5 left-0 right-0 mx-auto text-xs text-gray-300 text-center">{{ resolution }}</p>
<p v-if="!imageFailed && showResolution && resolution" class="absolute -bottom-5 left-0 right-0 mx-auto text-xs text-gray-300 text-center">{{ resolution }}</p>
</div>
</template>
@@ -65,11 +65,12 @@ export default {
return 0.8 * this.sizeMultiplier
},
resolution() {
if (!this.naturalWidth || !this.naturalHeight) return null
return `${this.naturalWidth}×${this.naturalHeight}px`
},
placeholderUrl() {
const config = this.$config || this.$nuxt.$config
return `${config.routerBasePath}/book_placeholder.jpg`
const store = this.$store || this.$nuxt.$store
return store.getters['globals/getPlaceholderCoverSrc']
}
},
methods: {
+6 -9
View File
@@ -120,10 +120,10 @@
</div>
<div class="flex pt-4 px-2">
<ui-btn v-if="hasOpenIDLink" small :loading="unlinkingFromOpenID" color="primary" type="button" class="mr-2" @click.stop="unlinkOpenID">{{ $strings.ButtonUnlinkOpenId }}</ui-btn>
<ui-btn v-if="hasOpenIDLink" small :loading="unlinkingFromOpenID" color="bg-primary" type="button" class="mr-2" @click.stop="unlinkOpenID">{{ $strings.ButtonUnlinkOpenId }}</ui-btn>
<ui-btn v-if="isEditingRoot" small class="flex items-center" to="/account">{{ $strings.ButtonChangeRootPassword }}</ui-btn>
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@@ -309,9 +309,9 @@ export default {
} else {
console.log('Account updated', data.user)
if (data.user.id === this.user.id && data.user.token !== this.user.token) {
console.log('Current user token was updated')
this.$store.commit('user/setUserToken', data.user.token)
if (data.user.id === this.user.id && data.user.accessToken !== this.user.accessToken) {
console.log('Current user access token was updated')
this.$store.commit('user/setAccessToken', data.user.accessToken)
}
this.$toast.success(this.$strings.ToastAccountUpdateSuccess)
@@ -351,9 +351,6 @@ export default {
this.$toast.error(errMsg || 'Failed to create account')
})
},
toggleActive() {
this.newUser.isActive = !this.newUser.isActive
},
userTypeUpdated(type) {
this.newUser.permissions = {
download: type !== 'guest',
@@ -10,21 +10,21 @@
<div class="w-full p-8">
<div class="flex mb-2">
<div class="w-3/4 p-1">
<ui-text-input-with-label v-model="newName" :label="$strings.LabelName" />
<ui-text-input-with-label v-model="newName" :label="$strings.LabelName" trim-whitespace />
</div>
<div class="w-1/4 p-1">
<ui-text-input-with-label value="Book" readonly :label="$strings.LabelMediaType" />
</div>
</div>
<div class="w-full mb-2 p-1">
<ui-text-input-with-label v-model="newUrl" label="URL" />
<ui-text-input-with-label v-model="newUrl" label="URL" trim-whitespace />
</div>
<div class="w-full mb-2 p-1">
<ui-text-input-with-label v-model="newAuthHeaderValue" :label="$strings.LabelProviderAuthorizationValue" type="password" />
</div>
<div class="flex px-1 pt-4">
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonAdd }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonAdd }}</ui-btn>
</div>
</div>
</div>
@@ -65,7 +65,11 @@ export default {
}
},
methods: {
submitForm() {
async submitForm() {
// Remove focus from active input
document.activeElement?.blur?.()
await this.$nextTick()
if (!this.newName || !this.newUrl) {
this.$toast.error(this.$strings.ToastProviderNameAndUrlRequired)
return
@@ -0,0 +1,60 @@
<template>
<modals-modal ref="modal" v-model="show" name="api-key-created" :width="800" :height="'unset'" persistent>
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="text-3xl text-white truncate">{{ title }}</p>
</div>
</template>
<form @submit.prevent="submitForm">
<div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 overflow-y-auto overflow-x-hidden" style="min-height: 200px; max-height: 80vh">
<div class="w-full p-8">
<p class="text-lg text-white mb-4">{{ $getString('LabelApiKeyCreated', [apiKeyName]) }}</p>
<p class="text-lg text-white mb-4">{{ $strings.LabelApiKeyCreatedDescription }}</p>
<ui-text-input label="API Key" :value="apiKeyKey" readonly show-copy />
<div class="flex justify-end mt-4">
<ui-btn color="bg-primary" @click="show = false">{{ $strings.ButtonClose }}</ui-btn>
</div>
</div>
</div>
</form>
</modals-modal>
</template>
<script>
export default {
props: {
value: Boolean,
apiKey: {
type: Object,
default: () => null
}
},
data() {
return {}
},
computed: {
show: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
title() {
return this.$strings.HeaderNewApiKey
},
apiKeyName() {
return this.apiKey?.name || ''
},
apiKeyKey() {
return this.apiKey?.apiKey || ''
}
},
methods: {},
mounted() {}
}
</script>
+198
View File
@@ -0,0 +1,198 @@
<template>
<modals-modal ref="modal" v-model="show" name="api-key" :width="800" :height="'unset'" :processing="processing">
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="text-3xl text-white truncate">{{ title }}</p>
</div>
</template>
<form @submit.prevent="submitForm">
<div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 overflow-y-auto overflow-x-hidden" style="min-height: 400px; max-height: 80vh">
<div class="w-full p-8">
<div class="flex py-2">
<div class="w-1/2 px-2">
<ui-text-input-with-label v-model.trim="newApiKey.name" :readonly="!isNew" :label="$strings.LabelName" />
</div>
<div v-if="isNew" class="w-1/2 px-2">
<ui-text-input-with-label v-model.trim="newApiKey.expiresIn" :label="$strings.LabelExpiresInSeconds" type="number" :min="0" />
</div>
</div>
<div class="flex items-center pt-4 pb-2 gap-2">
<div class="flex items-center px-2">
<p class="px-3 font-semibold" id="user-enabled-toggle">{{ $strings.LabelEnable }}</p>
<ui-toggle-switch :disabled="isExpired && !apiKey.isActive" labeledBy="user-enabled-toggle" v-model="newApiKey.isActive" />
</div>
<div v-if="isExpired" class="px-2">
<p class="text-sm text-error">{{ $strings.LabelExpired }}</p>
</div>
</div>
<div class="w-full border-t border-b border-black-200 py-4 px-3 mt-4">
<p class="text-lg mb-2 font-semibold">{{ $strings.LabelApiKeyUser }}</p>
<p class="text-sm mb-2 text-gray-400">{{ $strings.LabelApiKeyUserDescription }}</p>
<ui-select-input v-model="newApiKey.userId" :disabled="isExpired && !apiKey.isActive" :items="userItems" :placeholder="$strings.LabelSelectUser" :label="$strings.LabelApiKeyUser" label-hidden />
</div>
<div class="flex pt-4 px-2">
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
</form>
</modals-modal>
</template>
<script>
export default {
props: {
value: Boolean,
apiKey: {
type: Object,
default: () => null
},
users: {
type: Array,
default: () => []
}
},
data() {
return {
processing: false,
newApiKey: {},
isNew: true
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.init()
}
}
}
},
computed: {
show: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
title() {
return this.isNew ? this.$strings.HeaderNewApiKey : this.$strings.HeaderUpdateApiKey
},
userItems() {
return this.users
.filter((u) => {
// Only show root user if the current user is root
return u.type !== 'root' || this.$store.getters['user/getIsRoot']
})
.map((u) => ({ text: u.username, value: u.id, subtext: u.type }))
},
isExpired() {
if (!this.apiKey || !this.apiKey.expiresAt) return false
return new Date(this.apiKey.expiresAt).getTime() < Date.now()
}
},
methods: {
submitForm() {
if (!this.newApiKey.name) {
this.$toast.error(this.$strings.ToastNameRequired)
return
}
if (!this.newApiKey.userId) {
this.$toast.error(this.$strings.ToastNewApiKeyUserError)
return
}
if (this.isNew) {
this.submitCreateApiKey()
} else {
this.submitUpdateApiKey()
}
},
submitUpdateApiKey() {
if (this.newApiKey.isActive === this.apiKey.isActive && this.newApiKey.userId === this.apiKey.userId) {
this.$toast.info(this.$strings.ToastNoUpdatesNecessary)
this.show = false
return
}
const apiKey = {
isActive: this.newApiKey.isActive,
userId: this.newApiKey.userId
}
this.processing = true
this.$axios
.$patch(`/api/api-keys/${this.apiKey.id}`, apiKey)
.then((data) => {
this.processing = false
if (data.error) {
this.$toast.error(`${this.$strings.ToastFailedToUpdate}: ${data.error}`)
} else {
this.show = false
this.$emit('updated', data.apiKey)
}
})
.catch((error) => {
this.processing = false
console.error('Failed to update apiKey', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || this.$strings.ToastFailedToUpdate)
})
},
submitCreateApiKey() {
const apiKey = { ...this.newApiKey }
if (this.newApiKey.expiresIn) {
apiKey.expiresIn = parseInt(this.newApiKey.expiresIn)
} else {
delete apiKey.expiresIn
}
this.processing = true
this.$axios
.$post('/api/api-keys', apiKey)
.then((data) => {
this.processing = false
if (data.error) {
this.$toast.error(this.$strings.ToastFailedToCreate + ': ' + data.error)
} else {
this.show = false
this.$emit('created', data.apiKey)
}
})
.catch((error) => {
this.processing = false
console.error('Failed to create apiKey', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || this.$strings.ToastFailedToCreate)
})
},
init() {
this.isNew = !this.apiKey
if (this.apiKey) {
this.newApiKey = {
name: this.apiKey.name,
isActive: this.apiKey.isActive,
userId: this.apiKey.userId
}
} else {
this.newApiKey = {
name: null,
expiresIn: null,
isActive: true,
userId: null
}
}
}
},
mounted() {}
}
</script>
@@ -7,7 +7,7 @@
<ui-btn v-else-if="userIsAdminOrUp" small :loading="probingFile" class="ml-2" @click="getFFProbeData">{{ $strings.ButtonProbeAudioFile }}</ui-btn>
</div>
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
<div class="w-full h-px bg-white/10 my-4" />
<template v-if="!ffprobeData">
<ui-text-input-with-label :value="metadata.path" readonly :label="$strings.LabelPath" class="mb-4 text-sm" />
@@ -75,7 +75,7 @@
</div>
</div>
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
<div class="w-full h-px bg-white/10 my-4" />
<p class="font-bold mb-2">{{ $strings.LabelMetaTags }}</p>
@@ -6,7 +6,7 @@
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full py-4">
<div class="w-full overflow-y-auto overflow-x-hidden max-h-96">
<div class="flex px-8 items-center py-2">
@@ -32,11 +32,11 @@
</p>
</ui-tooltip>
</div>
<div class="mt-4 pt-4 text-white text-opacity-80 border-t border-white border-opacity-5">
<div class="mt-4 pt-4 text-white/80 border-t border-white/5">
<div class="flex items-center px-4">
<ui-btn type="button" @click="show = false">{{ $strings.ButtonCancel }}</ui-btn>
<div class="flex-grow" />
<ui-btn color="success" @click="doBatchQuickMatch">{{ $strings.ButtonSubmit }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" @click="doBatchQuickMatch">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@@ -88,7 +88,7 @@ export default {
},
providers() {
if (this.isPodcast) return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
return this.$store.state.scanners.bookProviders
},
libraryProvider() {
return this.$store.getters['libraries/getLibraryProvider'](this.currentLibraryId) || 'google'
@@ -96,6 +96,9 @@ export default {
},
methods: {
init() {
// Fetch providers when modal is shown
this.$store.dispatch('scanners/fetchProviders')
// If we don't have a set provider (first open of dialog) or we've switched library, set
// the selected provider to the current library default provider
if (!this.options.provider || this.lastUsedLibrary != this.currentLibraryId) {
@@ -127,8 +130,7 @@ export default {
this.show = false
})
}
},
mounted() {}
}
}
</script>
+5 -5
View File
@@ -17,16 +17,16 @@
<div v-if="canCreateBookmark && !hideCreate" class="w-full border-t border-white/10">
<form @submit.prevent="submitCreateBookmark">
<div class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
<div class="flex px-4 py-2 items-center text-center border-b border-white/10 text-white/80">
<div class="w-16 max-w-16 text-center">
<p class="text-sm font-mono text-gray-400">
{{ this.$secondsToTimestamp(currentTime / playbackRate) }}
</p>
</div>
<div class="flex-grow px-2">
<div class="grow px-2">
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full h-10" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">add</span></ui-btn>
<ui-btn type="submit" color="bg-success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">add</span></ui-btn>
</div>
</form>
</div>
@@ -79,10 +79,10 @@ export default {
return !this.bookmarks.find((bm) => Math.abs(this.currentTime - bm.time) < 1)
},
dateFormat() {
return this.$store.state.serverSettings.dateFormat
return this.$store.getters['getServerSetting']('dateFormat')
},
timeFormat() {
return this.$store.state.serverSettings.timeFormat
return this.$store.getters['getServerSetting']('timeFormat')
}
},
methods: {
+1 -1
View File
@@ -7,7 +7,7 @@
{{ chap.title }}
</p>
<span class="font-mono text-xxs sm:text-xs text-gray-400 pl-2 whitespace-nowrap">{{ $elapsedPrettyExtended((chap.end - chap.start) / _playbackRate) }}</span>
<span class="flex-grow" />
<span class="grow" />
<span class="font-mono text-xs sm:text-sm text-gray-300">{{ $secondsToTimestamp(chap.start / _playbackRate) }}</span>
<div v-show="chap.id === currentChapterId" class="w-0.5 h-full absolute top-0 left-0 bg-yellow-400" />
+3 -3
View File
@@ -7,12 +7,12 @@
</template>
<div class="w-full h-full overflow-hidden absolute top-0 left-0 flex items-center justify-center" @click="show = false">
<div ref="container" class="w-full overflow-x-hidden overflow-y-auto bg-primary rounded-lg border border-white border-opacity-20" style="max-height: 75%" @click.stop>
<div ref="container" class="w-full overflow-x-hidden overflow-y-auto bg-primary rounded-lg border border-white/20" style="max-height: 75%" @click.stop>
<ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
<template v-for="item in items">
<li :key="item.value" class="text-gray-50 select-none relative py-4 cursor-pointer hover:bg-black-400" :class="selected === item.value ? 'bg-success bg-opacity-10' : ''" role="option" @click="clickedOption(item.value)">
<li :key="item.value" class="text-gray-50 select-none relative py-4 cursor-pointer hover:bg-black-400" :class="selected === item.value ? 'bg-success/10' : ''" role="option" @click="clickedOption(item.value)">
<div class="relative flex items-center px-3">
<p class="font-normal block truncate text-base text-white text-opacity-80">{{ item.text }}</p>
<p class="font-normal block truncate text-base text-white/80">{{ item.text }}</p>
</div>
</li>
</template>
@@ -1,5 +1,5 @@
<template>
<div ref="wrapper" role="dialog" aria-modal="true" class="hidden absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 rounded-lg items-center justify-center" style="z-index: 61" @click="clickClose">
<div ref="wrapper" role="dialog" aria-modal="true" class="hidden absolute top-0 left-0 w-full h-full bg-black/50 rounded-lg items-center justify-center" style="z-index: 61" @click="clickClose">
<button type="button" class="absolute top-3 right-3 md:top-5 md:right-5 h-8 w-8 md:h-12 md:w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300" aria-label="Close modal">
<span class="material-symbols text-2xl md:text-4xl">close</span>
</button>
@@ -7,13 +7,14 @@
<form v-if="selectedSeries" @submit.prevent="submitSeriesForm">
<div class="bg-bg rounded-lg px-2 py-6 sm:p-6 md:p-8" @click.stop>
<div class="flex">
<div class="flex-grow p-1 min-w-48 sm:min-w-64 md:min-w-80">
<div class="grow p-1 min-w-48 sm:min-w-64 md:min-w-80">
<ui-input-dropdown ref="newSeriesSelect" v-model="selectedSeries.name" :items="existingSeriesNames" :disabled="!isNewSeries" :label="$strings.LabelSeriesName" @input="seriesNameInputHandler" />
</div>
<div class="w-24 sm:w-28 md:w-40 p-1">
<ui-text-input-with-label ref="sequenceInput" v-model="selectedSeries.sequence" :label="$strings.LabelSequence" />
</div>
</div>
<div v-if="error" class="text-error text-sm mt-2 p-1">{{ error }}</div>
<div class="flex justify-end mt-2 p-1">
<ui-btn type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
@@ -34,12 +35,17 @@ export default {
existingSeriesNames: {
type: Array,
default: () => []
},
originalSeriesSequence: {
type: String,
default: null
}
},
data() {
return {
el: null,
content: null
content: null,
error: null
}
},
watch: {
@@ -85,10 +91,17 @@ export default {
}
},
submitSeriesForm() {
this.error = null
if (this.$refs.newSeriesSelect) {
this.$refs.newSeriesSelect.blur()
}
if (this.selectedSeries.sequence !== this.originalSeriesSequence && this.selectedSeries.sequence.includes(' ')) {
this.error = this.$strings.MessageSeriesSequenceCannotContainSpaces
return
}
this.$emit('submit')
},
clickClose() {
@@ -100,6 +113,7 @@ export default {
}
},
setShow() {
this.error = null
if (!this.el || !this.content) {
this.init()
}
@@ -11,7 +11,7 @@
<p v-if="_session.displayAuthor" class="text-xs text-gray-400 px-4">{{ $getString('LabelByAuthor', [_session.displayAuthor]) }}</p>
</div>
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
<div class="w-full h-px bg-white/10 my-4" />
<div class="flex flex-wrap mb-4">
<div class="w-full md:w-2/3">
@@ -81,7 +81,7 @@
</div>
<div class="w-full md:w-1/3">
<p v-if="!isMediaItemShareSession" class="font-semibold uppercase text-xs text-gray-400 tracking-wide mb-2 mt-6 md:mt-0">{{ $strings.LabelUser }}</p>
<p v-if="!isMediaItemShareSession" class="mb-1 text-xs">{{ _session.userId }}</p>
<p v-if="!isMediaItemShareSession" class="mb-1 text-xs">{{ username }}</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">{{ $strings.LabelMediaPlayer }}</p>
<p class="mb-1">{{ playMethodName }}</p>
@@ -99,8 +99,8 @@
</div>
<div class="flex items-center">
<ui-btn v-if="!isOpenSession && !isMediaItemShareSession" small color="error" @click.stop="deleteSessionClick">{{ $strings.ButtonDelete }}</ui-btn>
<ui-btn v-else-if="!isMediaItemShareSession" small color="error" @click.stop="closeSessionClick">{{ $strings.ButtonCloseSession }}</ui-btn>
<ui-btn v-if="!isOpenSession && !isMediaItemShareSession" small color="bg-error" @click.stop="deleteSessionClick">{{ $strings.ButtonDelete }}</ui-btn>
<ui-btn v-else-if="!isMediaItemShareSession" small color="bg-error" @click.stop="closeSessionClick">{{ $strings.ButtonCloseSession }}</ui-btn>
</div>
</div>
</modals-modal>
@@ -132,6 +132,9 @@ export default {
_session() {
return this.session || {}
},
username() {
return this._session.user?.username || this._session.userId || ''
},
deviceInfo() {
return this._session.deviceInfo || {}
},
@@ -159,10 +162,10 @@ export default {
return 'Unknown'
},
dateFormat() {
return this.$store.state.serverSettings.dateFormat
return this.$store.getters['getServerSetting']('dateFormat')
},
timeFormat() {
return this.$store.state.serverSettings.timeFormat
return this.$store.getters['getServerSetting']('timeFormat')
},
isOpenSession() {
return !!this._session.open
+7 -7
View File
@@ -1,14 +1,14 @@
<template>
<div ref="wrapper" role="dialog" aria-modal="true" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary items-center justify-center opacity-0 hidden" :class="`z-${zIndex} bg-opacity-${bgOpacity}`">
<div class="absolute top-0 left-0 right-0 w-full h-36 bg-gradient-to-t from-transparent via-black-500 to-black-700 opacity-90 pointer-events-none" />
<div ref="wrapper" role="dialog" aria-modal="true" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary items-center justify-center opacity-0 hidden" :class="`z-${zIndex} bg-primary/${bgOpacity}`">
<div class="absolute top-0 left-0 right-0 w-full h-36 bg-linear-to-t from-transparent via-black-500 to-black-700 opacity-90 pointer-events-none" />
<button class="absolute top-4 right-4 landscape:top-4 landscape:right-4 md:portrait:top-5 md:portrait:right-5 lg:top-5 lg:right-5 inline-flex text-gray-200 hover:text-white" aria-label="Close modal" @click="clickClose">
<span class="material-symbols text-2xl landscape:text-2xl md:portrait:text-4xl lg:text-4xl">close</span>
</button>
<slot name="outer" />
<div ref="content" tabindex="0" style="min-width: 380px; min-height: 200px; max-width: 100vw" class="relative text-white outline-none" :style="{ height: modalHeight, width: modalWidth, marginTop: contentMarginTop + 'px' }" @mousedown="mousedownModal" @mouseup="mouseupModal" v-click-outside="clickBg">
<div ref="content" tabindex="0" style="min-width: 380px; min-height: 200px; max-width: 100vw" class="relative text-white outline-hidden" :style="{ height: modalHeight, width: modalWidth, marginTop: contentMarginTop + 'px' }" @mousedown="mousedownModal" @mouseup="mouseupModal" v-click-outside="clickBg">
<slot />
<div v-if="processing" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-black bg-opacity-60 rounded-lg flex items-center justify-center">
<div v-if="processing" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-black/60 rounded-lg flex items-center justify-center">
<ui-loading-indicator />
</div>
</div>
@@ -23,7 +23,7 @@ export default {
processing: Boolean,
persistent: {
type: Boolean,
default: true
default: false
},
width: {
type: [String, Number],
@@ -99,7 +99,7 @@ export default {
this.preventClickoutside = false
return
}
if (this.processing && this.persistent) return
if (this.processing || this.persistent) return
if (ev.srcElement && ev.srcElement.classList.contains('modal-bg')) {
this.show = false
}
@@ -163,4 +163,4 @@ export default {
this.$eventBus.$off('showing-prompt', this.showingPrompt)
}
}
</script>
</script>
+5 -5
View File
@@ -30,7 +30,7 @@
<label class="px-1 text-sm font-semibold block">{{ $strings.LabelSlug }}</label>
<ui-text-input v-model="newShareSlug" class="text-base h-10" />
</div>
<div class="flex-grow" />
<div class="grow" />
<div class="w-full sm:w-80">
<label class="px-1 text-sm font-semibold block">{{ $strings.LabelDuration }}</label>
<div class="inline-flex items-center space-x-2">
@@ -60,9 +60,9 @@
<p class="text-sm text-gray-300 py-1 px-1" v-html="$getString('MessageShareExpirationWillBe', [expirationDateString])" />
</template>
<div class="flex items-center pt-6">
<div class="flex-grow" />
<ui-btn v-if="currentShare" color="error" small @click="deleteShare">{{ $strings.ButtonDelete }}</ui-btn>
<ui-btn v-if="!currentShare" color="success" small @click="openShare">{{ $strings.ButtonShare }}</ui-btn>
<div class="grow" />
<ui-btn v-if="currentShare" color="bg-error" small @click="deleteShare">{{ $strings.ButtonDelete }}</ui-btn>
<ui-btn v-if="!currentShare" color="bg-success" small @click="openShare">{{ $strings.ButtonShare }}</ui-btn>
</div>
</div>
</modals-modal>
@@ -144,7 +144,7 @@ export default {
expirationDateString() {
if (!this.expireDurationSeconds) return this.$strings.LabelPermanent
const dateMs = Date.now() + this.expireDurationSeconds * 1000
return this.$formatDatetime(dateMs, this.$store.state.serverSettings.dateFormat, this.$store.state.serverSettings.timeFormat)
return this.$formatDatetime(dateMs, this.$store.getters['getServerSetting']('dateFormat'), this.$store.getters['getServerSetting']('timeFormat'))
}
},
methods: {
+1 -1
View File
@@ -15,7 +15,7 @@
</template>
<form class="flex items-center justify-center px-6 py-3" @submit.prevent="submitCustomTime">
<ui-text-input v-model="customTime" type="number" step="any" min="0.1" :placeholder="$strings.LabelTimeInMinutes" class="w-48" />
<ui-btn color="success" type="submit" :padding-x="0" class="h-9 w-18 flex items-center justify-center ml-1">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn color="bg-success" type="submit" :padding-x="0" class="h-9 w-18 flex items-center justify-center ml-1">{{ $strings.ButtonSubmit }}</ui-btn>
</form>
</div>
<div v-if="timerSet" class="w-full p-4">
@@ -5,9 +5,9 @@
<div class="w-40 pr-2 pt-4" style="min-width: 160px">
<ui-file-input ref="fileInput" @change="fileUploadSelected">Upload Cover</ui-file-input>
</div>
<form @submit.prevent="submitForm" class="flex flex-grow">
<form @submit.prevent="submitForm" class="flex grow">
<ui-text-input-with-label v-model="imageUrl" label="Cover Image URL" />
<ui-btn color="success" type="submit" :padding-x="4" class="mt-5 ml-3 w-24">Update</ui-btn>
<ui-btn color="bg-success" type="submit" :padding-x="4" class="mt-5 ml-3 w-24">Update</ui-btn>
</form>
</div>
<div v-if="previewUpload" class="absolute top-0 left-0 w-full h-full z-10 bg-bg p-8">
@@ -18,7 +18,7 @@
</div>
<div class="absolute bottom-0 right-0 flex py-4 px-5">
<ui-btn :disabled="processingUpload" class="mx-2" @click="resetCoverPreview">Clear</ui-btn>
<ui-btn :loading="processingUpload" color="success" @click="submitCoverUpload">Upload</ui-btn>
<ui-btn :loading="processingUpload" color="bg-success" @click="submitCoverUpload">Upload</ui-btn>
</div>
</div>
</div>
@@ -15,10 +15,10 @@
</div>
</div>
</div>
<div class="flex-grow">
<form @submit.prevent="submitUploadCover" class="flex flex-grow mb-2 p-2">
<div class="grow">
<form @submit.prevent="submitUploadCover" class="flex grow mb-2 p-2">
<ui-text-input v-model="imageUrl" :placeholder="$strings.LabelImageURLFromTheWeb" class="h-9 w-full" />
<ui-btn color="success" type="submit" :padding-x="4" :disabled="!imageUrl" class="ml-2 sm:ml-3 w-24 h-9">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn color="bg-success" type="submit" :padding-x="4" :disabled="!imageUrl" class="ml-2 sm:ml-3 w-24 h-9">{{ $strings.ButtonSubmit }}</ui-btn>
</form>
<form v-if="author" @submit.prevent="submitForm">
@@ -26,7 +26,7 @@
<div class="w-3/4 p-2">
<ui-text-input-with-label v-model="authorCopy.name" :disabled="processing" :label="$strings.LabelName" />
</div>
<div class="flex-grow p-2">
<div class="grow p-2">
<ui-text-input-with-label v-model="authorCopy.asin" :disabled="processing" label="ASIN" />
</div>
</div>
@@ -35,8 +35,8 @@
</div>
<div class="flex pt-2 px-2">
<ui-btn v-if="userCanDelete" small color="error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="flex-grow" />
<ui-btn v-if="userCanDelete" small color="bg-error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="grow" />
<ui-btn type="button" class="mx-2" @click="searchAuthor">{{ $strings.ButtonQuickMatch }}</ui-btn>
<ui-btn type="submit">{{ $strings.ButtonSave }}</ui-btn>
@@ -5,16 +5,16 @@
{{ this.$secondsToTimestamp(bookmark.time / playbackRate) }}
</p>
</div>
<div class="flex-grow overflow-hidden px-2">
<div class="grow overflow-hidden px-2">
<template v-if="isEditing">
<form @submit.prevent="submitUpdate">
<div class="flex items-center">
<div class="flex-grow pr-2">
<div class="grow pr-2">
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full h-10" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">forward</span></ui-btn>
<ui-btn type="submit" color="bg-success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">forward</span></ui-btn>
<div class="pl-2 flex items-center">
<span class="material-symbols text-3xl text-white text-opacity-70 hover:text-opacity-95 cursor-pointer" @click.stop.prevent="cancelEditing">close</span>
<span class="material-symbols text-3xl text-white/70 hover:text-white/95 cursor-pointer" @click.stop.prevent="cancelEditing">close</span>
</div>
</div>
</form>
@@ -48,7 +48,7 @@ export default {
computed: {
wrapperClass() {
var classes = []
if (this.highlight) classes.push('bg-bg bg-opacity-60')
if (this.highlight) classes.push('bg-bg/60')
if (!this.isEditing) classes.push('cursor-pointer')
return classes.join(' ')
}
@@ -99,4 +99,4 @@ export default {
}
}
}
</script>
</script>
@@ -40,7 +40,7 @@ export default {
}
},
dateFormat() {
return this.$store.state.serverSettings.dateFormat
return this.$store.getters['getServerSetting']('dateFormat')
},
releasesToShow() {
return this.versionData?.releasesToShow || []
@@ -62,6 +62,8 @@ since we don't have access to the actual elements in this component
2. v-deep allows these to take effect on the content passed in to the v-html in the div above
*/
@reference "tailwindcss";
.custom-text ::v-deep > h2 {
@apply text-lg font-bold;
}
@@ -6,7 +6,7 @@
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full">
<div class="py-4 px-4">
<h1 v-if="!showBatchCollectionModal" class="text-2xl">{{ $strings.LabelAddToCollection }}</h1>
@@ -33,13 +33,13 @@
</div>
</div>
<div class="w-full h-px bg-white bg-opacity-10" />
<div class="w-full h-px bg-white/10" />
<form @submit.prevent="submitCreateCollection">
<div class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
<div class="flex-grow px-2">
<div class="flex px-4 py-2 items-center text-center border-b border-white/10 text-white/80">
<div class="grow px-2">
<ui-text-input v-model="newCollectionName" :placeholder="$strings.PlaceholderNewCollection" class="w-full" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
<ui-btn type="submit" color="bg-success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
</div>
</form>
</div>
@@ -227,7 +227,7 @@ export default {
.catch((error) => {
console.error('Failed to create collection', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(this.$strings.ToastCollectionCreateFailed + ': ' + errMsg)
this.$toast.error(errMsg)
this.processing = false
})
}
@@ -1,15 +1,15 @@
<template>
<div class="flex items-center px-4 py-2 justify-start relative hover:bg-bg" @mouseover="mouseover" @mouseleave="mouseleave">
<div class="flex items-center px-4 py-2 justify-start relative hover:bg-black-400" @mouseover="mouseover" @mouseleave="mouseleave">
<div v-if="isBookIncluded" class="absolute top-0 left-0 h-full w-1 bg-success z-10" />
<div class="w-20 max-w-20 text-center">
<covers-collection-cover :book-items="books" :width="80" :height="40 * bookCoverAspectRatio" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div class="flex-grow overflow-hidden px-2">
<div class="grow overflow-hidden px-2">
<nuxt-link :to="`/collection/${collection.id}`" class="pl-2 pr-2 truncate hover:underline cursor-pointer" @click.native="clickNuxtLink">{{ collection.name }}</nuxt-link>
</div>
<div class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'">
<ui-btn v-if="!isBookIncluded" color="success" :padding-x="3" small class="h-9" @click.stop="clickAdd"><span class="material-symbols text-2xl pt-px">add</span></ui-btn>
<ui-btn v-else color="error" :padding-x="3" class="h-9" small @click.stop="clickRem"><span class="material-symbols text-2xl pt-px">remove</span></ui-btn>
<ui-btn v-if="!isBookIncluded" color="bg-success" :padding-x="3" small class="h-9" @click.stop="clickAdd"><span class="material-symbols text-2xl pt-px">add</span></ui-btn>
<ui-btn v-else color="bg-error" :padding-x="3" class="h-9" small @click.stop="clickRem"><span class="material-symbols text-2xl pt-px">remove</span></ui-btn>
</div>
</div>
</template>
@@ -55,4 +55,4 @@ export default {
},
mounted() {}
}
</script>
</script>
@@ -12,32 +12,32 @@
<div class="w-full flex justify-center mb-2 md:w-auto md:mb-0 md:block">
<covers-collection-cover :book-items="books" :width="200" :height="100 * bookCoverAspectRatio" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div class="flex-grow px-4">
<div class="grow px-4">
<ui-text-input-with-label v-model="newCollectionName" :label="$strings.LabelName" class="mb-2" />
<ui-textarea-with-label v-model="newCollectionDescription" :label="$strings.LabelDescription" />
</div>
</div>
<div class="absolute bottom-0 left-0 right-0 w-full py-2 px-4 flex">
<ui-btn v-if="userCanDelete" small color="error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSave }}</ui-btn>
<div class="absolute bottom-0 left-0 right-0 w-full py-4 px-4 flex">
<ui-btn v-if="userCanDelete" small color="bg-error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSave }}</ui-btn>
</div>
</form>
</template>
<template v-else>
<div class="flex items-center mb-3">
<div class="hover:bg-white hover:bg-opacity-10 cursor-pointer h-11 w-11 flex items-center justify-center rounded-full" @click="showImageUploader = false">
<div class="hover:bg-white/10 cursor-pointer h-11 w-11 flex items-center justify-center rounded-full" @click="showImageUploader = false">
<span class="material-symbols text-4xl">arrow_back</span>
</div>
<p class="ml-2 text-xl mb-1">Collection Cover Image</p>
</div>
<div class="flex mb-4">
<ui-btn small class="mr-2">Upload</ui-btn>
<ui-text-input v-model="newCoverImage" class="flex-grow" placeholder="Collection Cover Image" />
<ui-text-input v-model="newCoverImage" class="grow" placeholder="Collection Cover Image" />
</div>
<div class="flex justify-end">
<ui-btn color="success">Upload</ui-btn>
<ui-btn color="bg-success">Upload</ui-btn>
</div>
</template>
</div>
@@ -94,21 +94,32 @@ export default {
this.newCollectionDescription = this.collection.description || ''
},
removeClick() {
if (confirm(this.$getString('MessageConfirmRemoveCollection', [this.collectionName]))) {
this.processing = true
this.$axios
.$delete(`/api/collections/${this.collection.id}`)
.then(() => {
this.processing = false
this.show = false
this.$toast.success(this.$strings.ToastCollectionRemoveSuccess)
})
.catch((error) => {
console.error('Failed to remove collection', error)
this.processing = false
this.$toast.error(this.$strings.ToastRemoveFailed)
})
const payload = {
message: this.$getString('MessageConfirmRemoveCollection', [this.collectionName]),
callback: (confirmed) => {
if (confirmed) {
this.deleteCollection()
}
},
type: 'yesNo'
}
this.$store.commit('globals/setConfirmPrompt', payload)
},
deleteCollection() {
this.processing = true
this.$axios
.$delete(`/api/collections/${this.collection.id}`)
.then(() => {
this.show = false
this.$toast.success(this.$strings.ToastCollectionRemoveSuccess)
})
.catch((error) => {
console.error('Failed to remove collection', error)
this.$toast.error(this.$strings.ToastRemoveFailed)
})
.finally(() => {
this.processing = false
})
},
submitForm() {
if (this.newCollectionName === this.collectionName && this.newCollectionDescription === this.collection.description) {
@@ -26,8 +26,8 @@
</div>
<div class="flex items-center pt-4">
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@@ -18,8 +18,8 @@
</div>
<div class="flex items-center pt-4">
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
+9 -3
View File
@@ -16,10 +16,10 @@
</div>
<div v-show="canGoPrev" class="absolute -left-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6">
<button class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonNext" @click.stop.prevent="goPrevBook" @mousedown.prevent>arrow_back_ios</button>
<button class="material-symbols text-5xl text-white/50 hover:text-white/90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonNext" @click.stop.prevent="goPrevBook" @mousedown.prevent>arrow_back_ios</button>
</div>
<div v-show="canGoNext" class="absolute -right-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6">
<button class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonPrevious" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</button>
<button class="material-symbols text-5xl text-white/50 hover:text-white/90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonPrevious" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</button>
</div>
</modals-modal>
</template>
@@ -196,6 +196,9 @@ export default {
methods: {
async goPrevBook() {
if (this.currentBookshelfIndex - 1 < 0) return
// Remove focus from active input
document.activeElement?.blur?.()
var prevBookId = this.bookshelfBookIds[this.currentBookshelfIndex - 1]
this.processing = true
var prevBook = await this.$axios.$get(`/api/items/${prevBookId}?expanded=1`).catch((error) => {
@@ -215,6 +218,9 @@ export default {
},
async goNextBook() {
if (this.currentBookshelfIndex >= this.bookshelfBookIds.length - 1) return
// Remove focus from active input
document.activeElement?.blur?.()
this.processing = true
var nextBookId = this.bookshelfBookIds[this.currentBookshelfIndex + 1]
var nextBook = await this.$axios.$get(`/api/items/${nextBookId}?expanded=1`).catch((error) => {
@@ -300,4 +306,4 @@ export default {
.tab.tab-selected {
height: 41px;
}
</style>
</style>
+169 -35
View File
@@ -1,42 +1,42 @@
<template>
<div class="w-full h-full overflow-hidden overflow-y-auto px-2 sm:px-4 py-6 relative">
<div class="flex flex-col sm:flex-row mb-4">
<div class="relative self-center">
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId, libraryItemUpdatedAt, true)" :width="120" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="relative self-center md:self-start">
<covers-preview-cover :src="coverUrl" :width="120" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<!-- book cover overlay -->
<div v-if="media.coverPath" class="absolute top-0 left-0 w-full h-full z-10 opacity-0 hover:opacity-100 transition-opacity duration-100">
<div class="absolute top-0 left-0 w-full h-16 bg-gradient-to-b from-black-600 to-transparent" />
<div v-if="userCanDelete" class="p-1 absolute top-1 right-1 text-red-500 rounded-full w-8 h-8 cursor-pointer hover:text-red-400 shadow-sm" @click="removeCover">
<div class="absolute top-0 left-0 w-full h-16 bg-linear-to-b from-black-600 to-transparent" />
<div v-if="userCanDelete" class="p-1 absolute top-1 right-1 text-red-500 rounded-full w-8 h-8 cursor-pointer hover:text-red-400 shadow-xs" @click="removeCover">
<ui-tooltip direction="top" :text="$strings.LabelRemoveCover">
<span class="material-symbols text-2xl">delete</span>
</ui-tooltip>
</div>
</div>
</div>
<div class="flex-grow sm:pl-2 md:pl-6 sm:pr-2 mt-6 md:mt-0">
<div class="grow sm:pl-2 md:pl-6 sm:pr-2 mt-6 md:mt-0">
<div class="flex items-center">
<div v-if="userCanUpload" class="w-10 md:w-40 pr-2 md:min-w-32">
<ui-file-input ref="fileInput" @change="fileUploadSelected">
<span class="hidden md:inline-block">{{ $strings.ButtonUploadCover }}</span>
<span class="material-symbols text-2xl inline-block md:!hidden">upload</span>
<span class="material-symbols text-2xl inline-block md:hidden!">upload</span>
</ui-file-input>
</div>
<form @submit.prevent="submitForm" class="flex flex-grow">
<form @submit.prevent="submitForm" class="flex grow">
<ui-text-input v-model="imageUrl" :placeholder="$strings.LabelImageURLFromTheWeb" class="h-9 w-full" />
<ui-btn color="success" type="submit" :padding-x="4" :disabled="!imageUrl" class="ml-2 sm:ml-3 w-24 h-9">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn color="bg-success" type="submit" :padding-x="4" :disabled="!imageUrl" class="ml-2 sm:ml-3 w-24 h-9">{{ $strings.ButtonSubmit }}</ui-btn>
</form>
</div>
<div v-if="localCovers.length" class="mb-4 mt-6 border-t border-b border-white border-opacity-10">
<div v-if="localCovers.length" class="mb-4 mt-6 border-t border-b border-white/10">
<div class="flex items-center justify-center py-2">
<p>{{ localCovers.length }} local image{{ localCovers.length !== 1 ? 's' : '' }}</p>
<div class="flex-grow" />
<div class="grow" />
<ui-btn small @click="showLocalCovers = !showLocalCovers">{{ showLocalCovers ? $strings.ButtonHide : $strings.ButtonShow }}</ui-btn>
</div>
<div v-if="showLocalCovers" class="flex items-center justify-center pb-2">
<div v-if="showLocalCovers" class="flex items-center justify-center flex-wrap pb-2">
<template v-for="localCoverFile in localCovers">
<div :key="localCoverFile.ino" class="m-0.5 mb-5 border-2 border-transparent hover:border-yellow-300 cursor-pointer" :class="localCoverFile.metadata.path === coverPath ? 'border-yellow-300' : ''" @click="setCover(localCoverFile)">
<div class="h-24 bg-primary" :style="{ width: 96 / bookCoverAspectRatio + 'px' }">
@@ -50,20 +50,22 @@
</div>
<form @submit.prevent="submitSearchForm">
<div class="flex flex-wrap sm:flex-nowrap items-center justify-start -mx-1">
<div class="w-48 flex-grow p-1">
<ui-dropdown v-model="provider" :items="providers" :label="$strings.LabelProvider" small />
<div class="w-48 grow p-1">
<ui-dropdown v-model="provider" :items="providers" :disabled="searchInProgress" :label="$strings.LabelProvider" small />
</div>
<div class="w-72 flex-grow p-1">
<ui-text-input-with-label v-model="searchTitle" :label="searchTitleLabel" :placeholder="$strings.PlaceholderSearch" />
<div class="w-72 grow p-1">
<ui-text-input-with-label v-model="searchTitle" :disabled="searchInProgress" :label="searchTitleLabel" :placeholder="$strings.PlaceholderSearch" />
</div>
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 flex-grow p-1">
<ui-text-input-with-label v-model="searchAuthor" :label="$strings.LabelAuthor" />
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 grow p-1">
<ui-text-input-with-label v-model="searchAuthor" :disabled="searchInProgress" :label="$strings.LabelAuthor" />
</div>
<ui-btn class="mt-5 ml-1 md:min-w-24" :padding-x="4" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
<ui-btn v-if="!searchInProgress" class="mt-5 ml-1 md:min-w-24" :padding-x="4" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
<ui-btn v-else class="mt-5 ml-1 md:min-w-24" :padding-x="4" type="button" color="bg-error" @click.prevent="cancelCurrentSearch">{{ $strings.ButtonCancel }}</ui-btn>
</div>
</form>
<div v-if="hasSearched" class="flex items-center flex-wrap justify-center sm:max-h-80 sm:overflow-y-scroll mt-2 max-w-full">
<p v-if="!coversFound.length">{{ $strings.MessageNoCoversFound }}</p>
<p v-if="searchInProgress && !coversFound.length" class="text-gray-300 py-4">{{ $strings.MessageLoading }}</p>
<p v-else-if="!searchInProgress && !coversFound.length" class="text-gray-300 py-4">{{ $strings.MessageNoCoversFound }}</p>
<template v-for="cover in coversFound">
<div :key="cover" class="m-0.5 mb-5 border-2 border-transparent hover:border-yellow-300 cursor-pointer" :class="cover === coverPath ? 'border-yellow-300' : ''" @click="updateCover(cover)">
<covers-preview-cover :src="cover" :width="80" show-open-new-tab :book-cover-aspect-ratio="bookCoverAspectRatio" />
@@ -79,7 +81,7 @@
</div>
<div class="absolute bottom-0 right-0 flex py-4 px-5">
<ui-btn :disabled="processingUpload" class="mx-2" @click="resetCoverPreview">{{ $strings.ButtonReset }}</ui-btn>
<ui-btn :loading="processingUpload" color="success" @click="submitCoverUpload">{{ $strings.ButtonUpload }}</ui-btn>
<ui-btn :loading="processingUpload" color="bg-success" @click="submitCoverUpload">{{ $strings.ButtonUpload }}</ui-btn>
</div>
</div>
</div>
@@ -105,7 +107,10 @@ export default {
showLocalCovers: false,
previewUpload: null,
selectedFile: null,
provider: 'google'
provider: 'google',
currentSearchRequestId: null,
searchInProgress: false,
socketListenersActive: false
}
},
watch: {
@@ -128,8 +133,8 @@ export default {
}
},
providers() {
if (this.isPodcast) return this.$store.state.scanners.podcastProviders
return [{ text: 'All', value: 'all' }, ...this.$store.state.scanners.providers, ...this.$store.state.scanners.coverOnlyProviders]
if (this.isPodcast) return this.$store.state.scanners.podcastCoverProviders
return this.$store.state.scanners.bookCoverProviders
},
searchTitleLabel() {
if (this.provider.startsWith('audible')) return this.$strings.LabelSearchTitleOrASIN
@@ -157,6 +162,12 @@ export default {
coverPath() {
return this.media.coverPath
},
coverUrl() {
if (!this.coverPath) {
return this.$store.getters['globals/getPlaceholderCoverSrc']
}
return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId, this.libraryItemUpdatedAt, true)
},
mediaMetadata() {
return this.media.metadata || {}
},
@@ -180,6 +191,9 @@ export default {
_file.localPath = `${process.env.serverUrl}/api/items/${this.libraryItemId}/file/${file.ino}?token=${this.userToken}`
return _file
})
},
socket() {
return this.$root.socket
}
},
methods: {
@@ -229,7 +243,19 @@ export default {
this.searchTitle = this.mediaMetadata.title || ''
this.searchAuthor = this.mediaMetadata.authorName || ''
if (this.isPodcast) this.provider = 'itunes'
else this.provider = localStorage.getItem('book-cover-provider') || localStorage.getItem('book-provider') || 'google'
else {
// Migrate from 'all' to 'best' (only once)
const migrationKey = 'book-cover-provider-migrated'
const currentProvider = localStorage.getItem('book-cover-provider') || localStorage.getItem('book-provider') || 'google'
if (!localStorage.getItem(migrationKey) && currentProvider === 'all') {
localStorage.setItem('book-cover-provider', 'best')
localStorage.setItem(migrationKey, 'true')
this.provider = 'best'
} else {
this.provider = currentProvider
}
}
},
removeCover() {
if (!this.coverPath) {
@@ -285,22 +311,116 @@ export default {
console.error('PersistProvider', error)
}
},
generateRequestId() {
return `cover-search-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
},
addSocketListeners() {
if (!this.socket || this.socketListenersActive) return
this.socket.on('cover_search_result', this.handleSearchResult)
this.socket.on('cover_search_complete', this.handleSearchComplete)
this.socket.on('cover_search_error', this.handleSearchError)
this.socket.on('cover_search_provider_error', this.handleProviderError)
this.socket.on('cover_search_cancelled', this.handleSearchCancelled)
this.socket.on('disconnect', this.handleSocketDisconnect)
this.socketListenersActive = true
},
removeSocketListeners() {
if (!this.socket || !this.socketListenersActive) return
this.socket.off('cover_search_result', this.handleSearchResult)
this.socket.off('cover_search_complete', this.handleSearchComplete)
this.socket.off('cover_search_error', this.handleSearchError)
this.socket.off('cover_search_provider_error', this.handleProviderError)
this.socket.off('cover_search_cancelled', this.handleSearchCancelled)
this.socket.off('disconnect', this.handleSocketDisconnect)
this.socketListenersActive = false
},
handleSearchResult(data) {
if (data.requestId !== this.currentSearchRequestId) return
// Add new covers to the list (avoiding duplicates)
const newCovers = data.covers.filter((cover) => !this.coversFound.includes(cover))
this.coversFound.push(...newCovers)
},
handleSearchComplete(data) {
if (data.requestId !== this.currentSearchRequestId) return
this.searchInProgress = false
this.currentSearchRequestId = null
},
handleSearchError(data) {
if (data.requestId !== this.currentSearchRequestId) return
console.error('[Cover Search] Search error:', data.error)
this.$toast.error(this.$strings.ToastCoverSearchFailed)
this.searchInProgress = false
this.currentSearchRequestId = null
},
handleProviderError(data) {
if (data.requestId !== this.currentSearchRequestId) return
console.warn(`[Cover Search] Provider ${data.provider} failed:`, data.error)
},
handleSearchCancelled(data) {
if (data.requestId !== this.currentSearchRequestId) return
this.searchInProgress = false
this.currentSearchRequestId = null
},
handleSocketDisconnect() {
// If we were in the middle of a search, cancel it (server can't send results anymore)
if (this.searchInProgress && this.currentSearchRequestId) {
this.searchInProgress = false
this.currentSearchRequestId = null
}
},
cancelCurrentSearch() {
if (!this.currentSearchRequestId || !this.socket?.connected) {
console.error('[Cover Search] Socket not connected')
this.$toast.error(this.$strings.ToastConnectionNotAvailable)
return
}
this.socket.emit('cancel_cover_search', this.currentSearchRequestId)
this.currentSearchRequestId = null
this.searchInProgress = false
},
async submitSearchForm() {
if (!this.socket?.connected) {
console.error('[Cover Search] Socket not connected')
this.$toast.error(this.$strings.ToastConnectionNotAvailable)
return
}
// Cancel any existing search
if (this.searchInProgress) {
this.cancelCurrentSearch()
}
// Store provider in local storage
this.persistProvider()
this.isProcessing = true
const searchQuery = this.getSearchQuery()
const results = await this.$axios
.$get(`/api/search/covers?${searchQuery}`)
.then((res) => res.results)
.catch((error) => {
console.error('Failed', error)
return []
})
this.coversFound = results
this.isProcessing = false
// Setup socket listeners if not already done
this.addSocketListeners()
// Clear previous results
this.coversFound = []
this.hasSearched = true
this.searchInProgress = true
// Generate unique request ID
const requestId = this.generateRequestId()
this.currentSearchRequestId = requestId
// Emit search request via WebSocket
this.socket.emit('search_covers', {
requestId,
title: this.searchTitle,
author: this.searchAuthor || '',
provider: this.provider,
podcast: this.isPodcast
})
},
setCover(coverFile) {
this.isProcessing = true
@@ -314,6 +434,20 @@ export default {
this.isProcessing = false
})
}
},
mounted() {
// Setup socket listeners when component is mounted
this.addSocketListeners()
// Fetch providers if not already loaded
this.$store.dispatch('scanners/fetchProviders')
},
beforeDestroy() {
// Cancel any ongoing search when component is destroyed
if (this.searchInProgress) {
this.cancelCurrentSearch()
}
// Remove socket listeners
this.removeSocketListeners()
}
}
</script>
@@ -5,17 +5,15 @@
<widgets-podcast-details-edit v-else ref="itemDetailsEdit" :library-item="libraryItem" @submit="saveAndClose" />
</div>
<div class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg" :class="isScrollable ? 'box-shadow-md-up' : 'border-t border-white border-opacity-5'">
<div class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg" :class="isScrollable ? 'box-shadow-md-up' : 'border-t border-white/5'">
<div class="flex items-center px-4">
<ui-tooltip :disabled="!!quickMatching" :text="$getString('MessageQuickMatchDescription', [libraryProvider])" direction="bottom" class="mr-2 md:mr-4">
<ui-btn v-if="userIsAdminOrUp" :loading="quickMatching" color="bg" type="button" class="h-full" small @click.stop.prevent="quickMatch">{{ $strings.ButtonQuickMatch }}</ui-btn>
<ui-btn v-if="userIsAdminOrUp" :loading="quickMatching" color="bg-bg" type="button" class="h-full" small @click.stop.prevent="quickMatch">{{ $strings.ButtonQuickMatch }}</ui-btn>
</ui-tooltip>
<ui-tooltip :disabled="isLibraryScanning" text="Rescan library item including metadata" direction="bottom" class="mr-2 md:mr-4">
<ui-btn v-if="userIsAdminOrUp && !isFile" :loading="rescanning" :disabled="isLibraryScanning" color="bg" type="button" class="h-full" small @click.stop.prevent="rescan">{{ $strings.ButtonReScan }}</ui-btn>
</ui-tooltip>
<ui-btn v-if="userIsAdminOrUp && !isFile" :loading="rescanning" :disabled="isLibraryScanning" color="bg-bg" type="button" class="h-full" small @click.stop.prevent="rescan">{{ $strings.ButtonReScan }}</ui-btn>
<div class="flex-grow" />
<div class="grow" />
<!-- desktop -->
<ui-btn @click="save" class="mx-2 hidden md:block">{{ $strings.ButtonSave }}</ui-btn>
@@ -29,9 +29,6 @@ export default {
media() {
return this.libraryItem.media || {}
},
userToken() {
return this.$store.getters['user/getToken']
},
userCanUpdate() {
return this.$store.getters['user/getUserCanUpdate']
},
+92 -63
View File
@@ -2,10 +2,10 @@
<div id="match-wrapper" class="w-full h-full overflow-hidden px-2 md:px-4 py-4 md:py-6 relative">
<form @submit.prevent="submitSearch">
<div class="flex flex-wrap md:flex-nowrap items-center justify-start -mx-1">
<div class="w-36 px-1">
<div v-if="providersLoaded" class="w-36 px-1">
<ui-dropdown v-model="provider" :items="providers" :label="$strings.LabelProvider" small />
</div>
<div class="flex-grow md:w-72 px-1">
<div class="grow md:w-72 px-1">
<ui-text-input-with-label v-model="searchTitle" :label="searchTitleLabel" :placeholder="$strings.PlaceholderSearch" />
</div>
<div v-show="provider != 'itunes'" class="w-60 md:w-72 px-1">
@@ -27,7 +27,7 @@
</div>
<div v-if="selectedMatchOrig" class="absolute top-0 left-0 w-full bg-bg h-full px-2 py-6 md:p-8 max-h-full overflow-y-auto overflow-x-hidden">
<div class="flex mb-4">
<div class="w-8 h-8 rounded-full hover:bg-white hover:bg-opacity-10 flex items-center justify-center cursor-pointer" @click="clearSelectedMatch">
<div class="w-8 h-8 rounded-full hover:bg-white/10 flex items-center justify-center cursor-pointer" @click="clearSelectedMatch">
<span class="material-symbols text-3xl">arrow_back</span>
</div>
<p class="text-xl pl-3">{{ $strings.HeaderUpdateDetails }}</p>
@@ -35,9 +35,9 @@
<ui-checkbox v-model="selectAll" :label="$strings.LabelSelectAll" checkbox-bg="bg" @input="selectAllToggled" />
<form @submit.prevent="submitMatchUpdate">
<div v-if="selectedMatchOrig.cover" class="flex flex-wrap md:flex-nowrap items-center justify-center">
<div class="flex flex-grow items-center py-2">
<div class="flex grow items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.cover" checkbox-bg="bg" @input="checkboxToggled" />
<ui-text-input-with-label v-model="selectedMatch.cover" :disabled="!selectedMatchUsage.cover" readonly :label="$strings.LabelCover" class="flex-grow mx-4" />
<ui-text-input-with-label v-model="selectedMatch.cover" :disabled="!selectedMatchUsage.cover" readonly :label="$strings.LabelCover" class="grow mx-4" />
</div>
<div class="flex py-2">
@@ -57,118 +57,118 @@
</div>
<div v-if="selectedMatchOrig.title" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.title" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.title" :disabled="!selectedMatchUsage.title" :label="$strings.LabelTitle" />
<p v-if="mediaMetadata.title" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.title" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('title', mediaMetadata.title)">{{ mediaMetadata.title || '' }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.subtitle" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.subtitle" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.subtitle" :disabled="!selectedMatchUsage.subtitle" :label="$strings.LabelSubtitle" />
<p v-if="mediaMetadata.subtitle" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.subtitle" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('subtitle', mediaMetadata.subtitle)">{{ mediaMetadata.subtitle }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.author" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.author" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.author" :disabled="!selectedMatchUsage.author" :label="$strings.LabelAuthor" />
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ mediaMetadata.authorName }}</a>
<p v-if="mediaMetadata.authorName || (isPodcast && mediaMetadata.author)" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', isPodcast ? mediaMetadata.author : mediaMetadata.authorName)">{{ isPodcast ? mediaMetadata.author : mediaMetadata.authorName }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.narrator" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.narrator" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-multi-select v-model="selectedMatch.narrator" :items="narrators" :disabled="!selectedMatchUsage.narrator" :label="$strings.LabelNarrators" />
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('narrator', mediaMetadata.narrators)">{{ mediaMetadata.narratorName }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.description" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.description" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-rich-text-editor v-model="selectedMatch.description" :disabled="!selectedMatchUsage.description" :label="$strings.LabelDescription" />
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.descriptionPlain.substr(0, 100) + (mediaMetadata.descriptionPlain.length > 100 ? '...' : '') }}</a>
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.descriptionPlain.substr(0, 100) + (mediaMetadata.descriptionPlain.length > 100 ? '...' : '') }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.publisher" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.publisher" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.publisher" :disabled="!selectedMatchUsage.publisher" :label="$strings.LabelPublisher" />
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publisher', mediaMetadata.publisher)">{{ mediaMetadata.publisher }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.publishedYear" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.publishedYear" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.publishedYear" :disabled="!selectedMatchUsage.publishedYear" :label="$strings.LabelPublishYear" />
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.series" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.series" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<widgets-series-input-widget v-model="selectedMatch.series" :disabled="!selectedMatchUsage.series" />
<p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.genres?.length" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.genres" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-multi-select v-model="selectedMatch.genres" :items="genres" :disabled="!selectedMatchUsage.genres" :label="$strings.LabelGenres" />
<p v-if="mediaMetadata.genres?.length" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.genres?.length" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('genres', mediaMetadata.genres)">{{ mediaMetadata.genres.join(', ') }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.tags" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.tags" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-multi-select v-model="selectedMatch.tags" :items="tags" :disabled="!selectedMatchUsage.tags" :label="$strings.LabelTags" />
<p v-if="media.tags?.length" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="media.tags?.length" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('tags', media.tags)">{{ media.tags.join(', ') }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.language" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.language" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.language" :disabled="!selectedMatchUsage.language" :label="$strings.LabelLanguage" />
<p v-if="mediaMetadata.language" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.language" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('language', mediaMetadata.language)">{{ mediaMetadata.language }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.isbn" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.isbn" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.isbn" :disabled="!selectedMatchUsage.isbn" label="ISBN" />
<p v-if="mediaMetadata.isbn" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.isbn" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('isbn', mediaMetadata.isbn)">{{ mediaMetadata.isbn }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.asin" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.asin" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.asin" :disabled="!selectedMatchUsage.asin" label="ASIN" />
<p v-if="mediaMetadata.asin" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.asin" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
</p>
</div>
@@ -176,57 +176,57 @@
<div v-if="selectedMatchOrig.itunesId" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.itunesId" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.itunesId" type="number" :disabled="!selectedMatchUsage.itunesId" label="iTunes ID" />
<p v-if="mediaMetadata.itunesId" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.itunesId" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesId', mediaMetadata.itunesId)">{{ mediaMetadata.itunesId }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.feedUrl" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.feedUrl" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.feedUrl" :disabled="!selectedMatchUsage.feedUrl" label="RSS Feed URL" />
<p v-if="mediaMetadata.feedUrl" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.feedUrl" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('feedUrl', mediaMetadata.feedUrl)">{{ mediaMetadata.feedUrl }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.itunesPageUrl" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.itunesPageUrl" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.itunesPageUrl" :disabled="!selectedMatchUsage.itunesPageUrl" label="iTunes Page URL" />
<p v-if="mediaMetadata.itunesPageUrl" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.itunesPageUrl" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('itunesPageUrl', mediaMetadata.itunesPageUrl)">{{ mediaMetadata.itunesPageUrl }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.releaseDate" class="flex items-center py-2">
<ui-checkbox v-model="selectedMatchUsage.releaseDate" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4">
<div class="grow ml-4">
<ui-text-input-with-label v-model="selectedMatch.releaseDate" :disabled="!selectedMatchUsage.releaseDate" :label="$strings.LabelReleaseDate" />
<p v-if="mediaMetadata.releaseDate" class="text-xs ml-1 text-white text-opacity-60">
<p v-if="mediaMetadata.releaseDate" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a :title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('releaseDate', mediaMetadata.releaseDate)">{{ mediaMetadata.releaseDate }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.explicit != null" class="flex items-center pb-2" :class="{ 'pt-2': mediaMetadata.explicit == null }">
<ui-checkbox v-model="selectedMatchUsage.explicit" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4" :class="{ 'pt-4': mediaMetadata.explicit != null }">
<div class="grow ml-4" :class="{ 'pt-4': mediaMetadata.explicit != null }">
<ui-checkbox v-model="selectedMatch.explicit" :label="$strings.LabelExplicit" :disabled="!selectedMatchUsage.explicit" :checkbox-bg="!selectedMatchUsage.explicit ? 'bg' : 'primary'" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
<p v-if="mediaMetadata.explicit != null" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.explicit ? $strings.LabelExplicitChecked : $strings.LabelExplicitUnchecked }}</p>
<p v-if="mediaMetadata.explicit != null" class="text-xs ml-1 text-white/60">{{ $strings.LabelCurrently }} {{ mediaMetadata.explicit ? $strings.LabelExplicitChecked : $strings.LabelExplicitUnchecked }}</p>
</div>
</div>
<div v-if="selectedMatchOrig.abridged != null" class="flex items-center pb-2" :class="{ 'pt-2': mediaMetadata.abridged == null }">
<ui-checkbox v-model="selectedMatchUsage.abridged" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-grow ml-4" :class="{ 'pt-4': mediaMetadata.abridged != null }">
<div class="grow ml-4" :class="{ 'pt-4': mediaMetadata.abridged != null }">
<ui-checkbox v-model="selectedMatch.abridged" :label="$strings.LabelAbridged" :disabled="!selectedMatchUsage.abridged" :checkbox-bg="!selectedMatchUsage.abridged ? 'bg' : 'primary'" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
<p v-if="mediaMetadata.abridged != null" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.abridged ? $strings.LabelAbridgedChecked : $strings.LabelAbridgedUnchecked }}</p>
<p v-if="mediaMetadata.abridged != null" class="text-xs ml-1 text-white/60">{{ $strings.LabelCurrently }} {{ mediaMetadata.abridged ? $strings.LabelAbridgedChecked : $strings.LabelAbridgedUnchecked }}</p>
</div>
</div>
<div class="flex items-center justify-end py-2">
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</form>
</div>
@@ -253,6 +253,7 @@ export default {
hasSearched: false,
selectedMatch: null,
selectedMatchOrig: null,
waitingForProviders: false,
selectedMatchUsage: {
title: true,
subtitle: true,
@@ -285,9 +286,19 @@ export default {
handler(newVal) {
if (newVal) this.init()
}
},
providersLoaded(isLoaded) {
// Complete initialization once providers are loaded
if (isLoaded && this.waitingForProviders) {
this.waitingForProviders = false
this.initProviderAndSearch()
}
}
},
computed: {
providersLoaded() {
return this.$store.getters['scanners/areProvidersLoaded']
},
isProcessing: {
get() {
return this.processing
@@ -319,7 +330,7 @@ export default {
},
providers() {
if (this.isPodcast) return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
return this.$store.state.scanners.bookProviders
},
searchTitleLabel() {
if (this.provider.startsWith('audible')) return this.$strings.LabelSearchTitleOrASIN
@@ -400,7 +411,9 @@ export default {
this.$toast.warning(this.$strings.ToastTitleRequired)
return
}
this.persistProvider()
if (!this.isPodcast) {
this.persistProvider()
}
this.runSearch()
},
async runSearch() {
@@ -476,6 +489,24 @@ export default {
this.checkboxToggled()
},
initProviderAndSearch() {
// Set provider based on media type
if (this.isPodcast) {
this.provider = 'itunes'
} else {
this.provider = this.getDefaultBookProvider()
}
// Prefer using ASIN if set and using audible provider
if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) {
this.searchTitle = this.libraryItem.media.metadata.asin
this.searchAuthor = ''
}
if (this.searchTitle) {
this.submitSearch()
}
},
init() {
this.clearSelectedMatch()
this.initSelectedMatchUsage()
@@ -493,19 +524,13 @@ export default {
}
this.searchTitle = this.libraryItem.media.metadata.title
this.searchAuthor = this.libraryItem.media.metadata.authorName || ''
if (this.isPodcast) this.provider = 'itunes'
else {
this.provider = this.getDefaultBookProvider()
}
// Prefer using ASIN if set and using audible provider
if (this.provider.startsWith('audible') && this.libraryItem.media.metadata.asin) {
this.searchTitle = this.libraryItem.media.metadata.asin
this.searchAuthor = ''
}
if (this.searchTitle) {
this.submitSearch()
// Wait for providers to be loaded before setting provider and searching
if (this.providersLoaded || this.isPodcast) {
this.waitingForProviders = false
this.initProviderAndSearch()
} else {
this.waitingForProviders = true
}
},
selectMatch(match) {
@@ -635,6 +660,10 @@ export default {
this.selectedMatch = null
this.selectedMatchOrig = null
}
},
mounted() {
// Fetch providers if not already loaded
this.$store.dispatch('scanners/fetchProviders')
}
}
</script>
@@ -33,10 +33,10 @@
</template>
</div>
<div v-if="feedUrl || autoDownloadEpisodes" class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white border-opacity-5">
<div v-if="feedUrl || autoDownloadEpisodes" class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white/5">
<div class="flex items-center px-2 md:px-4">
<div class="flex-grow" />
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'success' : 'primary'" class="mx-2">{{ isUpdated ? $strings.ButtonSave : $strings.MessageNoUpdatesWereNecessary }}</ui-btn>
<div class="grow" />
<ui-btn @click="save" :disabled="!isUpdated" :color="isUpdated ? 'bg-success' : 'bg-primary'" class="mx-2">{{ isUpdated ? $strings.ButtonSave : $strings.MessageNoUpdatesWereNecessary }}</ui-btn>
</div>
</div>
</div>
@@ -158,6 +158,8 @@ export default {
this.isProcessing = true
var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, updatePayload).catch((error) => {
console.error('Failed to update', error)
const errorMessage = typeof error?.response?.data === 'string' ? error?.response?.data : null
this.$toast.error(errorMessage || this.$strings.ToastFailedToUpdate)
return false
})
this.isProcessing = false
+4 -10
View File
@@ -9,7 +9,7 @@
<p class="text-lg">{{ $strings.LabelToolsMakeM4b }}</p>
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $strings.LabelToolsMakeM4bDescription }}</p>
</div>
<div class="flex-grow" />
<div class="grow" />
<div>
<ui-btn :to="`/audiobook/${libraryItemId}/manage?tool=m4b`" class="flex items-center"
>{{ $strings.ButtonOpenManager }}
@@ -26,7 +26,7 @@
<p class="text-lg">{{ $strings.LabelToolsEmbedMetadata }}</p>
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $strings.LabelToolsEmbedMetadataDescription }}</p>
</div>
<div class="flex-grow" />
<div class="grow" />
<div>
<ui-btn :to="`/audiobook/${libraryItemId}/manage?tool=embed`" class="flex items-center"
>{{ $strings.ButtonOpenManager }}
@@ -74,19 +74,12 @@ export default {
mediaTracks() {
return this.media.tracks || []
},
isSingleM4b() {
return this.mediaTracks.length === 1 && this.mediaTracks[0].metadata.ext.toLowerCase() === '.m4b'
},
chapters() {
return this.media.chapters || []
},
showM4bDownload() {
if (!this.mediaTracks.length) return false
return !this.isSingleM4b
},
showMp3Split() {
if (!this.mediaTracks.length) return false
return this.isSingleM4b && this.chapters.length
return true
},
queuedEmbedLIds() {
return this.$store.state.tasks.queuedEmbedLIds || []
@@ -114,6 +107,7 @@ export default {
quickEmbed() {
const payload = {
message: this.$strings.MessageConfirmQuickEmbed,
allowHtml: true,
callback: (confirmed) => {
if (confirmed) {
this.$axios
@@ -5,7 +5,7 @@
<div class="w-2/5 md:w-72 px-1 py-1 md:py-0">
<ui-dropdown v-model="mediaType" :items="mediaTypes" :label="$strings.LabelMediaType" :disabled="!isNew" small @input="changedMediaType" />
</div>
<div class="w-full md:flex-grow px-1 py-1 md:py-0">
<div class="w-full md:grow px-1 py-1 md:py-0">
<ui-text-input-with-label ref="nameInput" v-model="name" :label="$strings.LabelLibraryName" @blur="nameBlurred" />
</div>
<div class="w-1/5 md:w-18 px-1 py-1 md:py-0">
@@ -19,16 +19,16 @@
<div class="folders-container overflow-y-auto w-full py-2 mb-2">
<p class="px-1 text-sm font-semibold">{{ $strings.LabelFolders }}</p>
<div v-for="(folder, index) in folders" :key="index" class="w-full flex items-center py-1 px-2">
<span class="material-symbols fill bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<ui-editable-text ref="folderInput" v-model="folder.fullPath" :readonly="!!folder.id" type="text" class="w-full" @blur="existingFolderInputBlurred(folder)" />
<span v-show="folders.length > 1" class="material-symbols text-2xl ml-2 cursor-pointer hover:text-error" @click="removeFolder(folder)">close</span>
</div>
<div class="flex py-1 px-2 items-center w-full">
<span class="material-symbols fill bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<ui-editable-text ref="newFolderInput" v-model="newFolderPath" :placeholder="$strings.PlaceholderNewFolderPath" type="text" class="w-full" @blur="newFolderInputBlurred" />
</div>
<ui-btn class="w-full mt-2" color="primary" @click="browseForFolder">{{ $strings.ButtonBrowseForFolder }}</ui-btn>
<ui-btn class="w-full mt-2" color="bg-primary" @click="browseForFolder">{{ $strings.ButtonBrowseForFolder }}</ui-btn>
</div>
</div>
<modals-libraries-lazy-folder-chooser v-else :paths="folderPaths" @back="showDirectoryPicker = false" @select="selectFolder" />
@@ -74,7 +74,7 @@ export default {
},
providers() {
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
return this.$store.state.scanners.bookProviders
}
},
methods: {
@@ -156,6 +156,8 @@ export default {
},
mounted() {
this.init()
// Fetch providers if not already loaded
this.$store.dispatch('scanners/fetchProviders')
}
}
</script>
@@ -14,7 +14,7 @@
<div class="px-2 md:px-4 w-full text-sm pt-2 md:pt-6 pb-20 rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
<component v-if="libraryCopy && show" ref="tabComponent" :is="tabName" :is-new="!library" :library="libraryCopy" :library-id="libraryId" :processing.sync="processing" @update="updateLibrary" @close="show = false" />
<div v-show="selectedTab !== 'tools'" class="absolute bottom-0 left-0 w-full px-4 py-4 border-t border-white border-opacity-10">
<div v-show="selectedTab !== 'tools'" class="absolute bottom-0 left-0 w-full px-4 py-4 border-t border-white/10">
<div class="flex justify-end">
<ui-btn @click="submit">{{ buttonText }}</ui-btn>
</div>
@@ -4,24 +4,24 @@
<span class="material-symbols text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
<p class="px-4 text-xl">{{ $strings.HeaderChooseAFolder }}</p>
</div>
<div v-if="rootDirs.length" class="w-full bg-primary bg-opacity-70 py-1 px-4 mb-2">
<div v-if="rootDirs.length" class="w-full bg-primary/70 py-1 px-4 mb-2">
<p class="font-mono truncate">{{ selectedPath || '/' }}</p>
</div>
<div v-if="rootDirs.length" class="relative flex bg-primary bg-opacity-50 p-4 folder-container">
<div v-if="rootDirs.length" class="relative flex bg-primary/50 p-4 folder-container">
<div class="w-1/2 border-r border-bg h-full overflow-y-auto">
<div v-if="level > 0" class="w-full p-1 cursor-pointer flex items-center hover:bg-white/10" @click="goBack">
<span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill text-yellow-200" style="font-size: 1.2rem">folder</span>
<p class="text-base font-mono px-2">..</p>
</div>
<div v-for="dir in _directories" :key="dir.path" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" :class="dir.className" @click="selectDir(dir)">
<span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill text-yellow-200" style="font-size: 1.2rem">folder</span>
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
<span v-if="dir.path === selectedPath" class="material-symbols" style="font-size: 1.1rem">arrow_right</span>
</div>
</div>
<div class="w-1/2 h-full overflow-y-auto">
<div v-for="dir in _subdirs" :key="dir.path" :class="dir.className" class="dir-item w-full p-1 cursor-pointer flex items-center hover:text-white text-gray-200 hover:bg-white/10" @click="selectSubDir(dir)">
<span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill text-yellow-200" style="font-size: 1.2rem">folder</span>
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
</div>
</div>
@@ -38,7 +38,7 @@
</div>
<div class="w-full py-2">
<ui-btn :disabled="!selectedPath" color="primary" class="w-full mt-2" @click="selectFolder">{{ $strings.ButtonSelectFolderPath }}</ui-btn>
<ui-btn :disabled="!selectedPath" color="bg-primary" class="w-full mt-2" @click="selectFolder">{{ $strings.ButtonSelectFolderPath }}</ui-btn>
</div>
</div>
</template>
@@ -21,7 +21,7 @@
<div class="text-center py-1 w-8 min-w-8">
{{ source.include ? getSourceIndex(source.id) : '' }}
</div>
<div class="flex-grow inline-flex justify-between px-4 py-3">
<div class="grow inline-flex justify-between px-4 py-3">
{{ source.name }} <span v-if="source.include && (index === firstActiveSourceIndex || index === lastActiveSourceIndex)" class="px-2 italic font-semibold text-xs text-gray-400">{{ index === firstActiveSourceIndex ? $strings.LabelHighestPriority : $strings.LabelLowestPriority }}</span>
</div>
<div class="px-2 opacity-100">
@@ -104,7 +104,6 @@ export default {
},
data() {
return {
provider: null,
useSquareBookCovers: false,
enableWatcher: false,
skipMatchingMediaWithAsin: false,
@@ -134,10 +133,6 @@ export default {
isPodcastLibrary() {
return this.mediaType === 'podcast'
},
providers() {
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
},
maskAsFinishedWhenItems() {
return [
{
@@ -6,7 +6,7 @@
<p class="text-lg">{{ $strings.LabelRemoveMetadataFile }}</p>
<p class="max-w-sm text-sm pt-2 text-gray-300">{{ $getString('LabelRemoveMetadataFileHelp', [mediaType]) }}</p>
</div>
<div class="flex-grow" />
<div class="grow" />
<div>
<ui-btn class="mb-4 block" @click.stop="removeAllMetadataClick('json')">{{ $strings.LabelRemoveAllMetadataJson }}</ui-btn>
<ui-btn @click.stop="removeAllMetadataClick('abs')">{{ $strings.LabelRemoveAllMetadataAbs }}</ui-btn>
@@ -25,8 +25,8 @@
<ui-toggle-switch v-model="newNotification.enabled" />
<p class="text-lg pl-2">{{ $strings.LabelEnable }}</p>
</div>
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<template>
<div v-if="item" class="w-full flex items-center px-4 py-2" :class="wrapperClass" @mouseover="mouseover" @mouseleave="mouseleave">
<covers-preview-cover :src="coverUrl" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" />
<div class="flex-grow px-2 py-1 queue-item-row-content truncate">
<div class="grow px-2 py-1 queue-item-row-content truncate">
<p class="text-gray-200 text-sm truncate">{{ title }}</p>
<p class="text-gray-300 text-sm">{{ subtitle }}</p>
<p v-if="caption" class="text-gray-400 text-xs">{{ caption }}</p>
@@ -9,10 +9,10 @@
<div class="w-28">
<p v-if="isOpenInPlayer" class="text-sm text-right text-gray-400">{{ $strings.ButtonPlaying }}</p>
<div v-else-if="isHovering" class="flex items-center justify-end -mx-1">
<button class="outline-none mx-1 flex items-center" @click.stop="playClick">
<button class="outline-hidden mx-1 flex items-center" @click.stop="playClick">
<span class="material-symbols fill text-2xl text-success">play_arrow</span>
</button>
<button class="outline-none mx-1 flex items-center" @click.stop="removeClick">
<button class="outline-hidden mx-1 flex items-center" @click.stop="removeClick">
<span class="material-symbols text-2xl text-error">close</span>
</button>
</div>
@@ -55,7 +55,7 @@ export default {
return this.item.coverPath
},
coverUrl() {
if (!this.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
if (!this.coverPath) return this.$store.getters['globals/getPlaceholderCoverSrc']
return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId)
},
bookCoverAspectRatio() {
@@ -72,9 +72,9 @@ export default {
return this.$store.getters['getIsMediaStreaming'](this.libraryItemId, this.episodeId)
},
wrapperClass() {
if (this.isOpenInPlayer) return 'bg-yellow-400 bg-opacity-10'
if (this.index % 2 === 0) return 'bg-gray-300 bg-opacity-5 hover:bg-opacity-10'
return 'bg-bg hover:bg-gray-300 hover:bg-opacity-10'
if (this.isOpenInPlayer) return 'bg-yellow-400/10'
if (this.index % 2 === 0) return 'bg-gray-300/5 hover:bg-gray-300/10'
return 'bg-bg hover:bg-gray-300/10'
}
},
methods: {
@@ -99,4 +99,4 @@ export default {
.queue-item-row-content {
max-width: calc(100% - 48px - 128px);
}
</style>
</style>
@@ -10,7 +10,7 @@
<div class="pb-4 px-4 flex items-center">
<p class="text-base text-gray-200">{{ $strings.HeaderPlayerQueue }}</p>
<p class="text-base text-gray-400 px-4">{{ playerQueueItems.length }} Items</p>
<div class="flex-grow" />
<div class="grow" />
<ui-checkbox v-model="playerQueueAutoPlay" label="Auto Play" medium checkbox-bg="primary" border-color="gray-600" label-class="pl-2 mb-px" />
</div>
<modals-player-queue-item-row v-for="(item, index) in playerQueueItems" :key="index" :item="item" :index="index" @play="playItem(index)" @remove="removeItem" />
@@ -6,7 +6,7 @@
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full">
<div class="py-4 px-4">
<h1 v-if="!isBatch" class="text-2xl">{{ $strings.LabelAddToPlaylist }}</h1>
@@ -32,13 +32,13 @@
</div>
</div>
</div>
<div class="w-full h-px bg-white bg-opacity-10" />
<div class="w-full h-px bg-white/10" />
<form @submit.prevent="submitCreatePlaylist">
<div class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
<div class="flex-grow px-2">
<div class="flex px-4 py-2 items-center text-center border-b border-white/10 text-white/80">
<div class="grow px-2">
<ui-text-input v-model="newPlaylistName" :placeholder="$strings.PlaceholderNewPlaylist" class="w-full" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
<ui-btn type="submit" color="bg-success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
</div>
</form>
</div>
@@ -97,7 +97,10 @@ export default {
...playlist
}
})
.sort((a, b) => (a.isItemIncluded ? -1 : 1))
.sort((a, b) => {
if (a.isItemIncluded !== b.isItemIncluded) return a.isItemIncluded ? -1 : 1
return a.name.localeCompare(b.name)
})
},
isBatch() {
return this.selectedPlaylistItems.length > 1
@@ -11,16 +11,16 @@
<div>
<covers-playlist-cover :items="items" :width="200" :height="200" />
</div>
<div class="flex-grow px-4">
<div class="grow px-4">
<ui-text-input-with-label v-model="newPlaylistName" :label="$strings.LabelName" class="mb-2" />
<ui-textarea-with-label v-model="newPlaylistDescription" :label="$strings.LabelDescription" />
</div>
</div>
<div class="absolute bottom-0 left-0 right-0 w-full py-2 px-4 flex">
<ui-btn v-if="userCanDelete" small color="error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="flex-grow" />
<ui-btn color="success" type="submit">{{ $strings.ButtonSave }}</ui-btn>
<ui-btn v-if="userCanDelete" small color="bg-error" type="button" @click.stop="removeClick">{{ $strings.ButtonRemove }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" type="submit">{{ $strings.ButtonSave }}</ui-btn>
</div>
</form>
</div>
@@ -74,21 +74,32 @@ export default {
this.newPlaylistDescription = this.playlist.description || ''
},
removeClick() {
if (confirm(this.$getString('MessageConfirmRemovePlaylist', [this.playlistName]))) {
this.processing = true
this.$axios
.$delete(`/api/playlists/${this.playlist.id}`)
.then(() => {
this.processing = false
this.show = false
this.$toast.success(this.$strings.ToastPlaylistRemoveSuccess)
})
.catch((error) => {
console.error('Failed to remove playlist', error)
this.processing = false
this.$toast.error(this.$strings.ToastRemoveFailed)
})
const payload = {
message: this.$getString('MessageConfirmRemovePlaylist', [this.playlistName]),
callback: (confirmed) => {
if (confirmed) {
this.removePlaylist()
}
},
type: 'yesNo'
}
this.$store.commit('globals/setConfirmPrompt', payload)
},
removePlaylist() {
this.processing = true
this.$axios
.$delete(`/api/playlists/${this.playlist.id}`)
.then(() => {
this.show = false
this.$toast.success(this.$strings.ToastPlaylistRemoveSuccess)
})
.catch((error) => {
console.error('Failed to remove playlist', error)
this.$toast.error(this.$strings.ToastRemoveFailed)
})
.finally(() => {
this.processing = false
})
},
submitForm() {
if (this.newPlaylistName === this.playlistName && this.newPlaylistDescription === this.playlist.description) {
@@ -1,15 +1,15 @@
<template>
<div class="flex items-center px-4 py-2 justify-start relative hover:bg-bg" @mouseover="mouseover" @mouseleave="mouseleave">
<div class="flex items-center px-4 py-2 justify-start relative hover:bg-black-400" @mouseover="mouseover" @mouseleave="mouseleave">
<div v-if="isItemIncluded" class="absolute top-0 left-0 h-full w-1 bg-success z-10" />
<div class="w-16 max-w-16 text-center">
<covers-playlist-cover :items="items" :width="64" :height="64" />
</div>
<div class="flex-grow overflow-hidden px-2">
<div class="grow overflow-hidden px-2">
<nuxt-link :to="`/playlist/${playlist.id}`" class="pl-2 pr-2 truncate hover:underline cursor-pointer" @click.native="clickNuxtLink">{{ playlist.name }}</nuxt-link>
</div>
<div class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'">
<ui-btn v-if="!isItemIncluded" color="success" :padding-x="3" small class="h-9" @click.stop="clickAdd"><span class="material-symbols text-2xl pt-px">add</span></ui-btn>
<ui-btn v-else color="error" :padding-x="3" class="h-9" small @click.stop="clickRem"><span class="material-symbols text-2xl pt-px">remove</span></ui-btn>
<ui-btn v-if="!isItemIncluded" color="bg-success" :padding-x="3" small class="h-9" @click.stop="clickAdd"><span class="material-symbols text-2xl pt-px">add</span></ui-btn>
<ui-btn v-else color="bg-error" :padding-x="3" class="h-9" small @click.stop="clickRem"><span class="material-symbols text-2xl pt-px">remove</span></ui-btn>
</div>
</div>
</template>
@@ -54,4 +54,4 @@ export default {
},
mounted() {}
}
</script>
</script>
@@ -12,10 +12,10 @@
</div>
<div v-show="canGoPrev" class="absolute -left-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6">
<div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goPrevEpisode" @mousedown.prevent>arrow_back_ios</div>
<div class="material-symbols text-5xl text-white/50 hover:text-white/90 cursor-pointer pointer-events-auto" @click.stop.prevent="goPrevEpisode" @mousedown.prevent>arrow_back_ios</div>
</div>
<div v-show="canGoNext" class="absolute -right-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6">
<div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goNextEpisode" @mousedown.prevent>arrow_forward_ios</div>
<div class="material-symbols text-5xl text-white/50 hover:text-white/90 cursor-pointer pointer-events-auto" @click.stop.prevent="goNextEpisode" @mousedown.prevent>arrow_forward_ios</div>
</div>
<div ref="wrapper" class="p-4 w-full text-sm rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative overflow-y-auto" style="max-height: 80vh">
@@ -117,8 +117,12 @@ export default {
methods: {
async goPrevEpisode() {
if (this.currentEpisodeIndex - 1 < 0) return
// Remove focus from active input
document.activeElement?.blur?.()
const prevEpisodeId = this.episodeTableEpisodeIds[this.currentEpisodeIndex - 1]
this.processing = true
const prevEpisode = await this.$axios.$get(`/api/podcasts/${this.libraryItem.id}/episode/${prevEpisodeId}`).catch((error) => {
const errorMsg = error.response && error.response.data ? error.response.data : 'Failed to fetch episode'
this.$toast.error(errorMsg)
@@ -134,8 +138,12 @@ export default {
},
async goNextEpisode() {
if (this.currentEpisodeIndex >= this.episodeTableEpisodeIds.length - 1) return
// Remove focus from active input
document.activeElement?.blur?.()
this.processing = true
const nextEpisodeId = this.episodeTableEpisodeIds[this.currentEpisodeIndex + 1]
const nextEpisode = await this.$axios.$get(`/api/podcasts/${this.libraryItem.id}/episode/${nextEpisodeId}`).catch((error) => {
const errorMsg = error.response && error.response.data ? error.response.data : 'Failed to fetch book'
this.$toast.error(errorMsg)
@@ -7,20 +7,21 @@
</template>
<div ref="wrapper" id="podcast-wrapper" class="p-4 w-full text-sm py-2 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden">
<div v-if="episodesCleaned.length" class="w-full py-3 mx-auto flex">
<form @submit.prevent="submit" class="flex flex-grow">
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="flex-grow mr-2 text-sm md:text-base" />
<form @submit.prevent="submit" class="flex grow">
<ui-text-input v-model="search" @input="inputUpdate" type="search" :placeholder="$strings.PlaceholderSearchEpisode" class="grow mr-2 text-sm md:text-base" />
</form>
<ui-btn :padding-x="4" @click="toggleSort">
<span class="pr-4">{{ $strings.LabelSortPubDate }}</span>
<span class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-2">
<span class="material-symbols text-xl" :aria-label="sortDescending ? $strings.LabelSortDescending : $strings.LabelSortAscending">{{ sortDescending ? 'expand_more' : 'expand_less' }}</span>
</span>
</ui-btn>
</div>
<div ref="episodeContainer" id="episodes-scroll" class="w-full overflow-x-hidden overflow-y-auto">
<div
v-for="(episode, index) in episodesList"
:key="index"
class="relative"
:class="getIsEpisodeDownloaded(episode) ? 'bg-primary bg-opacity-40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success bg-opacity-10' : index % 2 == 0 ? 'cursor-pointer bg-primary bg-opacity-25 hover:bg-opacity-40' : 'cursor-pointer bg-primary bg-opacity-5 hover:bg-opacity-25'"
@click="toggleSelectEpisode(episode)"
>
<div v-for="(episode, index) in episodesList" :key="index" class="relative" :class="episode.isDownloaded || episode.isDownloading ? 'bg-primary/40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success/10' : index % 2 == 0 ? 'cursor-pointer bg-primary/25 hover:bg-primary/40' : 'cursor-pointer bg-primary/5 hover:bg-primary/25'" @click="toggleSelectEpisode(episode)">
<div class="absolute top-0 left-0 h-full flex items-center p-2">
<span v-if="getIsEpisodeDownloaded(episode)" class="material-symbols text-success text-xl">download_done</span>
<span v-if="episode.isDownloaded" class="material-symbols text-success text-xl">download_done</span>
<span v-else-if="episode.isDownloading" class="material-symbols text-warning text-xl">download</span>
<ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
</div>
<div class="px-8 py-2">
@@ -34,14 +35,21 @@
<widgets-podcast-type-indicator :type="episode.episodeType" />
</div>
<p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p>
<p class="text-xs text-gray-300">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
<div class="flex items-center space-x-2">
<!-- published -->
<p class="text-xs text-gray-300 w-40">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
<!-- duration -->
<p v-if="episode.durationSeconds && !isNaN(episode.durationSeconds)" class="text-xs text-gray-300 min-w-28">{{ $strings.LabelDuration }}: {{ $elapsedPretty(episode.durationSeconds) }}</p>
<!-- size -->
<p v-if="episode.enclosure?.length && !isNaN(episode.enclosure.length) && Number(episode.enclosure.length) > 0" class="text-xs text-gray-300">{{ $strings.LabelSize }}: {{ $bytesPretty(Number(episode.enclosure.length)) }}</p>
</div>
</div>
</div>
</div>
<div class="flex justify-end pt-4">
<ui-checkbox v-if="!allDownloaded" v-model="selectAll" @input="toggleSelectAll" :label="selectAllLabel" small checkbox-bg="primary" border-color="gray-600" class="mx-8" />
<ui-btn v-if="!allDownloaded" :disabled="!episodesSelected.length" @click="submit">{{ buttonText }}</ui-btn>
<p v-else class="text-success text-base px-2 py-4">All episodes are downloaded</p>
<p v-else class="text-success text-base px-2 py-4">{{ $strings.LabelAllEpisodesDownloaded }}</p>
</div>
</div>
</modals-modal>
@@ -58,6 +66,14 @@ export default {
episodes: {
type: Array,
default: () => []
},
downloadQueue: {
type: Array,
default: () => []
},
episodesDownloading: {
type: Array,
default: () => []
}
},
data() {
@@ -70,7 +86,8 @@ export default {
searchTimeout: null,
searchText: null,
downloadedEpisodeGuidMap: {},
downloadedEpisodeUrlMap: {}
downloadedEpisodeUrlMap: {},
sortDescending: true
}
},
watch: {
@@ -79,6 +96,21 @@ export default {
handler(newVal) {
if (newVal) this.init()
}
},
episodes: {
handler(newVal) {
if (newVal) this.updateEpisodeDownloadStatuses()
}
},
episodesDownloading: {
handler(newVal) {
if (newVal) this.updateEpisodeDownloadStatuses()
}
},
downloadQueue: {
handler(newVal) {
if (newVal) this.updateEpisodeDownloadStatuses()
}
}
},
computed: {
@@ -123,6 +155,17 @@ export default {
}
},
methods: {
toggleSort() {
this.sortDescending = !this.sortDescending
this.episodesCleaned = this.episodesCleaned.toSorted((a, b) => {
if (this.sortDescending) {
return a.publishedAt < b.publishedAt ? 1 : -1
}
return a.publishedAt > b.publishedAt ? 1 : -1
})
this.selectedEpisodes = {}
this.selectAll = false
},
getIsEpisodeDownloaded(episode) {
if (episode.guid && !!this.downloadedEpisodeGuidMap[episode.guid]) {
return true
@@ -132,6 +175,13 @@ export default {
}
return false
},
getIsEpisodeDownloadingOrQueued(episode) {
const episodesToCheck = [...this.episodesDownloading, ...this.downloadQueue]
if (episode.guid) {
return episodesToCheck.some((download) => download.guid === episode.guid)
}
return episodesToCheck.some((download) => this.getCleanEpisodeUrl(download.url) === episode.cleanUrl)
},
/**
* UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
* Fallback to checking the clean url
@@ -173,13 +223,13 @@ export default {
},
toggleSelectAll(val) {
for (const episode of this.episodesList) {
if (this.getIsEpisodeDownloaded(episode)) this.selectedEpisodes[episode.cleanUrl] = false
if (episode.isDownloaded || episode.isDownloading) this.selectedEpisodes[episode.cleanUrl] = false
else this.$set(this.selectedEpisodes, episode.cleanUrl, val)
}
},
checkSetIsSelectedAll() {
for (const episode of this.episodesList) {
if (!this.getIsEpisodeDownloaded(episode) && !this.selectedEpisodes[episode.cleanUrl]) {
if (!episode.isDownloaded && !episode.isDownloading && !this.selectedEpisodes[episode.cleanUrl]) {
this.selectAll = false
return
}
@@ -187,7 +237,7 @@ export default {
this.selectAll = true
},
toggleSelectEpisode(episode) {
if (this.getIsEpisodeDownloaded(episode)) return
if (episode.isDownloaded || episode.isDownloading) return
this.$set(this.selectedEpisodes, episode.cleanUrl, !this.selectedEpisodes[episode.cleanUrl])
this.checkSetIsSelectedAll()
},
@@ -201,8 +251,8 @@ export default {
const sizeInMb = payloadSize / 1024 / 1024
const sizeInMbPretty = sizeInMb.toFixed(2) + 'MB'
console.log('Request size', sizeInMb)
if (sizeInMb > 4.99) {
return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 5Mb`)
if (sizeInMb > 9.99) {
return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 10Mb`)
}
this.processing = true
@@ -223,6 +273,23 @@ export default {
})
},
init() {
this.updateDownloadedEpisodeMaps()
this.episodesCleaned = this.episodes
.filter((ep) => ep.enclosure?.url)
.map((_ep) => {
return {
..._ep,
cleanUrl: this.getCleanEpisodeUrl(_ep.enclosure.url),
isDownloading: this.getIsEpisodeDownloadingOrQueued(_ep),
isDownloaded: this.getIsEpisodeDownloaded(_ep)
}
})
this.episodesCleaned.sort((a, b) => (a.publishedAt < b.publishedAt ? 1 : -1))
this.selectAll = false
this.selectedEpisodes = {}
},
updateDownloadedEpisodeMaps() {
this.downloadedEpisodeGuidMap = {}
this.downloadedEpisodeUrlMap = {}
@@ -230,18 +297,16 @@ export default {
if (episode.guid) this.downloadedEpisodeGuidMap[episode.guid] = episode.id
if (episode.enclosure?.url) this.downloadedEpisodeUrlMap[this.getCleanEpisodeUrl(episode.enclosure.url)] = episode.id
})
this.episodesCleaned = this.episodes
.filter((ep) => ep.enclosure?.url)
.map((_ep) => {
return {
..._ep,
cleanUrl: this.getCleanEpisodeUrl(_ep.enclosure.url)
}
})
this.episodesCleaned.sort((a, b) => (a.publishedAt < b.publishedAt ? 1 : -1))
this.selectAll = false
this.selectedEpisodes = {}
},
updateEpisodeDownloadStatuses() {
this.updateDownloadedEpisodeMaps()
this.episodesCleaned = this.episodesCleaned.map((ep) => {
return {
...ep,
isDownloading: this.getIsEpisodeDownloadingOrQueued(ep),
isDownloaded: this.getIsEpisodeDownloaded(ep)
}
})
}
},
mounted() {}
@@ -52,11 +52,11 @@
</div>
</div>
<div class="flex items-center py-4 px-2">
<div class="flex-grow" />
<div class="grow" />
<div class="px-4">
<ui-checkbox v-model="podcast.autoDownloadEpisodes" :label="$strings.LabelAutoDownloadEpisodes" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-sm md:text-base font-semibold" />
</div>
<ui-btn color="success" @click="submit">{{ $strings.ButtonSubmit }}</ui-btn>
<ui-btn color="bg-success" @click="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</modals-modal>

Some files were not shown because too many files have changed in this diff Show More