Compare commits

...

191 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
mikiher
6ea2ef0845 Remove autoprefixer plugin 2025-03-17 08:16:32 +02:00
mikiher
30db5d50fb Tailwind V4 migration initial commit 2025-03-16 16:41:37 +02: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
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
261 changed files with 5005 additions and 2277 deletions

48
.github/workflows/component-tests.yml vendored Normal file
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

View File

@@ -23,7 +23,7 @@ on:
jobs:
build:
if: ${{ !contains(github.event.head_commit.message, 'skip ci') && github.repository == 'advplyr/audiobookshelf' }}
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Check out

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ sw.*
.DS_STORE
.idea/*
tailwind.compiled.css
tailwind.config.js

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;
}

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
}
},
@@ -215,6 +224,8 @@ export default {
this.batchAutoMatchClick()
} else if (action === 'rescan') {
this.batchRescan()
} else if (action === 'download') {
this.batchDownload()
}
},
async batchRescan() {
@@ -241,6 +252,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)

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">

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>

View File

@@ -1,36 +1,36 @@
<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>
</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>
</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>
</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
@@ -39,10 +39,10 @@
/>
</svg>
</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 +52,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 +68,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 +83,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>

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>

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>

View File

@@ -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>
@@ -156,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

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>

View File

@@ -4,7 +4,7 @@
<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'">
<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'">
<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>
@@ -14,7 +14,7 @@
<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,7 +22,7 @@
<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'">
<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'">
<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>
@@ -32,7 +32,7 @@
<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'">
<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'">
<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>
@@ -42,7 +42,7 @@
<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 +50,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,7 +58,7 @@
<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'">
<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'">
<svg class="w-6 h-6" viewBox="0 0 24 24">
<path
fill="currentColor"
@@ -71,7 +71,7 @@
<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 +79,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 +87,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 +95,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,13 +103,13 @@
<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>

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>

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>

View File

@@ -1,23 +1,23 @@
<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 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 +27,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>

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>

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>

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>

View File

@@ -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>

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,7 +61,7 @@
<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">
<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 :text="nonInteractionLabel" />
</div>
</div>

View File

@@ -1,15 +1,15 @@
<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 ref="card" :id="`album-card-${index}`" :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-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` }">
<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>

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,12 +68,12 @@
</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>
@@ -94,19 +94,19 @@
</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>
</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>
@@ -223,8 +223,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)
@@ -430,8 +429,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')
}

View File

@@ -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>

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>

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>

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>

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">&#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>

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>

View File

@@ -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>

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>

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>

View File

@@ -1,10 +1,10 @@
<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>
@@ -12,34 +12,34 @@
</div>
</div>
<div v-if="publishedYear" role="paragraph" 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 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" role="paragraph" 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 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" role="paragraph" 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 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)" role="paragraph" 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 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 role="paragraph" 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 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 }}

View File

@@ -1,7 +1,7 @@
<template>
<div ref="wrapper" class="relative" v-click-outside="clickOutside">
<div class="relative h-9">
<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">{{ 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 max-h-96 rounded-md py-1 text-sm ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<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="menuitem" @click="clickedOption(item)">

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>

View File

@@ -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)">

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 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="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">

View File

@@ -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>

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>

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>

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

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>

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'

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>

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: {

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>

View File

@@ -23,8 +23,8 @@
<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>

View File

@@ -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>

View File

@@ -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>

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>

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" />

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>

View File

@@ -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,7 +7,7 @@
<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">

View File

@@ -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">
@@ -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>

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>
@@ -163,4 +163,4 @@ export default {
this.$eventBus.$off('showing-prompt', this.showingPrompt)
}
}
</script>
</script>

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>

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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -4,12 +4,12 @@
<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>

View File

@@ -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-4 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>
</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>

View File

@@ -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>

View File

@@ -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>

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>

View File

@@ -2,37 +2,37 @@
<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 md:self-start">
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](libraryItemId, libraryItemUpdatedAt, true)" :width="120" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<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>
@@ -50,13 +50,13 @@
</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">
<div class="w-48 grow p-1">
<ui-dropdown v-model="provider" :items="providers" :label="$strings.LabelProvider" small />
</div>
<div class="w-72 flex-grow p-1">
<div class="w-72 grow p-1">
<ui-text-input-with-label v-model="searchTitle" :label="searchTitleLabel" :placeholder="$strings.PlaceholderSearch" />
</div>
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 flex-grow p-1">
<div v-show="provider != 'itunes' && provider != 'audiobookcovers'" class="w-72 grow p-1">
<ui-text-input-with-label v-model="searchAuthor" :label="$strings.LabelAuthor" />
</div>
<ui-btn class="mt-5 ml-1 md:min-w-24" :padding-x="4" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
@@ -79,7 +79,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>
@@ -157,6 +157,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 || {}
},

View File

@@ -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>

View File

@@ -5,7 +5,7 @@
<div 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,63 +57,63 @@
</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">
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white/60">
{{ $strings.LabelCurrently }} <a title="$strings.LabelClickToUseCurrentValue" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('author', mediaMetadata.authorName)">{{ 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">
<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">
<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">
<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">
<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>
@@ -121,54 +121,54 @@
<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>

View File

@@ -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>

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 || []

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -4,12 +4,12 @@
<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>

View File

@@ -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">

View File

@@ -7,18 +7,18 @@
</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="episode.isDownloaded || episode.isDownloading ? '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="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>
@@ -42,7 +42,7 @@
<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>
@@ -79,7 +79,8 @@ export default {
searchTimeout: null,
searchText: null,
downloadedEpisodeGuidMap: {},
downloadedEpisodeUrlMap: {}
downloadedEpisodeUrlMap: {},
sortDescending: true
}
},
watch: {
@@ -147,6 +148,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

View File

@@ -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>

View File

@@ -32,8 +32,8 @@
</div>
</div>
<div class="flex items-center py-4">
<div class="flex-grow" />
<ui-btn color="success" @click="submit">{{ $strings.ButtonAddPodcasts }}</ui-btn>
<div class="grow" />
<ui-btn color="bg-success" @click="submit">{{ $strings.ButtonAddPodcasts }}</ui-btn>
</div>
</div>
</modals-modal>

View File

@@ -11,7 +11,7 @@
{{ $getString('MessageConfirmRemoveEpisode', [episodeTitle]) }}
</p>
<p v-else class="text-lg text-gray-200 mb-4">{{ $getString('MessageConfirmRemoveEpisodes', [episodes.length]) }}</p>
<p class="text-xs font-semibold text-warning text-opacity-90">Note: This does not delete the audio file unless toggling "Hard delete file"</p>
<p class="text-xs font-semibold text-warning/90">Note: This does not delete the audio file unless toggling "Hard delete file"</p>
</div>
<div class="flex justify-between items-center pt-4">
<ui-checkbox v-model="hardDeleteFile" :label="$strings.LabelHardDeleteFile" check-color="error" checkbox-bg="bg" small label-class="text-base text-gray-200 pl-3" />

View File

@@ -10,7 +10,7 @@
<div class="w-12 h-12">
<covers-book-cover :library-item="libraryItem" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>
<div class="flex-grow px-2">
<div class="grow px-2">
<p class="text-base mb-1">{{ podcastTitle }}</p>
<p class="text-xs text-gray-300">{{ podcastAuthor }}</p>
</div>
@@ -22,13 +22,13 @@
<div class="w-full h-px bg-white/5 my-4" />
<div class="flex items-center">
<div class="flex-grow">
<div class="grow">
<p class="font-semibold text-xs mb-1">{{ $strings.LabelFilename }}</p>
<p class="mb-2 text-xs">
{{ audioFileFilename }}
</p>
</div>
<div class="flex-grow">
<div class="grow">
<p class="font-semibold text-xs mb-1">{{ $strings.LabelSize }}</p>
<p class="mb-2 text-xs">
{{ audioFileSize }}

View File

@@ -15,7 +15,7 @@
<div v-if="!isProcessing && searchedTitle && !episodesFound.length" class="w-full py-8">
<p class="text-center text-lg">{{ $strings.MessageNoEpisodeMatchesFound }}</p>
</div>
<div v-for="(episode, index) in episodesFound" :key="index" class="w-full py-4 border-b border-white border-opacity-5 hover:bg-gray-300 hover:bg-opacity-10 cursor-pointer px-2" @click.stop="selectEpisode(episode)">
<div v-for="(episode, index) in episodesFound" :key="index" class="w-full py-4 border-b border-white/5 hover:bg-gray-300/10 cursor-pointer px-2" @click.stop="selectEpisode(episode)">
<p v-if="episode.episode" class="font-semibold text-gray-200">#{{ episode.episode }}</p>
<p class="break-words mb-1">{{ episode.title }}</p>
<p v-if="episode.subtitle" class="mb-1 text-sm text-gray-300 line-clamp-2">{{ episode.subtitle }}</p>

View File

@@ -16,19 +16,19 @@
<div v-if="currentFeed.meta" class="mt-5">
<div class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedPreventIndexing }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedPreventIndexing }}</span>
</div>
<div>{{ currentFeed.meta.preventIndexing ? 'Yes' : 'No' }}</div>
</div>
<div v-if="currentFeed.meta.ownerName" class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerName }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerName }}</span>
</div>
<div>{{ currentFeed.meta.ownerName }}</div>
</div>
<div v-if="currentFeed.meta.ownerEmail" class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerEmail }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerEmail }}</span>
</div>
<div>{{ currentFeed.meta.ownerEmail }}</div>
</div>
@@ -47,9 +47,9 @@
<p v-if="hasEpisodesWithoutPubDate" class="w-full pt-2 text-warning text-xs">{{ $strings.NoteRSSFeedPodcastAppsPubDate }}</p>
</div>
<div v-show="userIsAdminOrUp" class="flex items-center pt-6">
<div class="flex-grow" />
<ui-btn v-if="currentFeed" color="error" small @click="closeFeed">{{ $strings.ButtonCloseFeed }}</ui-btn>
<ui-btn v-else color="success" small @click="openFeed">{{ $strings.ButtonOpenFeed }}</ui-btn>
<div class="grow" />
<ui-btn v-if="currentFeed" color="bg-error" small @click="closeFeed">{{ $strings.ButtonCloseFeed }}</ui-btn>
<ui-btn v-else color="bg-success" small @click="openFeed">{{ $strings.ButtonOpenFeed }}</ui-btn>
</div>
</div>
</modals-modal>

View File

@@ -11,26 +11,26 @@
<div v-if="feed.meta" class="mt-5">
<div class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedPreventIndexing }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedPreventIndexing }}</span>
</div>
<div>{{ feed.meta.preventIndexing ? 'Yes' : 'No' }}</div>
</div>
<div v-if="feed.meta.ownerName" class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerName }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerName }}</span>
</div>
<div>{{ feed.meta.ownerName }}</div>
</div>
<div v-if="feed.meta.ownerEmail" class="flex py-0.5">
<div class="w-48">
<span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerEmail }}</span>
<span class="text-white/60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerEmail }}</span>
</div>
<div>{{ feed.meta.ownerEmail }}</div>
</div>
</div>
<!-- -->
<div class="episodesTable mt-2">
<div class="bg-primary bg-opacity-40 h-12 header">
<div class="bg-primary/40 h-12 header">
{{ $strings.LabelEpisodeTitle }}
</div>
<div class="scroller">

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex justify-center pt-4 pb-2 lg:pt-0 lg:pb-2">
<div class="flex items-center justify-center flex-grow">
<div class="flex items-center justify-center grow">
<template v-if="!loading">
<ui-tooltip direction="top" :text="$strings.ButtonPreviousChapter" class="mr-4 lg:mr-8">
<button :aria-label="$strings.ButtonPreviousChapter" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="prevChapter">
@@ -12,7 +12,7 @@
<span class="material-symbols text-2xl sm:text-3xl">replay</span>
</button>
</ui-tooltip>
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<button :aria-label="paused ? $strings.ButtonPlay : $strings.ButtonPause" class="p-2 shadow-xs bg-accent flex items-center justify-center rounded-full text-primary mx-4 lg:mx-8" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPause">
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
</button>
<ui-tooltip direction="top" :text="jumpForwardText">
@@ -27,7 +27,7 @@
</ui-tooltip>
</template>
<template v-else>
<div class="cursor-pointer p-2 shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
<div class="cursor-pointer p-2 shadow-xs bg-accent flex items-center justify-center rounded-full text-primary mx-8 animate-spin">
<span class="material-symbols text-2xl">autorenew</span>
</div>
</template>

View File

@@ -6,11 +6,11 @@
<div ref="bufferTrack" class="h-full bg-gray-500 absolute top-0 left-0 pointer-events-none" />
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
<div ref="trackCursor" class="h-full w-0.5 bg-gray-50 absolute top-0 left-0 opacity-0 pointer-events-none" />
<div v-if="loading" class="h-full w-1/4 absolute left-0 top-0 loadingTrack pointer-events-none bg-white bg-opacity-25" />
<div v-if="loading" class="h-full w-1/4 absolute left-0 top-0 loadingTrack pointer-events-none bg-white/25" />
</div>
<div class="w-full h-2 relative overflow-hidden" :class="useChapterTrack ? 'opacity-0' : ''">
<template v-for="(tick, index) in chapterTicks">
<div :key="index" :style="{ left: tick.left + 'px' }" class="absolute top-0 w-px bg-white bg-opacity-30 h-1 pointer-events-none" />
<div :key="index" :style="{ left: tick.left + 'px' }" class="absolute top-0 w-px bg-white/30 h-1 pointer-events-none" />
</template>
</div>
@@ -74,6 +74,9 @@ export default {
currentChapterStart() {
if (!this.currentChapter) return 0
return this.currentChapter.start
},
isMobile() {
return this.$store.state.globals.isMobile
}
},
methods: {
@@ -145,6 +148,9 @@ export default {
})
},
mousemoveTrack(e) {
if (this.isMobile) {
return
}
const offsetX = e.offsetX
const baseTime = this.useChapterTrack ? this.currentChapterStart : 0
@@ -198,6 +204,7 @@ export default {
setTrackWidth() {
if (this.$refs.track) {
this.trackWidth = this.$refs.track.clientWidth
this.trackOffsetLeft = this.$refs.track.getBoundingClientRect().left
} else {
console.error('Track not loaded', this.$refs)
}
@@ -218,4 +225,4 @@ export default {
window.removeEventListener('resize', this.windowResize)
}
}
</script>
</script>

View File

@@ -31,13 +31,13 @@
</ui-tooltip>
<ui-tooltip v-if="playerQueueItems.length" direction="top" :text="$strings.LabelViewQueue">
<button :aria-label="$strings.LabelViewQueue" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
<button :aria-label="$strings.LabelViewQueue" class="outline-hidden text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')">
<span class="material-symbols text-2.5xl sm:text-3xl">playlist_play</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="$strings.LabelViewPlayerSettings">
<button :aria-label="$strings.LabelViewPlayerSettings" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="showPlayerSettings">
<button :aria-label="$strings.LabelViewPlayerSettings" class="outline-hidden text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="showPlayerSettings">
<span class="material-symbols text-2xl sm:text-2.5xl">settings_slow_motion</span>
</button>
</ui-tooltip>
@@ -49,7 +49,7 @@
<player-track-bar ref="trackbar" :loading="loading" :chapters="chapters" :duration="duration" :current-chapter="currentChapter" :playback-rate="playbackRate" @seek="seek" />
<div class="relative flex items-center justify-between">
<div class="flex-grow flex items-center">
<div class="grow flex items-center">
<p ref="currentTimestamp" class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">00:00:00</p>
<p class="font-mono text-sm hidden sm:block text-gray-100 pointer-events-auto">&nbsp;/&nbsp;{{ progressPercent }}%</p>
</div>
@@ -58,7 +58,7 @@
{{ currentChapterName }} <span v-if="useChapterTrack" class="text-xs text-gray-400">&nbsp;({{ $getString('LabelPlayerChapterNumberMarker', [currentChapterIndex + 1, chapters.length]) }})</span>
</p>
</div>
<div class="flex-grow flex items-center justify-end">
<div class="grow flex items-center justify-end">
<p class="font-mono text-xxs sm:text-sm text-gray-100 pointer-events-auto">{{ timeRemainingPretty }}</p>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<div ref="wrapper" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-75 flex items-center justify-center z-60 opacity-0">
<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" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary/75 flex items-center justify-center z-60 opacity-0">
<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" />
<div ref="content" class="relative text-white" :style="{ height: modalHeight, width: modalWidth }" v-click-outside="clickedOutside">
<div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300">
<p id="confirm-prompt-message" class="text-lg mb-6 mt-2 px-1" v-html="message" />
@@ -8,10 +8,10 @@
<ui-checkbox v-if="checkboxLabel" v-model="checkboxValue" checkbox-bg="bg" :label="checkboxLabel" label-class="pl-2 text-base" class="mb-6 px-1" />
<div class="flex px-1 items-center">
<ui-btn v-if="isYesNo" color="primary" @click="nevermind">{{ $strings.ButtonCancel }}</ui-btn>
<div class="flex-grow" />
<ui-btn v-if="isYesNo" :color="yesButtonColor" @click="confirm">{{ yesButtonText }}</ui-btn>
<ui-btn v-else color="primary" @click="confirm">{{ $strings.ButtonOk }}</ui-btn>
<ui-btn v-if="isYesNo" color="bg-primary" @click="nevermind">{{ $strings.ButtonCancel }}</ui-btn>
<div class="grow" />
<ui-btn v-if="isYesNo" :color="`bg-${yesButtonColor}`" @click="confirm">{{ yesButtonText }}</ui-btn>
<ui-btn v-else color="bg-primary" @click="confirm">{{ $strings.ButtonOk }}</ui-btn>
</div>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<div ref="wrapper" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-75 flex items-center justify-center z-40 opacity-0">
<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" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary/75 flex items-center justify-center z-40 opacity-0">
<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" />
<div ref="content" style="min-width: 400px; min-height: 200px" class="relative text-white" :style="{ height: modalHeight, width: modalWidth }">
<slot />
</div>
@@ -80,4 +80,4 @@ export default {
this.el.remove()
}
}
</script>
</script>

View File

@@ -35,12 +35,12 @@
<div class="w-full h-full relative">
<div v-show="canGoPrev" ref="prevButton" class="absolute top-0 left-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
<div class="flex items-center justify-center h-full w-1/2">
<span v-show="loadedFirstPage" class="material-symbols text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
<span v-show="loadedFirstPage" class="material-symbols text-5xl text-white/30 cursor-pointer hover:text-white/90">arrow_back_ios</span>
</div>
</div>
<div v-show="canGoNext" ref="nextButton" class="absolute top-0 right-0 h-full w-1/2 lg:w-1/3 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="next" @mousedown.prevent>
<div class="flex items-center justify-center h-full w-1/2 ml-auto">
<span v-show="loadedFirstPage" class="material-symbols text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
<span v-show="loadedFirstPage" class="material-symbols text-5xl text-white/30 cursor-pointer hover:text-white/90">arrow_forward_ios</span>
</div>
</div>
<div ref="imageContainer" class="w-full h-full relative overflow-auto">

View File

@@ -1,7 +1,7 @@
<template>
<div class="w-full h-full">
<div class="h-full max-h-full w-full">
<div class="ebook-viewer absolute overflow-y-scroll left-0 right-0 top-16 w-full max-w-4xl m-auto z-10 border border-black border-opacity-20 shadow-md bg-white">
<div class="ebook-viewer absolute overflow-y-scroll left-0 right-0 top-16 w-full max-w-4xl m-auto z-10 border border-black/20 shadow-md bg-white">
<iframe title="html-viewer" width="100%"> Loading </iframe>
</div>
</div>
@@ -136,4 +136,4 @@ export default {
.ebook-viewer {
height: calc(100% - 96px);
}
</style>
</style>

View File

@@ -2,12 +2,12 @@
<div class="w-full h-full pt-20 relative">
<div v-show="canGoPrev" class="absolute top-0 left-0 h-full w-1/2 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
<div class="flex items-center justify-center h-full w-1/2">
<span class="material-symbols text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
<span class="material-symbols text-5xl text-white/30 cursor-pointer hover:text-white/90">arrow_back_ios</span>
</div>
</div>
<div v-show="canGoNext" class="absolute top-0 right-0 h-full w-1/2 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="next" @mousedown.prevent>
<div class="flex items-center justify-center h-full w-1/2 ml-auto">
<span class="material-symbols text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
<span class="material-symbols text-5xl text-white/30 cursor-pointer hover:text-white/90">arrow_forward_ios</span>
</div>
</div>
@@ -23,7 +23,7 @@
<div class="flex items-center justify-center">
<div :style="{ width: pdfWidth + 'px', height: pdfHeight + 'px' }" class="overflow-auto">
<div v-if="loadedRatio > 0 && loadedRatio < 1" style="background-color: green; color: white; text-align: center" :style="{ width: loadedRatio * 100 + '%' }">{{ Math.floor(loadedRatio * 100) }}%</div>
<pdf v-if="pdfDocInitParams" ref="pdf" class="m-auto z-10 border border-black border-opacity-20 shadow-md" :src="pdfDocInitParams" :page="page" :rotate="rotate" @progress="progressEvt" @error="error" @num-pages="numPagesLoaded" @link-clicked="page = $event" @loaded="loadedEvt"></pdf>
<pdf v-if="pdfDocInitParams" ref="pdf" class="m-auto z-10 border border-black/20 shadow-md" :src="pdfDocInitParams" :page="page" :rotate="rotate" @progress="progressEvt" @error="error" @num-pages="numPagesLoaded" @link-clicked="page = $event" @loaded="loadedEvt"></pdf>
</div>
</div>
</div>

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