Compare commits

...

176 Commits

Author SHA1 Message Date
advplyr
e81b3461b2 Version bump v2.12.2 2024-08-08 17:17:22 -05:00
advplyr
9345cb3934 Update version check get changelogs 2024-08-08 17:04:50 -05:00
advplyr
eb36a0b3dd Merge pull request #3247 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-08-08 16:58:06 -05:00
advplyr
7e442ecb3d Revert MemoryStore used in expressSession 2024-08-08 16:54:48 -05:00
tonttula
f07c5eb725 Translated using Weblate (Finnish)
Currently translated at 32.2% (275 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-08-08 23:54:39 +02:00
SunSpring
a486be92cb Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (853 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-08-08 23:54:39 +02:00
tonttula
4d84060036 Translated using Weblate (Finnish)
Currently translated at 26.2% (224 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-08-08 23:54:39 +02:00
tonttula
fc503691fe Translated using Weblate (Finnish)
Currently translated at 25.9% (221 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-08-08 23:54:39 +02:00
Charlie
c80dd43a3e Translated using Weblate (French)
Currently translated at 99.7% (851 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-08-08 23:54:39 +02:00
Charlie
a4a62e0c18 Translated using Weblate (French)
Currently translated at 99.7% (851 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-08-08 23:54:39 +02:00
advplyr
2f98cb9b6d Update:Changelog shows all releases matching minor version and lower than current #3250 2024-08-07 17:56:55 -05:00
advplyr
91dc6eebb0 Merge pull request #3254 from mikiher/unc-path-support
Fix path normalization to support UNC paths
2024-08-07 15:27:53 -05:00
mikiher
d72e0a4418 Fix path normalization to support UNC paths 2024-08-07 21:18:53 +03:00
advplyr
2c8ebd43cc Update ApiCacheManager unit test 2024-08-06 17:26:36 -05:00
advplyr
9f561aa296 Update:Skip library api cache for random sort #3249 2024-08-06 17:20:53 -05:00
advplyr
930bacd45d Fix:Podcast episode download request failing due to user-agent string #3246 2024-08-05 16:59:37 -05:00
advplyr
45c97a778d Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-08-05 09:01:53 -05:00
advplyr
6ebc64f73b Version bump v2.12.1 2024-08-05 09:01:45 -05:00
advplyr
52807d0d49 Merge pull request #3233 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-08-05 08:58:13 -05:00
SunSpring
a5e18e99bc Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (853 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2024-08-05 15:55:41 +02:00
Charlie
f545b3e745 Translated using Weblate (French)
Currently translated at 99.7% (851 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-08-05 15:55:41 +02:00
Vito0912
e0877803e3 Translated using Weblate (German)
Currently translated at 100.0% (853 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-05 15:55:41 +02:00
advplyr
4916887c8d Merge pull request #3236 from devnoname120/arm64/fix-unicode-so-load
Fix unicode.so loading
2024-08-05 08:55:37 -05:00
Paul
20eb573897 Fix unicode.so loading
`unicode.so` is dynamically linked against glibc but Alpine only comes with musl. Installing the compatibility layer fixes it.

Fix https://github.com/advplyr/audiobookshelf/issues/3231
Fix https://github.com/advplyr/audiobookshelf/issues/3234
Close https://github.com/advplyr/audiobookshelf/pull/3235
2024-08-05 13:34:56 +02:00
advplyr
8ff7b6b6e6 Add server log for process.platform and process.arch #3231 2024-08-04 17:08:55 -05:00
advplyr
06eaee8909 Fix:Binary manager dylib file ext check #3231 2024-08-04 16:51:07 -05:00
advplyr
8f9487ba70 Version bump v2.12.0 2024-08-04 16:36:38 -05:00
advplyr
eca51457b7 Update jsdocs and auto-formatting 2024-08-04 16:13:40 -05:00
advplyr
15c6fce648 Remove duplicate dependency ms 2024-08-04 12:52:52 -05:00
advplyr
6c872263c6 Fix:Show changelog when clicking version on config side rail #3232 2024-08-04 12:30:18 -05:00
advplyr
4d3b3d1740 Update:Replace default express-session MemoryStore with stable MemoryStore #2538 2024-08-04 12:00:10 -05:00
advplyr
bba8920855 Merge pull request #3228 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-08-04 10:10:02 -05:00
gallegonovato
f56b9487ff Translated using Weblate (Spanish)
Currently translated at 100.0% (853 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-08-04 00:09:30 +02:00
Mario
1946d8296b Translated using Weblate (German)
Currently translated at 100.0% (853 of 853 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-04 00:09:29 +02:00
burghy86
41e5d7f820 Translated using Weblate (Italian)
Currently translated at 100.0% (852 of 852 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/it/
2024-08-04 00:09:28 +02:00
weblate.user.1274
2507568103 Translated using Weblate (Norwegian Bokmål)
Currently translated at 83.4% (711 of 852 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nb_NO/
2024-08-04 00:09:27 +02:00
gallegonovato
19733798fa Translated using Weblate (Spanish)
Currently translated at 100.0% (852 of 852 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-08-04 00:09:26 +02:00
Mario
427d6da360 Translated using Weblate (German)
Currently translated at 100.0% (852 of 852 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-04 00:09:25 +02:00
Mario
2b67d3d1c5 Translated using Weblate (German)
Currently translated at 100.0% (850 of 850 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-04 00:09:24 +02:00
gallegonovato
6926a40ad6 Translated using Weblate (Spanish)
Currently translated at 100.0% (850 of 850 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-08-04 00:09:24 +02:00
Illia Pyshniak
7a8da5bf3a Translated using Weblate (Ukrainian)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2024-08-04 00:09:23 +02:00
gallegonovato
fc8fa17c6f Translated using Weblate (Spanish)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-08-04 00:09:22 +02:00
Mario
0a88659a9f Translated using Weblate (German)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-04 00:09:21 +02:00
Illia Pyshniak
9967858c44 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/uk/
2024-08-04 00:09:21 +02:00
Mario
e2ce388f90 Translated using Weblate (German)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-08-04 00:09:20 +02:00
gallegonovato
f31649f1d2 Translated using Weblate (Spanish)
Currently translated at 100.0% (849 of 849 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-08-04 00:09:20 +02:00
advplyr
a55c167dde Fix:Cleanup media progress when deleting podcasts, remove usage of old user model 2024-08-03 17:09:17 -05:00
advplyr
642cf232ba Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-08-03 15:08:45 -05:00
advplyr
164b4525c4 Replace function for cleaning up user seriesHideFromContinueListening to not use old model 2024-08-03 15:08:03 -05:00
advplyr
39c26d2bee Merge pull request #3217 from ic1415/LibraryItemController
Update LibraryItemController.js
2024-08-02 16:33:23 -05:00
advplyr
2a69955cc1 Update server/controllers/LibraryItemController.js 2024-08-02 16:30:21 -05:00
advplyr
4a5345dd5d Update:devcontainer dev.js default to not skip binaries check, fail gracefully if required binary env variables are not set when skipping 2024-08-01 14:25:57 -05:00
advplyr
1e6dd0e3e0 Add jsdocs for Ffmpeg and tools controller 2024-07-31 17:32:51 -05:00
advplyr
91cca2e358 Merge pull request #3214 from faush01/feature/persist_encoding_options
persist and show the advanced encoding options
2024-07-31 16:52:36 -05:00
advplyr
816a9be618 Merge pull request #3208 from nichwall/release_issue_workflow
Add: workflow to close issues on release
2024-07-31 16:07:24 -05:00
ic1415
9eb0ec76fe Update LibraryItemController.js
update library item controller to log downloaded ebooks to fix #3215
2024-07-31 10:48:41 -04:00
Shaun
49054d5239 persist the advanced encoding options, show the encoding options used with in progress encodes 2024-07-31 16:44:24 +10:00
advplyr
787c4e45a8 Merge pull request #3212 from mikiher/library-fetch
On item pages, fetch the item's library data to the store if it's not available
2024-07-30 16:52:51 -05:00
advplyr
34cb7a4d02 Remove unused loadLibraryFilterData func 2024-07-30 16:49:02 -05:00
advplyr
006241163b Replace setCurrentLibrary calls with fetch to ensure filterData matches library 2024-07-30 16:35:26 -05:00
advplyr
03818fadee Remove unnecessary setCurrentLibrary on mounted item page 2024-07-30 16:20:36 -05:00
mikiher
897c3ea625 on item pages, fetch item's library data if unavailable 2024-07-30 20:02:03 +03:00
Nicholas Wallace
73e4293f04 Fix: label has space in name 2024-07-29 18:08:40 -07:00
Nicholas Wallace
6f5ffcb1f8 Add: workflow to close issues on release 2024-07-29 18:06:05 -07:00
advplyr
ed70f3af83 Fix:Material symbols icon to use check instead of checkmark 2024-07-29 17:34:05 -05:00
advplyr
73196f9be8 Update:Match tab support clicking current value to set it #3200 2024-07-29 17:31:52 -05:00
advplyr
a77f4e9d77 Merge pull request #3204 from mikiher/embed-permissions
Fix permission issues in embed/merge
2024-07-29 16:49:12 -05:00
mikiher
294490f814 Fix permission issues in embed/merge 2024-07-29 20:19:58 +03:00
advplyr
6183001fca Merge pull request #3199 from mikiher/unaccent
Support accent-insensitive search using SQLean unicode sqlite3 extension
2024-07-28 17:21:32 -05:00
advplyr
3ac604c665 Remove ffmpeg binaries install step from debian preinst script 2024-07-28 16:55:45 -05:00
advplyr
e342b07cd0 Update:OPF metadata parser supports namespaces on creator and identifier tags #3201 2024-07-28 14:54:17 -05:00
advplyr
b524cbd1b3 Update:Parse epub cover image uses cover specified in opf meta #3201 2024-07-28 14:34:31 -05:00
advplyr
88693d73bd Fix:Shares not working with timeouts longer than 23 days #3164 2024-07-27 17:40:51 -05:00
mikiher
2c453a34ee Remove redundant console.log() message 2024-07-27 23:09:46 +03:00
mikiher
3d2b2e43b1 Set execution permission for downloaded binaries 2024-07-27 23:03:40 +03:00
mikiher
c3f3fca896 Remove dependency on libs/ffbinaries from BinaryManager test 2024-07-27 22:44:01 +03:00
mikiher
dedf6e5d4b Support accent-insensitive matching using the sqlean sqlite3 unicode extension 2024-07-27 21:56:07 +03:00
mikiher
6c379fc3a7 Add /unicode* to .gitignore 2024-07-27 21:52:33 +03:00
mikiher
329e9c9eb2 BinaryManager support for libraries and downloading from github release assets 2024-07-27 21:51:31 +03:00
advplyr
ee53086444 Update chapter modal colors for consistency 2024-07-26 17:36:39 -05:00
advplyr
43d6c6678f Add:Random library sorting option for libraries and series #3166
- Fixed author sort and match button not showing
2024-07-25 16:10:42 -05:00
advplyr
82f136ba79 Merge pull request #3195 from mikiher/remove-match-logic
Simplify ItemSearchCard component by removing matching logic
2024-07-25 15:46:12 -05:00
mikiher
e40d3dd64d Simplify ItemSearchCard component 2024-07-25 09:40:18 +03:00
advplyr
a5897fd64b Fix:Set series and collection RSS feed cover image using first item with cover #3193 2024-07-24 16:40:45 -05:00
advplyr
e786e3c057 Remove references to matrix server 2024-07-23 16:08:47 -05:00
advplyr
d347645475 Update:Format numbers on user stats page #3187 2024-07-22 17:43:42 -05:00
advplyr
215b78c162 Merge pull request #3186 from nichwall/author_image_restore
Ensure author and items folder is created before restoring backup
2024-07-22 16:41:55 -05:00
Nicholas Wallace
ee271519f9 Ensure author folder is created before extracting files 2024-07-21 18:04:46 +00:00
advplyr
b350277bbc Remove unused files 2024-07-21 11:12:17 -05:00
advplyr
604ae080ac Merge pull request #3185 from mikiher/genre-search
Adds genres to gloabl search
2024-07-21 11:07:43 -05:00
advplyr
a191dab359 Show numItems and numBooks on search cards 2024-07-21 11:07:54 -05:00
advplyr
3223011b13 Fix:Switching libraries on search page losing q query string 2024-07-21 10:52:23 -05:00
advplyr
f746e246e4 Merge pull request #3184 from brendans-bits/optionalDependencies
Moved cypress to `optionalDependencies`
2024-07-21 09:54:05 -05:00
mikiher
0476b68585 fix: Encode search query parameter in search.vue 2024-07-21 14:00:55 +03:00
mikiher
ec395bed72 fix: Encode search query parameter in GlobalSearch.vue 2024-07-21 13:53:10 +03:00
mikiher
bff56220c2 Adds genres to gloabl search 2024-07-21 11:10:05 +03:00
Brendan
3006405a52 Moved cypress to optionalDependencies 2024-07-21 11:30:50 +10:00
advplyr
9cd0ac80b1 Merge pull request #3182 from mikiher/ffmpeg-progress
Add progress and fixes to m4b and embed tools
2024-07-20 15:31:03 -05:00
mikiher
da51d38ba2 Improve documentation and arg names in TrackProgressMonitor.js 2024-07-20 21:42:58 +03:00
advplyr
5ba6459069 Merge pull request #3179 from nichwall/api_spec_opml
Update: podcast opml endpoints
2024-07-20 11:02:15 -05:00
mikiher
75899242fd put MessageEmbedFailed in akphabetic order. 2024-07-20 13:05:03 +03:00
mikiher
7faf42d892 Merge branch 'advplyr:master' into ffmpeg-progress 2024-07-20 12:28:47 +03:00
mikiher
10f5f331d7 Fixes + add progress to m4b and embed tools 2024-07-20 12:28:06 +03:00
Nicholas Wallace
b1414388e1 Add: podcast tags to opml endpoints 2024-07-20 02:42:41 +00:00
Nicholas Wallace
eb0f5b2e1b Update: podcast opml endpoints 2024-07-20 02:38:56 +00:00
advplyr
7af02ad2e2 Fix:Series bookshelf row padding when using ignore prefixes setting #3169 2024-07-19 17:12:12 -05:00
advplyr
8330dabc46 Merge branch 'master' of https://github.com/advplyr/audiobookshelf 2024-07-18 16:43:10 -05:00
advplyr
dbc7ad0b3b Fix:Podcast episode match not properly encoding search query #3177 2024-07-18 16:43:00 -05:00
advplyr
c0fd24770e Merge pull request #3170 from ajvgwu/nfo-metadata-language
Parse book language from NFO metadata source
2024-07-18 16:14:48 -05:00
advplyr
4289fe4990 Update Nfo scanner and auto-formatting 2024-07-18 16:09:40 -05:00
advplyr
e925e9b23f Update:Increase size of more menu icon on items page 2024-07-18 16:06:37 -05:00
Alex
71cd86fdd5 Merge branch 'advplyr:master' into nfo-metadata-language 2024-07-18 13:26:05 -04:00
advplyr
03be947ad6 Merge pull request #3163 from ajyey/feature/disable-max-backup-size
Adds support for allowing backups of unlimited size
2024-07-17 17:09:37 -05:00
advplyr
96f9084f2e Update:Disable axios progress indicator for sync requests 2024-07-17 17:11:57 -05:00
advplyr
bbccfcbd12 Merge pull request #3165 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2024-07-17 16:57:52 -05:00
Alex
9a697f48db feat: parse language from NFO metadata source 2024-07-17 11:50:27 -04:00
advplyr
37ad1cced2 Fix:Large OPML import timeouts #3118
- Added OPML Api endpoints for /parse and /create, removed old
- Show task for OPML import and create failed tasks for failed feeds
2024-07-16 17:05:52 -05:00
Mario
26db20f63d Translated using Weblate (German)
Currently translated at 100.0% (848 of 848 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 16:47:20 +02:00
Andrew Leonard
ff788e3591 feat: adds better conditional for max backup size 2024-07-16 01:31:12 -04:00
Andrew Leonard
4b482488de feat: remember setting of 0 on server side 2024-07-16 01:30:00 -04:00
Andrew Leonard
e230b6640f feat: adds unlimited text to text label 2024-07-16 01:11:20 -04:00
Andrew Leonard
2bc949fae3 feat: adds support for allowing backups of unlimited size 2024-07-15 23:58:05 -04:00
advplyr
b1bc472205 Update:Incrase icon font size for more context menu and player loading 2024-07-15 17:38:13 -05:00
gallegonovato
5c7a38c292 Translated using Weblate (Spanish)
Currently translated at 100.0% (848 of 848 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-07-16 00:35:43 +02:00
Petteri Hjort
bbd6c51eb6 Translated using Weblate (Finnish)
Currently translated at 21.4% (182 of 848 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-07-16 00:35:43 +02:00
Hosted Weblate
d17f9b0687 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/
2024-07-16 00:35:43 +02:00
Marcin Martela
4d2bdb6eee Translated using Weblate (Polish)
Currently translated at 90.2% (763 of 845 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-07-16 00:35:43 +02:00
Vito0912
b6a1014c72 Translated using Weblate (German)
Currently translated at 100.0% (845 of 845 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
gallegonovato
b99885c806 Translated using Weblate (Spanish)
Currently translated at 100.0% (845 of 845 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-07-16 00:35:43 +02:00
tonttula
f422c9b820 Translated using Weblate (Finnish)
Currently translated at 21.4% (181 of 844 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fi/
2024-07-16 00:35:43 +02:00
Ori Z
0befe91360 Translated using Weblate (Hebrew)
Currently translated at 92.2% (779 of 844 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/he/
2024-07-16 00:35:43 +02:00
gallegonovato
da671e3fd5 Translated using Weblate (Spanish)
Currently translated at 100.0% (844 of 844 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-07-16 00:35:43 +02:00
Mario
fec94c18aa Translated using Weblate (German)
Currently translated at 100.0% (844 of 844 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
gallegonovato
11c6fc7d90 Translated using Weblate (Spanish)
Currently translated at 100.0% (838 of 838 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-07-16 00:35:43 +02:00
Michał Pomarański
7ea5e7dc95 Translated using Weblate (Polish)
Currently translated at 90.4% (758 of 838 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-07-16 00:35:43 +02:00
Mario
2a98e2c361 Translated using Weblate (German)
Currently translated at 100.0% (838 of 838 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
Charlie
7fb499b301 Translated using Weblate (French)
Currently translated at 100.0% (832 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/fr/
2024-07-16 00:35:43 +02:00
Lode Smets
af9aee76cf Translated using Weblate (Dutch)
Currently translated at 86.7% (722 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/nl/
2024-07-16 00:35:43 +02:00
gallegonovato
075ec15f02 Translated using Weblate (Spanish)
Currently translated at 100.0% (832 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/es/
2024-07-16 00:35:43 +02:00
Valentin
1c650473f8 Translated using Weblate (German)
Currently translated at 100.0% (832 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
Valentin
0efdf50821 Translated using Weblate (German)
Currently translated at 100.0% (832 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
Mario
df65ef2191 Translated using Weblate (German)
Currently translated at 100.0% (832 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/de/
2024-07-16 00:35:43 +02:00
Ahetek
bc3b1d9565 Translated using Weblate (Polish)
Currently translated at 90.8% (756 of 832 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/pl/
2024-07-16 00:35:43 +02:00
advplyr
2998d3ba6a Merge pull request #3160 from glorenzen/home-page-ultrawide-support
Add ultrawide support for home page
2024-07-15 16:30:44 -05:00
Greg Lorenzen
ea11153032 Update responsive limit for displayed items on personalized shelves on home page 2024-07-15 15:15:06 +00:00
Greg Lorenzen
733f61075f WIP: Add "End of chapter" option for sleep timer (#3151)
* Add SleepTimerTypes for countdown and chapter

* Add functionality for 'end of chapter' sleep timer

* Fix custom time for sleep timer

* Include end of chapter string for sleep timer

* Increase chapter end tolerance to 0.75

* Show sleep time options in modal when timer is active

* Add SleepTimerTypes for countdown and chapter

* Add functionality for 'end of chapter' sleep timer

* Fix custom time for sleep timer

* Include end of chapter string for sleep timer

* Increase chapter end tolerance to 0.75

* Show sleep time options in modal when timer is active

* Sleep timer cleanup

* Localization for sleep timer modal, UI updates

---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2024-07-14 13:56:48 -05:00
Greg Lorenzen
618e69775c Add responsive limit for displayed items on personalized shelves on home page 2024-07-13 22:32:44 -07:00
advplyr
eabfa90121 Update:Move library stats page to SideRail #3134 2024-07-13 15:26:07 -05:00
Greg Lorenzen
43b7ccd61a WIP: Add adjustable skip amount (#3113)
* Add playback settings string to en-us

* Add playback settings UI for jump forwards and jump backwards

* Remove jump forwards and jump backwards settings

* Remove jump forwards and jump backwards en-us strings

* Update player UI to include player settings button

* Add label view player settings string

* Add PlayerSettingsModal component

Includes a toggle switch for enabling/disabling the chapter track feature.

* Add player settings modal component to MediaPlayerContainer

* Handle useChapterTrack changes in PlayerUI

* Add jump forwards and jump backwards settings to user store

* Add jump forwards and jump backwards label strings

* Add jump forwards and jump backwards settings to PlayerSettingsModal

* Update jump forwards and jump backwards to handle user state values in PlayerHandler

* Update jump backwards icon in PlayerPlaybackControls

* Add playback settings string to en-us

* Add playback settings UI for jump forwards and jump backwards

* Remove jump forwards and jump backwards settings

* Remove jump forwards and jump backwards en-us strings

* Update player UI to include player settings button

* Add label view player settings string

* Add PlayerSettingsModal component

Includes a toggle switch for enabling/disabling the chapter track feature.

* Add player settings modal component to MediaPlayerContainer

* Handle useChapterTrack changes in PlayerUI

* Add jump forwards and jump backwards settings to user store

* Add jump forwards and jump backwards label strings

* Add jump forwards and jump backwards settings to PlayerSettingsModal

* Update jump forwards and jump backwards to handle user state values in PlayerHandler

* Update jump backwards icon in PlayerPlaybackControls

* Add jump amounts to playback controls tooltips

* Fix merge issues and add new Material Symbols to player ui

* Alphabetize strings in en-us.json

* Update dropdown component with SelectInput to support menu overflowing modal

* Update localization for player settings

* Update en-us strings order

---------

Co-authored-by: advplyr <advplyr@protonmail.com>
2024-07-12 17:52:48 -05:00
advplyr
b6875a44cf Merge pull request #3152 from mikiher/fix-opf-fetch-series
Fix OPF fetchSeries so it deduplicates found series
2024-07-12 07:59:10 -05:00
mikiher
c0004dd532 Fix fetchSeries so it deduplicates returned series 2024-07-12 12:42:42 +03:00
advplyr
0ee3b89760 Fix:Series and collection RSS feeds keeping correct order #3137 2024-07-11 17:49:05 -05:00
Nicholas W
c5e60d30e1 Podcast endpoints (#3140)
* Add: `AudioTrack.yaml`

* Fix: audiotrack example

* Initial: podcast schemas and endpoints

* Update schemas

* Add: podcasts tag

* Update bundled spec
2024-07-11 16:29:35 -05:00
advplyr
acaf1ac196 Merge pull request #3138 from glorenzen/update-material-icons
Update material icons
2024-07-10 16:41:21 -05:00
advplyr
8dc4538c95 Remove old material-icons files and classes 2024-07-10 16:41:42 -05:00
advplyr
e224fd2595 Update edit library folder icon to fill 2024-07-10 16:38:58 -05:00
advplyr
f0a1ea4d6d Merge pull request #3139 from nichwall/email_notification_linting_fixes
Email notification linting fixes
2024-07-09 16:25:31 -05:00
advplyr
10cb8ebf3b Merge pull request #3117 from mikiher/show-subtitles
New feature: Show Subtitles
2024-07-09 16:08:36 -05:00
advplyr
8c4afa1866 Fix typo 2024-07-09 16:09:41 -05:00
advplyr
eb5af47bbf Merge branch 'master' into show-subtitles 2024-07-09 15:58:34 -05:00
Nicholas Wallace
4fd93ce64c Update bundled spec 2024-07-09 01:55:55 +00:00
Nicholas Wallace
7ba4e9e66d Add: summary to Notification endpoints 2024-07-09 01:55:35 +00:00
Nicholas Wallace
e2e5449d25 Fix: schema reference in EmailController 2024-07-09 01:51:39 +00:00
Greg Lorenzen
abc76ca155 Replace Material Icons in LaxyBookCard and ShareModal components 2024-07-08 22:29:36 +00:00
Greg Lorenzen
0fc84a8684 Replace material-icons in YearInReview components 2024-07-08 22:29:20 +00:00
Greg Lorenzen
a76600e53b Update play_arrow icons with fill CSS class 2024-07-08 22:00:16 +00:00
Greg Lorenzen
e55cf30705 Add material-icons fill class to play icon in LazyBookCard.vue 2024-07-08 16:43:50 +00:00
Greg Lorenzen
2c65b8fd2b Replace material-icons class with material-symbols class in components 2024-07-08 09:39:00 -07:00
Greg Lorenzen
20b8e35132 Revert material-icons CSS classes
Add new material-symbols classes for Material Symbols fonts
2024-07-08 09:39:00 -07:00
Greg Lorenzen
8007225a41 Update font names and file paths for Material Symbols 2024-07-08 09:39:00 -07:00
Greg Lorenzen
63a6da6680 Add Material Symbols outlined and rounded woff files 2024-07-08 09:39:00 -07:00
mikiher
31c8cb476a Make menu item localized where they're not 2024-07-06 00:58:25 +03:00
mikiher
79bd6a25d9 Move Collapse Series option to the tollbar's context menu 2024-07-05 00:45:18 +03:00
mikiher
0042604e6d Merge branch 'advplyr:master' into show-subtitles 2024-07-05 00:17:01 +03:00
mikiher
54f2bb1092 Add a Show Subtitles option 2024-07-04 20:35:58 +03:00
mikiher
6b6df619f5 Remove tailwind.compiled.css (auto-generated, added by mistake) 2024-07-04 20:30:29 +03:00
238 changed files with 6996 additions and 7737 deletions

View File

@@ -6,5 +6,5 @@ module.exports.config = {
MetadataPath: Path.resolve('metadata'),
FFmpegPath: '/usr/bin/ffmpeg',
FFProbePath: '/usr/bin/ffprobe',
SkipBinariesCheck: true
}
SkipBinariesCheck: false
}

View File

@@ -3,6 +3,3 @@ contact_links:
- name: Discord
url: https://discord.gg/HQgCbd6E75
about: Ask questions, get help troubleshooting, and join the Abs community here.
- name: Matrix
url: https://matrix.to/#/#audiobookshelf:matrix.org
about: Ask questions, get help troubleshooting, and join the Abs community here.

View File

@@ -0,0 +1,20 @@
name: Close fixed issues on release.
on:
release:
types: [published]
permissions:
contents: read
issues: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Close issues marked as fixed upon a release.
uses: gcampbell-msft/fixed-pending-release@7fa1b75a0c04bcd4b375110522878e5f6100cff5
with:
label: 'awaiting release'
removeLabel: true
applyToAll: true
message: Fixed in [${releaseTag}](${releaseUrl}).

3
.gitignore vendored
View File

@@ -15,8 +15,9 @@
/.nyc_output/
/ffmpeg*
/ffprobe*
/unicode*
sw.*
.DS_STORE
.idea/*
tailwind.compiled.css
tailwind.compiled.css

View File

@@ -16,6 +16,7 @@ RUN apk update && \
tzdata \
ffmpeg \
make \
gcompat \
python3 \
g++ \
tini

View File

@@ -2,7 +2,6 @@
set -e
set -o pipefail
FFMPEG_INSTALL_DIR="/usr/lib/audiobookshelf-ffmpeg"
DEFAULT_DATA_DIR="/usr/share/audiobookshelf"
CONFIG_PATH="/etc/default/audiobookshelf"
DEFAULT_PORT=13378
@@ -46,25 +45,6 @@ add_group() {
fi
}
install_ffmpeg() {
echo "Starting FFMPEG Install"
WGET="wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz --output-document=ffmpeg-git-amd64-static.tar.xz"
if ! cd "$FFMPEG_INSTALL_DIR"; then
echo "Creating ffmpeg install dir at $FFMPEG_INSTALL_DIR"
mkdir "$FFMPEG_INSTALL_DIR"
chown -R 'audiobookshelf:audiobookshelf' "$FFMPEG_INSTALL_DIR"
cd "$FFMPEG_INSTALL_DIR"
fi
$WGET
tar xvf ffmpeg-git-amd64-static.tar.xz --strip-components=1 --no-same-owner
rm ffmpeg-git-amd64-static.tar.xz
echo "Good to go on Ffmpeg... hopefully"
}
setup_config() {
if [ -f "$CONFIG_PATH" ]; then
echo "Existing config found."
@@ -83,8 +63,6 @@ setup_config() {
config_text="METADATA_PATH=$DEFAULT_DATA_DIR/metadata
CONFIG_PATH=$DEFAULT_DATA_DIR/config
FFMPEG_PATH=$FFMPEG_INSTALL_DIR/ffmpeg
FFPROBE_PATH=$FFMPEG_INSTALL_DIR/ffprobe
PORT=$DEFAULT_PORT
HOST=$DEFAULT_HOST"
@@ -101,5 +79,3 @@ add_group 'audiobookshelf' ''
add_user 'audiobookshelf' '' 'audiobookshelf' 'audiobookshelf user-daemon' '/bin/false'
setup_config
install_ffmpeg

View File

@@ -1,19 +1,19 @@
@font-face {
font-family: 'Material Icons';
font-family: 'Material Symbols Rounded';
font-style: normal;
font-weight: 400;
src: url(~static/fonts/MaterialIcons.woff2) format('woff2');
src: url(~static/fonts/MaterialSymbolsRounded[FILL,GRAD,opsz,wght].woff2) format('woff2');
}
@font-face {
font-family: 'Material Icons Outlined';
font-family: 'Material Symbols Outlined';
font-style: normal;
font-weight: 400;
src: url(~static/fonts/MaterialIconsOutlined.woff2) format('woff2');
src: url(~static/fonts/MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].woff2) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
.material-symbols {
font-family: 'Material Symbols Rounded';
font-weight: normal;
font-style: normal;
line-height: 1;
@@ -27,12 +27,13 @@
vertical-align: top;
}
.material-icons:not([class*="text-"]) {
font-size: 1.5rem;
.material-symbols.fill {
font-variation-settings:
'FILL' 1
}
.material-icons-outlined {
font-family: 'Material Icons Outlined';
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
line-height: 1;
@@ -43,10 +44,12 @@
word-wrap: normal;
direction: ltr;
-webkit-font-smoothing: antialiased;
vertical-align: top;
}
.material-icons-outlined:not([class*="text-"]) {
font-size: 1.5rem;
.material-symbols-outlined.fill {
font-variation-settings:
'FILL' 1
}
/* cyrillic-ext */
@@ -317,4 +320,4 @@
font-display: swap;
src: url(~static/fonts/Ubuntu_Mono/UbuntuMono-Regular.ttf) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
}

View File

@@ -16,7 +16,7 @@
<div class="flex-grow" />
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
<span class="material-icons-outlined text-2xl text-warning text-opacity-50"> cast </span>
<span class="material-symbols-outlined text-2xl text-warning text-opacity-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>
@@ -26,19 +26,19 @@
<nuxt-link v-if="currentLibrary" to="/config/stats" class="hover:text-gray-200 cursor-pointer w-8 h-8 hidden sm:flex items-center justify-center mx-1">
<ui-tooltip :text="$strings.HeaderYourStats" direction="bottom" class="flex items-center">
<span class="material-icons text-2xl" aria-label="User Stats" role="button">equalizer</span>
<span class="material-symbols text-2xl" aria-label="User Stats" role="button">equalizer</span>
</ui-tooltip>
</nuxt-link>
<nuxt-link v-if="userCanUpload && currentLibrary" to="/upload" class="hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center mx-1">
<ui-tooltip :text="$strings.ButtonUpload" direction="bottom" class="flex items-center">
<span class="material-icons text-2xl" aria-label="Upload Media" role="button">upload</span>
<span class="material-symbols text-2xl" aria-label="Upload Media" role="button">upload</span>
</ui-tooltip>
</nuxt-link>
<nuxt-link v-if="userIsAdminOrUp" to="/config" class="hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center mx-1">
<ui-tooltip :text="$strings.HeaderSettings" direction="bottom" class="flex items-center">
<span class="material-icons text-2xl" aria-label="System Settings" role="button">settings</span>
<span class="material-symbols text-2xl" aria-label="System Settings" role="button">settings</span>
</ui-tooltip>
</nuxt-link>
@@ -47,7 +47,7 @@
<span class="block truncate">{{ username }}</span>
</span>
<span class="h-full md:ml-3 md:absolute inset-y-0 md:right-0 flex items-center justify-center md:pr-2 pointer-events-none">
<span class="material-icons text-xl text-gray-100">person</span>
<span class="material-symbols text-xl text-gray-100">person</span>
</span>
</nuxt-link>
</div>
@@ -55,7 +55,7 @@
<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">
<span class="material-icons text-2xl -ml-2 pr-1 text-white">play_arrow</span>
<span class="material-symbols fill text-2xl -ml-2 pr-1 text-white">play_arrow</span>
{{ $strings.ButtonPlay }}
</ui-btn>
<ui-tooltip v-if="isBookLibrary" :text="selectedIsFinished ? $strings.MessageMarkAsNotFinished : $strings.MessageMarkAsFinished" direction="bottom">
@@ -76,7 +76,7 @@
<ui-context-menu-dropdown v-if="contextMenuItems.length && !processingBatch" :items="contextMenuItems" class="ml-1" @action="contextMenuAction" />
<ui-tooltip :text="$strings.LabelDeselectAll" direction="bottom" class="flex items-center">
<span class="material-icons text-3xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatch ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span>
<span class="material-symbols text-3xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatch ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span>
</ui-tooltip>
</div>
</div>
@@ -170,13 +170,13 @@ export default {
if (!this.isPodcastLibrary && this.selectedMediaItemsArePlayable) {
options.push({
text: 'Quick Embed Metadata',
text: this.$strings.ButtonQuickEmbedMetadata,
action: 'quick-embed'
})
}
options.push({
text: 'Re-Scan',
text: this.$strings.ButtonReScan,
action: 'rescan'
})

View File

@@ -167,8 +167,19 @@ export default {
this.loaded = true
},
async fetchCategories() {
// Sets the limit for the number of items to be displayed based on the viewport width.
const viewportWidth = window.innerWidth
let limit
if (viewportWidth >= 3240) {
limit = 15
} else if (viewportWidth >= 2880 && viewportWidth < 3240) {
limit = 12
}
const limitQuery = limit ? `&limit=${limit}` : ''
const categories = await this.$axios
.$get(`/api/libraries/${this.currentLibraryId}/personalized?include=rssfeed,numEpisodesIncomplete,share`)
.$get(`/api/libraries/${this.currentLibraryId}/personalized?include=rssfeed,numEpisodesIncomplete,share${limitQuery}`)
.then((data) => {
return data
})

View File

@@ -44,10 +44,10 @@
<div class="bookshelfDividerCategorized h-6e w-full absolute top-0 left-0 right-0 z-20"></div>
</div>
<div v-show="canScrollLeft && !isScrolling" class="hidden sm:flex absolute top-0 left-0 w-32 pr-8 bg-black book-shelf-arrow-left items-center justify-center cursor-pointer opacity-0 hover:opacity-100 z-40" @click="scrollLeft">
<span class="material-icons text-white" :style="{ fontSize: 3.75 + 'em' }">chevron_left</span>
<span class="material-symbols text-white" :style="{ fontSize: 3.75 + 'em' }">chevron_left</span>
</div>
<div v-show="canScrollRight && !isScrolling" class="hidden sm:flex absolute top-0 right-0 w-32 pl-8 bg-black book-shelf-arrow-right items-center justify-center cursor-pointer opacity-0 hover:opacity-100 z-40" @click="scrollRight">
<span class="material-icons text-white" :style="{ fontSize: 3.75 + 'em' }">chevron_right</span>
<span class="material-symbols text-white" :style="{ fontSize: 3.75 + 'em' }">chevron_right</span>
</div>
</div>
</template>

View File

@@ -24,11 +24,11 @@
</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'">
<p v-if="isPlaylistsPage || isPodcastLibrary" class="text-sm">{{ $strings.ButtonPlaylists }}</p>
<span v-else class="material-icons-outlined text-lg">queue_music</span>
<span v-else class="material-symbols-outlined text-lg">queue_music</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'">
<p v-if="isCollectionsPage" class="text-sm">{{ $strings.ButtonCollections }}</p>
<span v-else class="material-icons-outlined text-lg">collections_bookmark</span>
<span v-else class="material-symbols-outlined text-lg">collections_bookmark</span>
</nuxt-link>
<nuxt-link v-if="isBookLibrary" :to="`/library/${currentLibraryId}/authors`" class="flex-grow h-full flex justify-center items-center" :class="isAuthorsPage ? 'bg-primary bg-opacity-80' : 'bg-primary bg-opacity-40'">
<p v-if="isAuthorsPage" class="text-sm">{{ $strings.ButtonAuthors }}</p>
@@ -53,7 +53,6 @@
<span class="font-mono">{{ numShowing }}</span>
</div>
<div class="flex-grow" />
<ui-checkbox v-if="!isBatchSelecting" v-model="settings.collapseBookSeries" :label="$strings.LabelCollapseSeries" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseBookSeries" />
<!-- RSS feed -->
<ui-tooltip v-if="seriesRssFeed" :text="$strings.LabelOpenRSSFeed" direction="top">
@@ -68,9 +67,6 @@
<div class="flex-grow hidden sm:inline-block" />
<!-- collapse series checkbox -->
<ui-checkbox v-if="isLibraryPage && isBookLibrary && !isBatchSelecting" v-model="settings.collapseSeries" :label="$strings.LabelCollapseSeries" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
<!-- 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" />
@@ -93,14 +89,20 @@
<div class="flex-grow" />
<p>{{ $strings.MessageSearchResultsFor }} "{{ searchQuery }}"</p>
<div class="flex-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="page === 'authors'">
<div class="flex-grow" />
<ui-btn v-if="userCanUpdate && authors && authors.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<ui-btn v-if="userCanUpdate && authors?.length && !isBatchSelecting" :loading="processingAuthors" color="primary" small @click="matchAllAuthors">{{ $strings.ButtonMatchAllAuthors }}</ui-btn>
<!-- author sort select -->
<controls-sort-select v-if="authors && authors.length" 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" />
<controls-sort-select v-if="authors?.length" 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" />
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
</div>
</div>
@@ -151,11 +153,13 @@ export default {
if (this.isSeriesRemovedFromContinueListening) {
items.push({
text: 'Re-Add Series to Continue Listening',
text: this.$strings.LabelReAddSeriesToContinueListening,
action: 're-add-to-continue-listening'
})
}
this.addSubtitlesMenuItem(items)
return items
},
seriesSortItems() {
@@ -183,6 +187,10 @@ export default {
{
text: this.$strings.LabelTotalDuration,
value: 'totalDuration'
},
{
text: this.$strings.LabelRandomly,
value: 'random'
}
]
},
@@ -318,11 +326,14 @@ export default {
if (this.isPodcastLibrary && this.isLibraryPage && this.userCanDownload) {
items.push({
text: 'Export OPML',
text: this.$strings.LabelExportOPML,
action: 'export-opml'
})
}
this.addSubtitlesMenuItem(items)
this.addCollapseSeriesMenuItem(items)
return items
},
showPlaylists() {
@@ -330,9 +341,70 @@ export default {
}
},
methods: {
addSubtitlesMenuItem(items) {
if (this.isBookLibrary && (!this.page || this.page === 'search')) {
if (this.settings.showSubtitles) {
items.push({
text: this.$strings.LabelHideSubtitles,
action: 'hide-subtitles'
})
} else {
items.push({
text: this.$strings.LabelShowSubtitles,
action: 'show-subtitles'
})
}
}
},
addCollapseSeriesMenuItem(items) {
if (this.isLibraryPage && this.isBookLibrary && !this.isBatchSelecting) {
if (this.settings.collapseSeries) {
items.push({
text: this.$strings.LabelExpandSeries,
action: 'expand-series'
})
} else {
items.push({
text: this.$strings.LabelCollapseSeries,
action: 'collapse-series'
})
}
}
},
handleSubtitlesAction(action) {
if (action === 'show-subtitles') {
this.settings.showSubtitles = true
this.updateShowSubtitles()
return true
}
if (action === 'hide-subtitles') {
this.settings.showSubtitles = false
this.updateShowSubtitles()
return true
}
return false
},
handleCollapseSeriesAction(action) {
if (action === 'collapse-series') {
this.settings.collapseSeries = true
this.updateCollapseSeries()
return true
}
if (action === 'expand-series') {
this.settings.collapseSeries = false
this.updateCollapseSeries()
return true
}
return false
},
contextMenuAction({ action }) {
if (action === 'export-opml') {
this.exportOPML()
return
} else if (this.handleSubtitlesAction(action)) {
return
} else if (this.handleCollapseSeriesAction(action)) {
return
}
},
exportOPML() {
@@ -353,6 +425,8 @@ export default {
return
}
this.markSeriesFinished()
} else if (this.handleSubtitlesAction(action)) {
return
}
},
showOpenSeriesRSSFeed() {
@@ -482,6 +556,9 @@ export default {
updateCollapseBookSeries() {
this.saveSettings()
},
updateShowSubtitles() {
this.saveSettings()
},
updateAuthorSort() {
this.saveSettings()
},

View File

@@ -2,7 +2,7 @@
<div>
<div 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 v-show="isMobilePortrait" class="flex items-center justify-end pb-2 px-4 mb-1" @click="closeDrawer">
<span class="material-icons text-2xl">arrow_back</span>
<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'">
@@ -10,7 +10,7 @@
<div v-show="routeName === route.iod" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.version" />
<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' }">
@@ -19,7 +19,7 @@
<p class="text-xs text-gray-300 italic">{{ Source }}</p>
</div>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xs">Latest: {{ latestVersion }}</a>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xs">Latest: {{ $config.version }}</a>
</div>
</div>
</template>
@@ -114,9 +114,9 @@ export default {
if (this.currentLibraryId) {
configRoutes.push({
id: 'config-library-stats',
id: 'library-stats',
title: this.$strings.HeaderLibraryStats,
path: '/config/library-stats'
path: `/library/${this.currentLibraryId}/stats`
})
configRoutes.push({
id: 'config-stats',
@@ -156,15 +156,9 @@ export default {
hasUpdate() {
return !!this.versionData.hasUpdate
},
latestVersion() {
return this.versionData.latestVersion
},
githubTagUrl() {
return this.versionData.githubTagUrl
},
currentVersionChangelog() {
return this.versionData.currentVersionChangelog || 'No Changelog Available'
},
streamLibraryItem() {
return this.$store.state.streamLibraryItem
}
@@ -182,4 +176,4 @@ export default {
}
}
}
</script>
</script>

View File

@@ -13,7 +13,7 @@
<widgets-explicit-indicator v-if="isExplicit" />
</div>
<div v-if="!playerHandler.isVideo" class="text-gray-400 flex items-center w-1/2 sm:w-4/5 lg:w-2/5">
<span class="material-icons text-sm">person</span>
<span class="material-symbols text-sm">person</span>
<div v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ podcastAuthor }}</div>
<div v-else-if="musicArtists" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ musicArtists }}</div>
<div v-else-if="authors.length" class="pl-1 sm:pl-1.5 text-xs sm:text-base truncate">
@@ -23,23 +23,25 @@
</div>
<div class="text-gray-400 flex items-center">
<span class="material-icons text-xs">schedule</span>
<span class="material-symbols text-xs">schedule</span>
<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" />
<ui-tooltip direction="top" :text="$strings.LabelClosePlayer">
<button :aria-label="$strings.LabelClosePlayer" class="material-icons sm:px-2 py-1 lg:p-4 cursor-pointer text-xl sm:text-2xl" @click="closePlayer">close</button>
<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>
</div>
<player-ui
ref="audioPlayer"
:chapters="chapters"
:current-chapter="currentChapter"
:paused="!isPlaying"
:loading="playerLoading"
:bookmarks="bookmarks"
:sleep-timer-set="sleepTimerSet"
:sleep-timer-remaining="sleepTimerRemaining"
:sleep-timer-type="sleepTimerType"
:is-podcast="isPodcast"
@playPause="playPause"
@jumpForward="jumpForward"
@@ -51,13 +53,16 @@
@showBookmarks="showBookmarks"
@showSleepTimer="showSleepTimerModal = true"
@showPlayerQueueItems="showPlayerQueueItemsModal = true"
@showPlayerSettings="showPlayerSettingsModal = true"
/>
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :current-time="bookmarkCurrentTime" :library-item-id="libraryItemId" @select="selectBookmark" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :timer-set="sleepTimerSet" :timer-time="sleepTimerTime" :remaining="sleepTimerRemaining" @set="setSleepTimer" @cancel="cancelSleepTimer" @increment="incrementSleepTimer" @decrement="decrementSleepTimer" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :timer-set="sleepTimerSet" :timer-type="sleepTimerType" :remaining="sleepTimerRemaining" :has-chapters="!!chapters.length" @set="setSleepTimer" @cancel="cancelSleepTimer" @increment="incrementSleepTimer" @decrement="decrementSleepTimer" />
<modals-player-queue-items-modal v-model="showPlayerQueueItemsModal" :library-item-id="libraryItemId" />
<modals-player-settings-modal v-model="showPlayerSettingsModal" />
</div>
</template>
@@ -76,9 +81,10 @@ export default {
currentTime: 0,
showSleepTimerModal: false,
showPlayerQueueItemsModal: false,
showPlayerSettingsModal: false,
sleepTimerSet: false,
sleepTimerTime: 0,
sleepTimerRemaining: 0,
sleepTimerType: null,
sleepTimer: null,
displayTitle: null,
currentPlaybackRate: 1,
@@ -145,6 +151,9 @@ export default {
if (this.streamEpisode) return this.streamEpisode.chapters || []
return this.media.chapters || []
},
currentChapter() {
return this.chapters.find((chapter) => chapter.start <= this.currentTime && this.currentTime < chapter.end)
},
title() {
if (this.playerHandler.displayTitle) return this.playerHandler.displayTitle
return this.mediaMetadata.title || 'No Title'
@@ -204,14 +213,18 @@ export default {
this.$store.commit('setIsPlaying', isPlaying)
this.updateMediaSessionPlaybackState()
},
setSleepTimer(seconds) {
setSleepTimer(time) {
this.sleepTimerSet = true
this.sleepTimerTime = seconds
this.sleepTimerRemaining = seconds
this.runSleepTimer()
this.showSleepTimerModal = false
this.sleepTimerType = time.timerType
if (this.sleepTimerType === this.$constants.SleepTimerTypes.COUNTDOWN) {
this.runSleepTimer(time)
}
},
runSleepTimer() {
runSleepTimer(time) {
this.sleepTimerRemaining = time.seconds
var lastTick = Date.now()
clearInterval(this.sleepTimer)
this.sleepTimer = setInterval(() => {
@@ -220,12 +233,23 @@ export default {
this.sleepTimerRemaining -= elapsed / 1000
if (this.sleepTimerRemaining <= 0) {
this.clearSleepTimer()
this.playerHandler.pause()
this.$toast.info('Sleep Timer Done.. zZzzZz')
this.sleepTimerEnd()
}
}, 1000)
},
checkChapterEnd(time) {
if (!this.currentChapter) return
const chapterEndTime = this.currentChapter.end
const tolerance = 0.75
if (time >= chapterEndTime - tolerance) {
this.sleepTimerEnd()
}
},
sleepTimerEnd() {
this.clearSleepTimer()
this.playerHandler.pause()
this.$toast.info('Sleep Timer Done.. zZzzZz')
},
cancelSleepTimer() {
this.showSleepTimerModal = false
this.clearSleepTimer()
@@ -235,6 +259,7 @@ export default {
this.sleepTimerRemaining = 0
this.sleepTimer = null
this.sleepTimerSet = false
this.sleepTimerType = null
},
incrementSleepTimer(amount) {
if (!this.sleepTimerSet) return
@@ -275,6 +300,10 @@ export default {
if (this.$refs.audioPlayer) {
this.$refs.audioPlayer.setCurrentTime(time)
}
if (this.sleepTimerType === this.$constants.SleepTimerTypes.CHAPTER && this.sleepTimerSet) {
this.checkChapterEnd(time)
}
},
setDuration(duration) {
this.totalDuration = duration

View File

@@ -15,7 +15,7 @@
</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'">
<span class="material-icons text-2xl">format_list_bulleted</span>
<span class="material-symbols text-2xl">format_list_bulleted</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonLatest }}</p>
@@ -43,7 +43,7 @@
</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'">
<span class="material-icons-outlined text-2xl">collections_bookmark</span>
<span class="material-symbols-outlined text-2xl">collections_bookmark</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonCollections }}</p>
@@ -51,7 +51,7 @@
</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'">
<span class="material-icons text-2.5xl">queue_music</span>
<span class="material-symbols text-2.5xl">queue_music</span>
<p class="pt-0.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonPlaylists }}</p>
@@ -72,13 +72,21 @@
</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'">
<span class="material-icons text-2xl">record_voice_over</span>
<span class="material-symbols text-2xl">record_voice_over</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.LabelNarrators }}</p>
<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'">
<span class="material-symbols text-2xl">monitoring</span>
<p class="pt-1 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonStats }}</p>
<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'">
<span class="abs-icons icon-podcast text-xl"></span>
@@ -88,7 +96,7 @@
</nuxt-link>
<nuxt-link v-if="isMusicLibrary" :to="`/library/${currentLibraryId}/bookshelf/albums`" 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="isMusicAlbumsPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<span class="material-icons-outlined text-xl">album</span>
<span class="material-symbols-outlined text-xl">album</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">Albums</p>
@@ -96,15 +104,15 @@
</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'">
<span class="material-icons text-2xl">file_download</span>
<span class="material-symbols text-2xl">file_download</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonDownloadQueue }}</p>
<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'">
<span class="material-icons text-2xl">warning</span>
<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'">
<span class="material-symbols text-2xl">warning</span>
<p class="pt-1.5 text-center leading-4" style="font-size: 1rem">{{ $strings.ButtonIssues }}</p>
@@ -194,6 +202,9 @@ export default {
isPlaylistsPage() {
return this.paramId === 'playlists'
},
isStatsPage() {
return this.$route.name === 'library-library-stats'
},
libraryBookshelfPage() {
return this.$route.name === 'library-library-bookshelf-id'
},

View File

@@ -15,12 +15,12 @@
<!-- Search icon btn -->
<div cy-id="match" v-show="!searching && isHovering && userCanUpdate" class="absolute top-0 left-0 p-2e cursor-pointer hover:text-white text-gray-200 transform hover:scale-125 duration-150" @click.prevent.stop="searchAuthor">
<ui-tooltip :text="$strings.ButtonQuickMatch" direction="bottom">
<span class="material-icons" :style="{ fontSize: 1.125 + 'em' }">search</span>
<span class="material-symbols" :style="{ fontSize: 1.125 + 'em' }">search</span>
</ui-tooltip>
</div>
<div cy-id="edit" v-show="isHovering && !searching && userCanUpdate" class="absolute top-0 right-0 p-2e cursor-pointer hover:text-white text-gray-200 transform hover:scale-125 duration-150" @click.prevent.stop="$emit('edit', author)">
<ui-tooltip :text="$strings.LabelEdit" direction="bottom">
<span class="material-icons" :style="{ fontSize: 1.125 + 'em' }">edit</span>
<span class="material-symbols" :style="{ fontSize: 1.125 + 'em' }">edit</span>
</ui-tooltip>
</div>

View File

@@ -5,6 +5,7 @@
</div>
<div class="flex-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>
</div>
</template>
@@ -23,6 +24,9 @@ export default {
computed: {
name() {
return this.author.name
},
numBooks() {
return this.author.numBooks
}
},
methods: {},
@@ -33,9 +37,9 @@ export default {
<style>
.authorSearchCardContent {
width: calc(100% - 80px);
height: 40px;
height: 44px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
</style>

View File

@@ -1,254 +0,0 @@
<template>
<div ref="wrapper" class="relative pointer-events-none" :style="{ width: standardWidth * 0.8 * 1.1 * scale + 'px', height: standardHeight * 1.1 * scale + 'px', marginBottom: 20 + 'px', marginTop: 15 + 'px' }">
<div ref="card" class="wrap absolute origin-center transform duration-200" :style="{ transform: `scale(${scale * scaleMultiplier}) translateY(${hover2 ? '-40%' : '-50%'})` }">
<div class="perspective">
<div class="book-wrap transform duration-100 pointer-events-auto" :class="hover2 ? 'z-80' : 'rotate'" @mouseover="hover = true" @mouseout="hover = false">
<div class="book book-1 box-shadow-book3d" ref="front"></div>
<div class="title book-1 pointer-events-none" ref="left"></div>
<div class="bottom book-1 pointer-events-none" ref="bottom"></div>
<div class="book-back book-1 pointer-events-none">
<div class="text pointer-events-none">
<h3 class="mb-4">Book Back</h3>
<p>
<span>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt earum doloremque aliquam culpa dolor nostrum consequatur quas dicta? Molestias repellendus minima pariatur libero vel, reiciendis optio magnam rerum, labore corporis.</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
src: String,
width: {
type: Number,
default: 200
}
},
data() {
return {
hover: false,
hover2: false,
standardWidth: 200,
standardHeight: 320,
isAttached: true,
pageX: 0,
pageY: 0
}
},
watch: {
src(newVal) {
this.setCover()
},
width(newVal) {
this.init()
},
hover(newVal) {
if (newVal) {
this.unattach()
} else {
this.attach()
}
setTimeout(() => {
this.hover2 = newVal
}, 100)
}
},
computed: {
scaleMultiplier() {
return this.hover2 ? 1.25 : 1
},
scale() {
var scale = this.width / this.standardWidth
return scale
}
},
methods: {
unattach() {
if (this.$refs.card && this.isAttached) {
var bookshelf = document.getElementById('bookshelf')
if (bookshelf) {
var pos = this.$refs.wrapper.getBoundingClientRect()
this.pageX = pos.x
this.pageY = pos.y
document.body.appendChild(this.$refs.card)
this.$refs.card.style.left = this.pageX + 'px'
this.$refs.card.style.top = this.pageY + 'px'
this.$refs.card.style.zIndex = 50
this.isAttached = false
} else if (bookshelf) {
console.log(this.pageX, this.pageY)
this.isAttached = false
}
}
},
attach() {
if (this.$refs.card && !this.isAttached) {
if (this.$refs.wrapper) {
this.isAttached = true
this.$refs.wrapper.appendChild(this.$refs.card)
this.$refs.card.style.left = '0px'
this.$refs.card.style.top = '0px'
}
} else {
console.log('Is attached already', this.isAttached)
}
},
init() {
var standardWidth = this.standardWidth
document.documentElement.style.setProperty('--book-w', standardWidth + 'px')
document.documentElement.style.setProperty('--book-wx', standardWidth + 1 + 'px')
document.documentElement.style.setProperty('--book-h', standardWidth * 1.6 + 'px')
document.documentElement.style.setProperty('--book-d', 40 + 'px')
},
setElBg(el) {
el.style.backgroundImage = `url("${this.src}")`
el.style.backgroundSize = 'cover'
el.style.backgroundPosition = 'center center'
el.style.backgroundRepeat = 'no-repeat'
},
setCover() {
if (this.$refs.front) {
this.setElBg(this.$refs.front)
}
if (this.$refs.bottom) {
this.setElBg(this.$refs.bottom)
this.$refs.bottom.style.backgroundSize = '2000%'
this.$refs.bottom.style.filter = 'blur(1px)'
}
if (this.$refs.left) {
this.setElBg(this.$refs.left)
this.$refs.left.style.backgroundSize = '2000%'
this.$refs.left.style.filter = 'blur(1px)'
}
}
},
mounted() {
this.setCover()
this.init()
}
}
</script>
<style>
/* :root {
--book-w: 200px;
--book-h: 320px;
--book-d: 30px;
--book-wx: 201px;
} */
/*
.wrap {
width: calc(1.1 * var(--book-w));
height: calc(1.1 * var(--book-h));
margin: 0 auto;
}
.perspective {
position: relative;
width: 100%;
height: 100%;
perspective: 600px;
transform-style: preserve-3d;
overflow: hidden;
}
.book-wrap {
height: 100%;
width: 100%;
transform-style: preserve-3d;
transition: 'all ease-out 0.6s';
}
.book {
width: var(--book-w);
height: var(--book-h);
background: url(https://covers.openlibrary.org/b/id/8303020-L.jpg) no-repeat center center;
background-size: cover;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
cursor: pointer;
}
.title {
content: '';
height: var(--book-h);
width: var(--book-d);
position: absolute;
right: 0;
left: calc(var(--book-wx) * -1);
top: 0;
bottom: 0;
margin: auto;
background: #444;
transform: rotateY(-80deg) translateX(-14px);
background: url(https://covers.openlibrary.org/b/id/8303020-L.jpg) no-repeat center center;
background-size: 5000%;
filter: blur(1px);
}
.bottom {
content: '';
height: var(--book-d);
width: var(--book-w);
position: absolute;
right: 0;
bottom: var(--book-h);
top: 0;
left: 0;
margin: auto;
background: #444;
transform: rotateY(0deg) rotateX(90deg) translateY(-15px) translateX(-2.5px) skewX(10deg);
background: url(https://covers.openlibrary.org/b/id/8303020-L.jpg) no-repeat center center;
background-size: 5000%;
filter: blur(1px);
}
.book-back {
width: var(--book-w);
height: var(--book-h);
background-color: #444;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
cursor: pointer;
transform: rotate(180deg) translateZ(-30px) translateX(5px);
}
.book-back .text {
transform: rotateX(180deg);
position: absolute;
bottom: 0px;
padding: 20px;
text-align: left;
font-size: 12px;
}
.book-back .text h3 {
color: #fff;
}
.book-back .text span {
display: block;
margin-bottom: 20px;
color: #fff;
}
.book-wrap.rotate {
transform: rotateY(30deg) rotateX(0deg);
}
.book-wrap.flip {
transform: rotateY(180deg);
} */
</style>

View File

@@ -0,0 +1,36 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<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">
<p class="truncate text-sm">{{ genre }}</p>
<p class="text-xs text-gray-400">{{ $getString('LabelXItems', [numItems]) }}</p>
</div>
</div>
</template>
<script>
export default {
props: {
genre: String,
numItems: Number
},
data() {
return {}
},
computed: {},
methods: {},
mounted() {}
}
</script>
<style>
.tagSearchCardContent {
width: calc(100% - 40px);
height: 44px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

View File

@@ -2,15 +2,9 @@
<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">
<p v-if="matchKey !== 'title'" class="truncate text-sm">{{ title }}</p>
<p v-else class="truncate text-sm" v-html="matchHtml" />
<p v-if="matchKey === 'subtitle'" class="truncate text-xs text-gray-300" v-html="matchHtml" />
<p v-if="matchKey !== 'authors'" class="text-xs text-gray-200 truncate">{{ $getString('LabelByAuthor', [authorName]) }}</p>
<p v-else class="truncate text-xs text-gray-200" v-html="matchHtml" />
<div v-if="matchKey === 'series' || matchKey === 'tags' || matchKey === 'isbn' || matchKey === 'asin' || matchKey === 'episode' || matchKey === 'narrators'" class="m-0 p-0 truncate text-xs" v-html="matchHtml" />
<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>
</div>
</div>
</template>
@@ -21,10 +15,7 @@ export default {
libraryItem: {
type: Object,
default: () => {}
},
search: String,
matchKey: String,
matchText: String
}
},
data() {
return {}
@@ -58,23 +49,6 @@ export default {
authorName() {
if (this.isPodcast) return this.mediaMetadata.author || 'Unknown'
return this.mediaMetadata.authorName || 'Unknown'
},
matchHtml() {
if (!this.matchText || !this.search) return ''
// This used to highlight the part of the search found
// but with removing commas periods etc this is no longer plausible
const html = this.matchText
if (this.matchKey === 'episode') return `<p class="truncate">${this.$strings.LabelEpisode}: ${html}</p>`
if (this.matchKey === 'tags') return `<p class="truncate">${this.$strings.LabelTags}: ${html}</p>`
if (this.matchKey === 'subtitle') return `<p class="truncate">${html}</p>`
if (this.matchKey === 'authors') this.$getString('LabelByAuthor', [html])
if (this.matchKey === 'isbn') return `<p class="truncate">ISBN: ${html}</p>`
if (this.matchKey === 'asin') return `<p class="truncate">ASIN: ${html}</p>`
if (this.matchKey === 'series') return `<p class="truncate">${this.$strings.LabelSeries}: ${html}</p>`
if (this.matchKey === 'narrators') return `<p class="truncate">${this.$strings.LabelNarrator}: ${html}</p>`
return `${html}`
}
},
methods: {},

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex items-center px-1 overflow-hidden">
<div class="w-8 flex items-center justify-center">
<span v-if="isFinished" :class="taskIconStatus" class="material-icons text-base">{{ actionIcon }}</span>
<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">

View File

@@ -5,7 +5,7 @@
</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-icons">close</span>
<span class="text-base text-white text-opacity-80 font-mono material-symbols">close</span>
</div>
<template v-if="!uploadSuccess && !uploadFailed">
@@ -22,7 +22,7 @@
<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-icons">sync</span>
<span class="text-base text-white text-opacity-80 font-mono material-symbols">sync</span>
</div>
</ui-tooltip>
</div>

View File

@@ -45,28 +45,28 @@
<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="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-icons" :style="{ fontSize: playIconFontSize + 'em' }">play_circle_filled</span>
<span class="material-symbols fill" :style="{ fontSize: playIconFontSize + 'em' }">play_arrow</span>
</div>
</div>
<div cy-id="readButton" v-show="showReadButton" 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="clickReadEBook">
<span class="material-icons" :style="{ fontSize: playIconFontSize + 'em' }">auto_stories</span>
<span class="material-symbols" :style="{ fontSize: playIconFontSize + 'em' }">auto_stories</span>
</div>
</div>
<div cy-id="editButton" v-if="userCanUpdate" v-show="!isSelectionMode" class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-150 top-0 right-0" :style="{ padding: 0.375 + 'em' }" @click.stop.prevent="editClick">
<span class="material-icons" :style="{ fontSize: 1 + 'em' }">edit</span>
<span class="material-symbols" :style="{ fontSize: 1 + 'em' }">edit</span>
</div>
<!-- Radio button -->
<div cy-id="selectedRadioButton" v-if="!isAuthorBookshelfView" class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-100" :style="{ top: 0.375 + 'em', left: 0.375 + 'em' }" @click.stop.prevent="selectBtnClick">
<span class="material-icons" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 + 'em' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span>
<span class="material-symbols" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 + 'em' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span>
</div>
<!-- More Menu Icon -->
<div cy-id="moreButton" ref="moreIcon" v-show="!isSelectionMode && moreMenuItems.length" class="md:block absolute cursor-pointer hover:text-yellow-300 300 hover:scale-125 transform duration-150" :style="{ bottom: 0.375 + 'em', right: 0.375 + 'em' }" @click.stop.prevent="clickShowMore">
<span class="material-icons" :style="{ fontSize: 1.2 + 'em' }">more_vert</span>
<span class="material-symbols" :style="{ fontSize: 1.2 + 'em' }">more_vert</span>
</div>
<div cy-id="ebookFormat" v-if="ebookFormat" class="absolute" :style="{ bottom: 0.375 + 'em', left: 0.375 + 'em' }">
@@ -87,17 +87,17 @@
<!-- Error widget -->
<ui-tooltip cy-id="ErrorTooltip" v-if="showError" :text="errorText" class="absolute bottom-4e left-0 z-10">
<div :style="{ height: 1.5 + 'em', width: 2.5 + 'em' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<span class="material-icons text-red-100 pr-1e" :style="{ fontSize: 0.875 + 'em' }">priority_high</span>
<span class="material-symbols text-red-100 pr-1e" :style="{ fontSize: 0.875 + 'em' }">priority_high</span>
</div>
</ui-tooltip>
<!-- rss feed icon -->
<div cy-id="rssFeed" v-if="rssFeed && !isSelectionMode && !isHovering" class="absolute text-success top-0 left-0 z-10" :style="{ padding: 0.375 + 'em' }">
<span class="material-icons" :style="{ fontSize: 1.5 + 'em' }">rss_feed</span>
<span class="material-symbols" :style="{ fontSize: 1.5 + 'em' }">rss_feed</span>
</div>
<!-- media item shared icon -->
<div cy-id="mediaItemShare" v-if="mediaItemShare && !isSelectionMode && !isHovering" class="absolute text-success left-0 z-10" :style="{ padding: 0.375 + 'em', top: rssFeed ? '2em' : '0px' }">
<span class="material-icons" :style="{ fontSize: 1.5 + 'em' }">public</span>
<span class="material-symbols" :style="{ fontSize: 1.5 + 'em' }">public</span>
</div>
<!-- Series sequence -->
@@ -132,6 +132,9 @@
<widgets-explicit-indicator cy-id="explicitIndicator" v-if="isExplicit" />
</ui-tooltip>
</div>
<ui-tooltip v-if="showSubtitles" :text="displaySubtitle" :disabled="!displaySubtitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<p cy-id="subtitle" class="truncate" ref="displaySubtitle" :style="{ fontSize: 0.6 + 'em' }">{{ displaySubtitle }}</p>
</ui-tooltip>
<p cy-id="line2" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displayLineTwo || '&nbsp;' }}</p>
<p cy-id="line3" v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displaySortLine }}</p>
</div>
@@ -171,6 +174,7 @@ export default {
selected: false,
isSelectionMode: false,
displayTitleTruncated: false,
displaySubtitleTruncated: false,
showCoverBg: false
}
},
@@ -237,7 +241,7 @@ export default {
return this._libraryItem.mediaType
},
isPodcast() {
return this.mediaType === 'podcast'
return this.mediaType === 'podcast' || this.store.getters['libraries/getCurrentLibraryMediaType'] === 'podcast'
},
isMusic() {
return this.mediaType === 'music'
@@ -339,6 +343,13 @@ export default {
if (this.collapsedSeries) return ignorePrefix ? this.collapsedSeries.nameIgnorePrefix : this.collapsedSeries.name
return ignorePrefix ? this.mediaMetadata.titleIgnorePrefix || '\u00A0' : this.title || '\u00A0'
},
displaySubtitle() {
if (!this.libraryItem) return '\u00A0'
if (this.collapsedSeries) return this.collapsedSeries.numBooks === 1 ? '1 book' : `${this.collapsedSeries.numBooks} books`
if (this.mediaMetadata.subtitle) return this.mediaMetadata.subtitle
if (this.mediaMetadata.seriesName) return this.mediaMetadata.seriesName
return ''
},
displayLineTwo() {
if (this.recentEpisode) return this.title
if (this.isPodcast) return this.author
@@ -644,6 +655,9 @@ export default {
},
mediaItemShare() {
return this._libraryItem.mediaItemShare || null
},
showSubtitles() {
return !this.isPodcast && this.store.getters['user/getUserSetting']('showSubtitles')
}
},
methods: {
@@ -685,6 +699,9 @@ export default {
if (this.$refs.displayTitle) {
this.displayTitleTruncated = this.$refs.displayTitle.scrollWidth > this.$refs.displayTitle.clientWidth
}
if (this.$refs.displaySubtitle) {
this.displaySubtitleTruncated = this.$refs.displaySubtitle.scrollWidth > this.$refs.displaySubtitle.clientWidth
}
})
},
clickCard(e) {

View File

@@ -7,11 +7,11 @@
</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 class="absolute pointer-events-auto" :style="{ top: 0.5 + 'em', right: 0.5 + 'em' }" @click.stop.prevent="clickEdit">
<span class="material-icons text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
<span class="material-symbols text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
</div>
</div>
<span v-if="!isHovering && rssFeed" class="absolute z-10 material-icons text-success" :style="{ top: 0.5 + 'em', left: 0.5 + 'em', fontSize: 1.5 + 'em' }">rss_feed</span>
<span v-if="!isHovering && rssFeed" class="absolute z-10 material-symbols text-success" :style="{ top: 0.5 + 'em', left: 0.5 + 'em', fontSize: 1.5 + 'em' }">rss_feed</span>
</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' }">

View File

@@ -7,7 +7,7 @@
</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 class="absolute pointer-events-auto" :style="{ top: 0.5 + 'em', right: 0.5 + 'em' }" @click.stop.prevent="clickEdit">
<span class="material-icons text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
<span class="material-symbols text-white text-opacity-75 hover:text-opacity-100" :style="{ fontSize: 1.25 + 'em' }">edit</span>
</div>
</div>
</div>

View File

@@ -16,7 +16,7 @@
<p :style="{ fontSize: 1.2 + 'em' }">{{ displayTitle }}</p>
</div>
<span cy-id="rssFeedMarker" v-if="!isHovering && rssFeed" class="absolute z-10 material-icons text-success" :style="{ top: 0.5 + 'em', left: 0.5 + 'em', fontSize: 1.5 + 'em' }">rss_feed</span>
<span cy-id="rssFeedMarker" v-if="!isHovering && rssFeed" class="absolute z-10 material-symbols text-success" :style="{ top: 0.5 + 'em', left: 0.5 + 'em', fontSize: 1.5 + 'em' }">rss_feed</span>
</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' }">
@@ -81,16 +81,16 @@ export default {
return this.store.getters['user/getSizeMultiplier']
},
seriesId() {
return this.series ? this.series.id : ''
return this.series?.id || ''
},
title() {
return this.series ? this.series.name : ''
return this.series?.name || ''
},
nameIgnorePrefix() {
return this.series ? this.series.nameIgnorePrefix : ''
return this.series?.nameIgnorePrefix || ''
},
displayTitle() {
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title || '\u00A0'
return this.title || '\u00A0'
},
displaySortLine() {
@@ -110,13 +110,13 @@ export default {
}
},
books() {
return this.series ? this.series.books || [] : []
return this.series?.books || []
},
addedAt() {
return this.series ? this.series.addedAt : 0
return this.series?.addedAt || 0
},
totalDuration() {
return this.series ? this.series.totalDuration : 0
return this.series?.totalDuration || 0
},
seriesBookProgress() {
return this.books
@@ -161,7 +161,7 @@ export default {
return this.bookshelfView == constants.BookshelfView.DETAIL
},
rssFeed() {
return this.series ? this.series.rssFeed : null
return this.series?.rssFeed
}
},
methods: {

View File

@@ -3,7 +3,7 @@
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=narrators.${$encode(name)}`">
<div cy-id="card" :style="{ width: cardWidth + 'px', height: cardHeight + 'px', fontSize: sizeMultiplier + 'rem' }" class="bg-primary box-shadow-book rounded-md relative overflow-hidden">
<div class="absolute inset-0 w-full h-full flex items-center justify-center pointer-events-none opacity-40">
<span class="material-icons-outlined text-[10em]">record_voice_over</span>
<span class="material-symbols-outlined text-[10em]">record_voice_over</span>
</div>
<!-- Narrator name & num books overlay -->

View File

@@ -1,10 +1,11 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-icons text-2xl text-gray-200">record_voice_over</span>
<span class="material-symbols text-2xl text-gray-200">record_voice_over</span>
</div>
<div class="flex-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>
</div>
</template>
@@ -12,7 +13,8 @@
<script>
export default {
props: {
narrator: String
narrator: String,
numBooks: Number
},
data() {
return {}
@@ -26,9 +28,9 @@ export default {
<style scoped>
.narratorSearchCardContent {
width: calc(100% - 40px);
height: 40px;
height: 44px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
</style>

View File

@@ -1,10 +1,11 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-icons text-2xl text-gray-200">local_offer</span>
<span class="material-symbols text-2xl text-gray-200">local_offer</span>
</div>
<div class="flex-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>
</div>
</template>
@@ -12,7 +13,8 @@
<script>
export default {
props: {
tag: String
tag: String,
numItems: Number
},
data() {
return {}
@@ -26,9 +28,9 @@ export default {
<style>
.tagSearchCardContent {
width: calc(100% - 40px);
height: 40px;
height: 44px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
</style>

View File

@@ -10,7 +10,7 @@
</svg>
</span>
<div v-else class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-300" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected">
<span class="material-icons" style="font-size: 1.1rem">close</span>
<span class="material-symbols" style="font-size: 1.1rem">close</span>
</div>
</button>
@@ -24,7 +24,7 @@
<!-- selected checkmark icon -->
<div v-if="item.value === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none">
<span class="material-icons text-base text-yellow-400">check</span>
<span class="material-symbols text-base text-yellow-400">check</span>
</div>
</li>
</template>

View File

@@ -5,8 +5,8 @@
<ui-text-input ref="input" v-model="search" :placeholder="$strings.PlaceholderSearch" @input="inputUpdate" @focus="focussed" @blur="blurred" class="w-full h-8 text-sm" />
</form>
<div class="absolute top-0 right-0 bottom-0 h-full flex items-center px-2 text-gray-400 cursor-pointer" @click="clickClear">
<span v-if="!search" class="material-icons" style="font-size: 1.2rem">search</span>
<span v-else class="material-icons" style="font-size: 1.2rem">close</span>
<span v-if="!search" class="material-symbols" style="font-size: 1.2rem">search</span>
<span v-else class="material-symbols" style="font-size: 1.2rem">close</span>
</div>
</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">
@@ -25,7 +25,7 @@
<template v-for="item in bookResults">
<li :key="item.libraryItem.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/item/${item.libraryItem.id}`">
<cards-item-search-card :library-item="item.libraryItem" :match-key="item.matchKey" :match-text="item.matchText" :search="lastSearch" />
<cards-item-search-card :library-item="item.libraryItem" />
</nuxt-link>
</li>
</template>
@@ -34,7 +34,7 @@
<template v-for="item in podcastResults">
<li :key="item.libraryItem.id" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/item/${item.libraryItem.id}`">
<cards-item-search-card :library-item="item.libraryItem" :match-key="item.matchKey" :match-text="item.matchText" :search="lastSearch" />
<cards-item-search-card :library-item="item.libraryItem" />
</nuxt-link>
</li>
</template>
@@ -59,9 +59,18 @@
<p v-if="tagResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelTags }}</p>
<template v-for="item in tagResults">
<li :key="item.name" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<li :key="`tag.${item.name}`" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=tags.${$encode(item.name)}`">
<cards-tag-search-card :tag="item.name" />
<cards-tag-search-card :tag="item.name" :num-items="item.numItems" />
</nuxt-link>
</li>
</template>
<p v-if="genreResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelGenres }}</p>
<template v-for="item in genreResults">
<li :key="`genre.${item.name}`" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=genres.${$encode(item.name)}`">
<cards-genre-search-card :genre="item.name" :num-items="item.numItems" />
</nuxt-link>
</li>
</template>
@@ -70,7 +79,7 @@
<template v-for="narrator in narratorResults">
<li :key="narrator.name" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=narrators.${$encode(narrator.name)}`">
<cards-narrator-search-card :narrator="narrator.name" />
<cards-narrator-search-card :narrator="narrator.name" :num-books="narrator.numBooks" />
</nuxt-link>
</li>
</template>
@@ -95,6 +104,7 @@ export default {
authorResults: [],
seriesResults: [],
tagResults: [],
genreResults: [],
narratorResults: [],
searchTimeout: null,
lastSearch: null
@@ -105,7 +115,7 @@ export default {
return this.$store.state.libraries.currentLibraryId
},
totalResults() {
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.podcastResults.length + this.narratorResults.length
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length
}
},
methods: {
@@ -116,7 +126,7 @@ export default {
if (!this.search) return
var search = this.search
this.clearResults()
this.$router.push(`/library/${this.currentLibraryId}/search?q=${search}`)
this.$router.push(`/library/${this.currentLibraryId}/search?q=${encodeURIComponent(search)}`)
},
clearResults() {
this.search = null
@@ -126,6 +136,7 @@ export default {
this.authorResults = []
this.seriesResults = []
this.tagResults = []
this.genreResults = []
this.narratorResults = []
this.showMenu = false
this.isFetching = false
@@ -155,7 +166,7 @@ export default {
}
this.isFetching = true
const searchResults = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/search?q=${value}&limit=3`).catch((error) => {
const searchResults = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/search?q=${encodeURIComponent(value)}&limit=3`).catch((error) => {
console.error('Search error', error)
return []
})
@@ -168,6 +179,7 @@ export default {
this.authorResults = searchResults.authors || []
this.seriesResults = searchResults.series || []
this.tagResults = searchResults.tags || []
this.genreResults = searchResults.genres || []
this.narratorResults = searchResults.narrators || []
this.isFetching = false
@@ -203,4 +215,4 @@ export default {
.globalSearchMenu {
max-height: calc(100vh - 75px);
}
</style>
</style>

View File

@@ -10,7 +10,7 @@
</svg>
</span>
<div v-else class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-200" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected">
<span class="material-icons" style="font-size: 1.1rem">close</span>
<span class="material-symbols" style="font-size: 1.1rem">close</span>
</div>
</button>
@@ -22,11 +22,11 @@
<span class="font-normal ml-3 block truncate text-sm">{{ item.text }}</span>
</div>
<div v-if="item.sublist" class="absolute right-1 top-0 bottom-0 h-full flex items-center">
<span class="material-icons text-2xl">arrow_right</span>
<span class="material-symbols text-2xl">arrow_right</span>
</div>
<!-- selected checkmark icon -->
<div v-if="item.value === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none">
<span class="material-icons text-base text-yellow-400">check</span>
<span class="material-symbols text-base text-yellow-400">check</span>
</div>
</li>
</template>
@@ -34,7 +34,7 @@
<ul v-show="sublist" class="h-full w-full" role="listbox" aria-labelledby="listbox-label">
<li class="text-gray-50 select-none relative py-2 pl-9 cursor-pointer hover:bg-white/5" role="option" @click="sublist = null">
<div class="absolute left-1 top-0 bottom-0 h-full flex items-center">
<span class="material-icons text-2xl">arrow_left</span>
<span class="material-symbols text-2xl">arrow_left</span>
</div>
<div class="flex items-center justify-between">
<span class="font-normal block truncate">{{ $strings.ButtonBack }}</span>
@@ -52,7 +52,7 @@
</div>
<!-- selected checkmark icon -->
<div v-if="`${sublist}.${item.value}` === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none">
<span class="material-icons text-base text-yellow-400">check</span>
<span class="material-symbols text-base text-yellow-400">check</span>
</div>
</li>
</template>

View File

@@ -3,7 +3,7 @@
<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="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span>
<span class="material-icons text-lg text-yellow-400">{{ descending ? 'expand_more' : 'expand_less' }}</span>
<span class="material-symbols text-lg text-yellow-400">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</button>
@@ -14,7 +14,7 @@
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
</div>
<span v-if="item.value === selected" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
<span class="material-icons text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span>
<span class="material-symbols text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</li>
</template>
@@ -88,6 +88,10 @@ export default {
{
text: this.$strings.LabelFileModified,
value: 'mtimeMs'
},
{
text: this.$strings.LabelRandomly,
value: 'random'
}
]
},
@@ -128,6 +132,10 @@ export default {
{
text: this.$strings.LabelFileModified,
value: 'mtimeMs'
},
{
text: this.$strings.LabelRandomly,
value: 'random'
}
]
},
@@ -215,4 +223,4 @@ export default {
}
}
}
</script>
</script>

View File

@@ -3,7 +3,7 @@
<button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu">
<span class="flex items-center justify-between">
<span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span>
<span class="material-icons text-lg text-yellow-400">{{ descending ? 'expand_more' : 'expand_less' }}</span>
<span class="material-symbols text-lg text-yellow-400">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</button>
@@ -14,7 +14,7 @@
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
</div>
<span v-if="item.value === selected" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
<span class="material-icons text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span>
<span class="material-symbols text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span>
</span>
</li>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div class="relative" v-click-outside="clickOutside" @mouseover="mouseover" @mouseleave="mouseleave">
<button :aria-label="$strings.LabelVolume" class="text-gray-300 hover:text-white" @mousedown.prevent @mouseup.prevent @click="clickVolumeIcon">
<span class="material-icons text-2xl sm:text-3xl">{{ volumeIcon }}</span>
<span class="material-symbols text-2xl sm:text-3xl">{{ volumeIcon }}</span>
</button>
<transition name="menux">
<div v-show="isOpen" class="volumeMenu h-6 absolute bottom-2 w-28 px-2 bg-bg shadow-sm rounded-lg" style="left: -116px">

View File

@@ -7,7 +7,7 @@
<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' }">
<span class="material-icons" :style="{ fontSize: sizeMultiplier * 1.75 + 'rem' }">open_in_new</span>
<span class="material-symbols" :style="{ fontSize: sizeMultiplier * 1.75 + 'rem' }">open_in_new</span>
</a>
</div>

View File

@@ -91,7 +91,7 @@
<ui-textarea-with-label :value="prettyFfprobeData" readonly :rows="30" class="text-xs" />
<button class="absolute top-4 right-4" :class="copiedToClipboard ? 'text-success' : 'text-white/50 hover:text-white/80'" @click.stop="copyFfprobeData">
<span class="material-icons">{{ copiedToClipboard ? 'check' : 'content_copy' }}</span>
<span class="material-symbols">{{ copiedToClipboard ? 'check' : 'content_copy' }}</span>
</button>
</div>
</div>

View File

@@ -19,7 +19,7 @@
<ui-tooltip :text="$strings.LabelUpdateCoverHelp">
<p class="pl-4">
{{ $strings.LabelUpdateCover }}
<span class="material-icons icon-text">info_outlined</span>
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
</div>
@@ -28,7 +28,7 @@
<ui-tooltip :text="$strings.LabelUpdateDetailsHelp">
<p class="pl-4">
{{ $strings.LabelUpdateDetails }}
<span class="material-icons icon-text">info_outlined</span>
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
</div>

View File

@@ -24,7 +24,7 @@
<div class="flex-grow px-2">
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-icons text-2xl -mt-px">add</span></ui-btn>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-symbols text-2xl -mt-px">add</span></ui-btn>
</div>
</form>
</div>

View File

@@ -1,8 +1,8 @@
<template>
<modals-modal v-model="show" name="chapters" :width="600" :height="'unset'">
<div id="chapter-modal-wrapper" ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div id="chapter-modal-wrapper" ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<template v-for="chap in chapters">
<div :key="chap.id" :id="`chapter-row-${chap.id}`" class="flex items-center px-6 py-3 justify-start cursor-pointer hover:bg-bg relative" :class="chap.id === currentChapterId ? 'bg-yellow-400 bg-opacity-10' : chap.end / _playbackRate <= currentChapterStart ? 'bg-success bg-opacity-5' : 'bg-opacity-20'" @click="clickChapter(chap)">
<div :key="chap.id" :id="`chapter-row-${chap.id}`" class="flex items-center px-6 py-3 justify-start cursor-pointer relative" :class="chap.id === currentChapterId ? 'bg-yellow-400/20 hover:bg-yellow-400/10' : chap.end / _playbackRate <= currentChapterStart ? 'bg-success/10 hover:bg-success/5' : 'hover:bg-primary/10'" @click="clickChapter(chap)">
<p class="chapter-title truncate text-sm md:text-base">
{{ chap.title }}
</p>
@@ -87,4 +87,4 @@ export default {
max-width: calc(100% - 150px);
}
}
</style>
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div ref="wrapper" 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 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">
<span class="material-icons text-2xl md:text-4xl">close</span>
<span class="material-symbols text-2xl md:text-4xl">close</span>
</div>
<div ref="content" class="text-white">
<form v-if="selectedSeries" @submit.prevent="submitSeriesForm">

View File

@@ -3,7 +3,7 @@
<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" />
<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-icons text-2xl landscape:text-2xl md:portrait:text-4xl lg:text-4xl">close</span>
<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" style="min-width: 380px; min-height: 200px; max-width: 100vw" class="relative text-white" aria-modal="true" :style="{ height: modalHeight, width: modalWidth, marginTop: contentMarginTop + 'px' }" @mousedown="mousedownModal" @mouseup="mouseupModal" v-click-outside="clickBg">

View File

@@ -0,0 +1,70 @@
<template>
<modals-modal v-model="show" name="player-settings" :width="500" :height="'unset'">
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden p-4" style="max-height: 80vh; min-height: 40vh">
<h3 class="text-xl font-semibold mb-8">{{ $strings.HeaderPlayerSettings }}</h3>
<div class="flex items-center mb-4">
<ui-toggle-switch v-model="useChapterTrack" @input="setUseChapterTrack" />
<div class="pl-4">
<span>{{ $strings.LabelUseChapterTrack }}</span>
</div>
</div>
<div class="flex items-center mb-4">
<ui-select-input v-model="jumpForwardAmount" :label="$strings.LabelJumpForwardAmount" menuMaxHeight="250px" :items="jumpValues" @input="setJumpForwardAmount" />
</div>
<div class="flex items-center">
<ui-select-input v-model="jumpBackwardAmount" :label="$strings.LabelJumpBackwardAmount" menuMaxHeight="250px" :items="jumpValues" @input="setJumpBackwardAmount" />
</div>
</div>
</modals-modal>
</template>
<script>
export default {
props: {
value: Boolean
},
data() {
return {
useChapterTrack: false,
jumpValues: [
{ text: this.$getString('LabelTimeDurationXSeconds', ['10']), value: 10 },
{ text: this.$getString('LabelTimeDurationXSeconds', ['15']), value: 15 },
{ text: this.$getString('LabelTimeDurationXSeconds', ['30']), value: 30 },
{ text: this.$getString('LabelTimeDurationXSeconds', ['60']), value: 60 },
{ text: this.$getString('LabelTimeDurationXMinutes', ['2']), value: 120 },
{ text: this.$getString('LabelTimeDurationXMinutes', ['5']), value: 300 }
],
jumpForwardAmount: 10,
jumpBackwardAmount: 10
}
},
computed: {
show: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
},
methods: {
setUseChapterTrack() {
this.$store.dispatch('user/updateUserSettings', { useChapterTrack: this.useChapterTrack })
},
setJumpForwardAmount(val) {
this.jumpForwardAmount = val
this.$store.dispatch('user/updateUserSettings', { jumpForwardAmount: val })
},
setJumpBackwardAmount(val) {
this.jumpBackwardAmount = val
this.$store.dispatch('user/updateUserSettings', { jumpBackwardAmount: val })
}
},
mounted() {
this.useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack')
this.jumpForwardAmount = this.$store.getters['user/getUserSetting']('jumpForwardAmount')
this.jumpBackwardAmount = this.$store.getters['user/getUserSetting']('jumpBackwardAmount')
}
}
</script>

View File

@@ -9,7 +9,7 @@
<div class="absolute top-0 right-0 p-4">
<ui-tooltip :text="$strings.LabelClickForMoreInfo" class="inline-flex ml-2">
<a href="https://www.audiobookshelf.org/guides/media-item-shares" target="_blank" class="inline-flex">
<span class="material-icons text-xl w-5 text-gray-200">help_outline</span>
<span class="material-symbols text-xl w-5 text-gray-200">help_outline</span>
</a>
</ui-tooltip>
</div>

View File

@@ -6,34 +6,36 @@
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="!timerSet" class="w-full">
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div class="w-full">
<template v-for="time in sleepTimes">
<div :key="time.text" class="flex items-center px-6 py-3 justify-center cursor-pointer hover:bg-bg relative" @click="setTime(time.seconds)">
<p class="text-xl text-center">{{ time.text }}</p>
<div :key="time.text" class="flex items-center px-6 py-3 justify-center cursor-pointer hover:bg-primary/25 relative" @click="setTime(time)">
<p class="text-lg text-center">{{ time.text }}</p>
</div>
</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="Time in minutes" class="w-48" />
<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-12 flex items-center justify-center ml-1">Set</ui-btn>
</form>
</div>
<div v-else class="w-full p-4">
<div class="mb-4 flex items-center justify-center">
<ui-btn :padding-x="2" small :disabled="remaining < 30 * 60" class="flex items-center mr-4" @click="decrement(30 * 60)">
<span class="material-icons text-lg">remove</span>
<span class="pl-1 text-base font-mono">30m</span>
<div v-if="timerSet" class="w-full p-4">
<div class="mb-4 h-px w-full bg-white/10" />
<div v-if="timerType === $constants.SleepTimerTypes.COUNTDOWN" class="mb-4 flex items-center justify-center space-x-4">
<ui-btn :padding-x="2" small :disabled="remaining < 30 * 60" class="flex items-center h-9" @click="decrement(30 * 60)">
<span class="material-symbols text-lg">remove</span>
<span class="pl-1 text-sm">30m</span>
</ui-btn>
<ui-icon-btn icon="remove" @click="decrement(60 * 5)" />
<ui-icon-btn icon="remove" class="min-w-9" @click="decrement(60 * 5)" />
<p class="mx-6 text-2xl font-mono">{{ $secondsToTimestamp(remaining) }}</p>
<p class="text-2xl font-mono">{{ $secondsToTimestamp(remaining) }}</p>
<ui-icon-btn icon="add" @click="increment(60 * 5)" />
<ui-icon-btn icon="add" class="min-w-9" @click="increment(60 * 5)" />
<ui-btn :padding-x="2" small class="flex items-center ml-4" @click="increment(30 * 60)">
<span class="material-icons text-lg">add</span>
<span class="pl-1 text-base font-mono">30m</span>
<ui-btn :padding-x="2" small class="flex items-center h-9" @click="increment(30 * 60)">
<span class="material-symbols text-lg">add</span>
<span class="pl-1 text-sm">30m</span>
</ui-btn>
</div>
<ui-btn class="w-full" @click="$emit('cancel')">{{ $strings.ButtonCancel }}</ui-btn>
@@ -47,52 +49,13 @@ export default {
props: {
value: Boolean,
timerSet: Boolean,
timerTime: Number,
remaining: Number
timerType: String,
remaining: Number,
hasChapters: Boolean
},
data() {
return {
customTime: null,
sleepTimes: [
{
seconds: 60 * 5,
text: '5 minutes'
},
{
seconds: 60 * 15,
text: '15 minutes'
},
{
seconds: 60 * 20,
text: '20 minutes'
},
{
seconds: 60 * 30,
text: '30 minutes'
},
{
seconds: 60 * 45,
text: '45 minutes'
},
{
seconds: 60 * 60,
text: '60 minutes'
},
{
seconds: 60 * 90,
text: '90 minutes'
},
{
seconds: 60 * 120,
text: '2 hours'
}
]
}
},
watch: {
show(newVal) {
if (newVal) {
}
customTime: null
}
},
computed: {
@@ -103,6 +66,54 @@ export default {
set(val) {
this.$emit('input', val)
}
},
sleepTimes() {
const times = [
{
seconds: 60 * 5,
text: this.$getString('LabelTimeDurationXMinutes', ['5']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 15,
text: this.$getString('LabelTimeDurationXMinutes', ['15']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 20,
text: this.$getString('LabelTimeDurationXMinutes', ['20']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 30,
text: this.$getString('LabelTimeDurationXMinutes', ['30']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 45,
text: this.$getString('LabelTimeDurationXMinutes', ['45']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 60,
text: this.$getString('LabelTimeDurationXMinutes', ['60']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 90,
text: this.$getString('LabelTimeDurationXMinutes', ['90']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
},
{
seconds: 60 * 120,
text: this.$getString('LabelTimeDurationXHours', ['2']),
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
}
]
if (this.hasChapters) {
times.push({ seconds: -1, text: this.$strings.LabelEndOfChapter, timerType: this.$constants.SleepTimerTypes.CHAPTER })
}
return times
}
},
methods: {
@@ -113,10 +124,14 @@ export default {
}
const timeInSeconds = Math.round(Number(this.customTime) * 60)
this.setTime(timeInSeconds)
const time = {
seconds: timeInSeconds,
timerType: this.$constants.SleepTimerTypes.COUNTDOWN
}
this.setTime(time)
},
setTime(seconds) {
this.$emit('set', seconds)
setTime(time) {
this.$emit('set', time)
},
increment(amount) {
this.$emit('increment', amount)
@@ -130,4 +145,4 @@ export default {
}
}
}
</script>
</script>

View File

@@ -12,7 +12,7 @@
</div>
<div v-if="previewUpload" class="absolute top-0 left-0 w-full h-full z-10 bg-bg p-8">
<p class="text-lg">Preview Cover</p>
<span class="absolute top-4 right-4 material-icons text-2xl cursor-pointer" @click="resetCoverPreview">close</span>
<span class="absolute top-4 right-4 material-symbols text-2xl cursor-pointer" @click="resetCoverPreview">close</span>
<div class="flex justify-center py-4">
<covers-preview-cover :src="previewUpload" :width="240" />
</div>

View File

@@ -11,7 +11,7 @@
<div class="w-full h-45 relative">
<covers-author-image :author="authorCopy" />
<div v-if="userCanDelete && !processing && author.imagePath" class="absolute top-0 left-0 w-full h-full opacity-0 hover:opacity-100">
<span class="absolute top-2 right-2 material-icons text-error transform hover:scale-125 transition-transform cursor-pointer text-lg" @click="removeCover">delete</span>
<span class="absolute top-2 right-2 material-symbols text-error transform hover:scale-125 transition-transform cursor-pointer text-lg" @click="removeCover">delete</span>
</div>
</div>
</div>

View File

@@ -12,9 +12,9 @@
<div class="flex-grow pr-2">
<ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-icons text-2xl -mt-px">forward</span></ui-btn>
<ui-btn type="submit" color="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-icons 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 text-opacity-70 hover:text-opacity-95 cursor-pointer" @click.stop.prevent="cancelEditing">close</span>
</div>
</div>
</form>
@@ -22,8 +22,8 @@
<p v-else class="pl-2 pr-2 truncate">{{ bookmark.title }}</p>
</div>
<div v-if="!isEditing" class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'">
<span class="material-icons text-xl mr-2 text-gray-200 hover:text-yellow-400" @click.stop="editClick">edit</span>
<span class="material-icons text-xl text-gray-200 hover:text-error cursor-pointer" @click.stop="deleteClick">delete</span>
<span class="material-symbols text-xl mr-2 text-gray-200 hover:text-yellow-400" @click.stop="editClick">edit</span>
<span class="material-symbols text-xl text-gray-200 hover:text-error cursor-pointer" @click.stop="deleteClick">delete</span>
</div>
</div>
</template>

View File

@@ -6,10 +6,15 @@
</div>
</template>
<div class="px-8 py-6 w-full rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-y-scroll" style="max-height: 80vh">
<p class="text-xl font-bold pb-4">
Changelog <a :href="currentTagUrl" target="_blank" class="hover:underline">v{{ currentVersionNumber }}</a> ({{ currentVersionPubDate }})
</p>
<div class="custom-text" v-html="compiledMarkedown" />
<template v-for="release in releasesToShow">
<div :key="release.name">
<p class="text-xl font-bold pb-4">
Changelog <a :href="`https://github.com/advplyr/audiobookshelf/releases/tag/${release.name}`" target="_blank" class="hover:underline">{{ release.name }}</a> ({{ $formatDate(release.pubdate, dateFormat) }})
</p>
<div class="custom-text" v-html="getChangelog(release)" />
</div>
<div v-if="release !== releasesToShow[releasesToShow.length - 1]" class="border-b border-black-300 my-8" />
</template>
</div>
</modals-modal>
</template>
@@ -37,24 +42,15 @@ export default {
dateFormat() {
return this.$store.state.serverSettings.dateFormat
},
changelog() {
return this.versionData?.currentVersionChangelog || 'No Changelog Available'
},
compiledMarkedown() {
return marked.parse(this.changelog, { gfm: true, breaks: true })
},
currentVersionPubDate() {
if (!this.versionData?.currentVersionPubDate) return 'Unknown release date'
return `${this.$formatDate(this.versionData.currentVersionPubDate, this.dateFormat)}`
},
currentTagUrl() {
return this.versionData?.currentTagUrl
},
currentVersionNumber() {
return this.$config.version
releasesToShow() {
return this.versionData?.releasesToShow || []
}
},
methods: {
getChangelog(release) {
return marked.parse(release.changelog || 'No Changelog Available', { gfm: true, breaks: true })
}
},
methods: {},
mounted() {}
}
</script>

View File

@@ -8,8 +8,8 @@
<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-icons 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-icons text-2xl pt-px">remove</span></ui-btn>
<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>
</div>
</div>
</template>

View File

@@ -28,7 +28,7 @@
<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">
<span class="material-icons text-4xl">arrow_back</span>
<span class="material-symbols text-4xl">arrow_back</span>
</div>
<p class="ml-2 text-xl mb-1">Collection Cover Image</p>
</div>

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-icons text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goPrevBook" @mousedown.prevent>arrow_back_ios</div>
<div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goPrevBook" @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-icons text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</div>
<div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</div>
</div>
<div class="w-full h-full max-h-full text-sm rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative">

View File

@@ -9,7 +9,7 @@
<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">
<ui-tooltip direction="top" :text="$strings.LabelRemoveCover">
<span class="material-icons text-2xl">delete</span>
<span class="material-symbols text-2xl">delete</span>
</ui-tooltip>
</div>
</div>
@@ -19,7 +19,7 @@
<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-icons text-2xl inline-block md:!hidden">upload</span>
<span class="material-symbols text-2xl inline-block md:!hidden">upload</span>
</ui-file-input>
</div>
@@ -73,7 +73,7 @@
<div v-if="previewUpload" class="absolute top-0 left-0 w-full h-full z-10 bg-bg p-8">
<p class="text-lg">{{ $strings.HeaderPreviewCover }}</p>
<span class="absolute top-4 right-4 material-icons text-2xl cursor-pointer" @click="resetCoverPreview">close</span>
<span class="absolute top-4 right-4 material-symbols text-2xl cursor-pointer" @click="resetCoverPreview">close</span>
<div class="flex justify-center py-4">
<covers-preview-cover :src="previewUpload" :width="240" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div>

View File

@@ -7,7 +7,7 @@
<div class="flex -mb-0.5">
<p class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': checkingNewEpisodes }">{{ $strings.LabelLimit }}</p>
<ui-tooltip direction="top" text="Max # of episodes to download. Use 0 for unlimited.">
<span class="material-icons text-base">info_outlined</span>
<span class="material-symbols text-base">info</span>
</ui-tooltip>
</div>
</ui-text-input-with-label>

View File

@@ -28,7 +28,7 @@
<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">
<span class="material-icons text-3xl">arrow_back</span>
<span class="material-symbols text-3xl">arrow_back</span>
</div>
<p class="text-xl pl-3">{{ $strings.HeaderUpdateDetails }}</p>
</div>
@@ -59,49 +59,63 @@
<ui-checkbox v-model="selectedMatchUsage.title" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-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">{{ $strings.LabelCurrently }} {{ mediaMetadata.title || '' }}</p>
<p v-if="mediaMetadata.title" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.subtitle || '' }}</p>
<p v-if="mediaMetadata.subtitle" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<ui-text-input-with-label v-model="selectedMatch.author" :disabled="!selectedMatchUsage.author" :label="$strings.LabelAuthor" />
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.authorName || '' }}</p>
<p v-if="mediaMetadata.authorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<ui-multi-select v-model="selectedMatch.narrator" :items="narrators" :disabled="!selectedMatchUsage.narrator" :label="$strings.LabelNarrators" />
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.narratorName || '' }}</p>
<p v-if="mediaMetadata.narratorName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<ui-textarea-with-label v-model="selectedMatch.description" :rows="3" :disabled="!selectedMatchUsage.description" :label="$strings.LabelDescription" />
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.length > 100 ? '...' : '') }}</p>
<p v-if="mediaMetadata.description" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('description', mediaMetadata.description)">{{ mediaMetadata.description.substr(0, 100) + (mediaMetadata.description.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">
<ui-text-input-with-label v-model="selectedMatch.publisher" :disabled="!selectedMatchUsage.publisher" :label="$strings.LabelPublisher" />
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.publisher || '' }}</p>
<p v-if="mediaMetadata.publisher" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<ui-text-input-with-label v-model="selectedMatch.publishedYear" :disabled="!selectedMatchUsage.publishedYear" :label="$strings.LabelPublishYear" />
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.publishedYear || '' }}</p>
<p v-if="mediaMetadata.publishedYear" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('publishedYear', mediaMetadata.publishedYear)">{{ mediaMetadata.publishedYear }}</a>
</p>
</div>
</div>
@@ -109,42 +123,54 @@
<ui-checkbox v-model="selectedMatchUsage.series" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-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">{{ $strings.LabelCurrently }} {{ mediaMetadata.seriesName || '' }}</p>
<p v-if="mediaMetadata.seriesName" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('series', mediaMetadata.series)">{{ mediaMetadata.seriesName }}</a>
</p>
</div>
</div>
<div v-if="selectedMatchOrig.genres && selectedMatchOrig.genres.length" class="flex items-center py-2">
<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">
<ui-multi-select v-model="selectedMatch.genres" :items="genres" :disabled="!selectedMatchUsage.genres" :label="$strings.LabelGenres" />
<p v-if="mediaMetadata.genres" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ mediaMetadata.genres.join(', ') }}</p>
<p v-if="mediaMetadata.genres?.length" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<ui-multi-select v-model="selectedMatch.tags" :items="tags" :disabled="!selectedMatchUsage.tags" :label="$strings.LabelTags" />
<p v-if="media.tags" class="text-xs ml-1 text-white text-opacity-60">{{ $strings.LabelCurrently }} {{ media.tags.join(', ') }}</p>
<p v-if="media.tags?.length" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.language || '' }}</p>
<p v-if="mediaMetadata.language" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.isbn || '' }}</p>
<p v-if="mediaMetadata.isbn" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.asin || '' }}</p>
<p v-if="mediaMetadata.asin" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" class="cursor-pointer hover:underline" @click.stop="setMatchFieldValue('asin', mediaMetadata.asin)">{{ mediaMetadata.asin }}</a>
</p>
</div>
</div>
@@ -152,28 +178,36 @@
<ui-checkbox v-model="selectedMatchUsage.itunesId" checkbox-bg="bg" @input="checkboxToggled" />
<div class="flex-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">{{ $strings.LabelCurrently }} {{ mediaMetadata.itunesId || '' }}</p>
<p v-if="mediaMetadata.itunesId" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.feedUrl || '' }}</p>
<p v-if="mediaMetadata.feedUrl" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.itunesPageUrl || '' }}</p>
<p v-if="mediaMetadata.itunesPageUrl" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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">
<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">{{ $strings.LabelCurrently }} {{ mediaMetadata.releaseDate || '' }}</p>
<p v-if="mediaMetadata.releaseDate" class="text-xs ml-1 text-white text-opacity-60">
{{ $strings.LabelCurrently }} <a title="Click to use current value" 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 }">
@@ -281,7 +315,7 @@ export default {
return this.$store.getters['libraries/getBookCoverAspectRatio']
},
filterData() {
return this.$store.state.libraries.filterData
return this.$store.state.libraries.filterData || {}
},
providers() {
if (this.isPodcast) return this.$store.state.scanners.podcastProviders
@@ -321,6 +355,13 @@ export default {
}
},
methods: {
setMatchFieldValue(field, value) {
if (Array.isArray(value)) {
this.selectedMatch[field] = [...value]
} else {
this.selectedMatch[field] = value
}
},
selectAllToggled(val) {
for (const key in this.selectedMatchUsage) {
this.selectedMatchUsage[key] = val

View File

@@ -15,7 +15,7 @@
<ui-tooltip text="Value of 0 sets no max limit. After a new episode is auto-downloaded this will delete the oldest episode if you have more than X episodes. <br>This will only delete 1 episode per new download.">
<p class="pl-4 text-base">
Max episodes to keep
<span class="material-icons icon-text">info_outlined</span>
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
</div>
@@ -24,7 +24,7 @@
<ui-tooltip text="Value of 0 sets no max limit. When checking for new episodes this is the max number of episodes that will be downloaded.">
<p class="pl-4 text-base">
Max new episodes to download per check
<span class="material-icons icon-text">info_outlined</span>
<span class="material-symbols icon-text">info</span>
</p>
</ui-tooltip>
</div>

View File

@@ -13,7 +13,7 @@
<div>
<ui-btn :to="`/audiobook/${libraryItemId}/manage?tool=m4b`" class="flex items-center"
>{{ $strings.ButtonOpenManager }}
<span class="material-icons text-lg ml-2">launch</span>
<span class="material-symbols text-lg ml-2">launch</span>
</ui-btn>
</div>
</div>
@@ -30,7 +30,7 @@
<div>
<ui-btn :to="`/audiobook/${libraryItemId}/manage?tool=embed`" class="flex items-center"
>{{ $strings.ButtonOpenManager }}
<span class="material-icons text-lg ml-2">launch</span>
<span class="material-symbols text-lg ml-2">launch</span>
</ui-btn>
<ui-btn v-if="!isMetadataEmbedQueued && !isEmbedTaskRunning" class="w-full mt-4" small @click.stop="quickEmbed">Quick Embed</ui-btn>

View File

@@ -19,12 +19,12 @@
<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-icons bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill bg-opacity-50 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-icons text-2xl ml-2 cursor-pointer hover:text-error" @click="removeFolder(folder)">close</span>
<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-icons bg-opacity-50 mr-2 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill bg-opacity-50 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>
@@ -169,4 +169,4 @@ export default {
max-height: calc(80vh - 292px);
}
}
</style>
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div class="w-full h-full bg-bg absolute top-0 left-0 px-4 py-4 z-10">
<div class="flex items-center py-1 mb-2">
<span class="material-icons text-3xl cursor-pointer hover:text-gray-300" @click="$emit('back')">arrow_back</span>
<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">
@@ -10,18 +10,18 @@
<div v-if="rootDirs.length" class="relative flex bg-primary bg-opacity-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-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill bg-opacity-50 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-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill bg-opacity-50 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-icons" style="font-size: 1.1rem">arrow_right</span>
<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-icons bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<span class="material-symbols fill bg-opacity-50 text-yellow-200" style="font-size: 1.2rem">folder</span>
<p class="text-base font-mono px-2 truncate">{{ dir.dirname }}</p>
</div>
</div>
@@ -199,4 +199,4 @@ export default {
height: calc(100% - 130px);
min-height: calc(100% - 130px);
}
</style>
</style>

View File

@@ -9,7 +9,7 @@
<p class="text-sm text-gray-300 pr-2">{{ $strings.LabelMetadataOrderOfPrecedenceDescription }}</p>
<ui-tooltip :text="$strings.LabelClickForMoreInfo" class="inline-flex">
<a href="https://www.audiobookshelf.org/guides/book-scanner" target="_blank" class="inline-flex">
<span class="material-icons text-xl w-5">help_outline</span>
<span class="material-symbols text-xl w-5">help_outline</span>
</a>
</ui-tooltip>
</div>
@@ -17,7 +17,7 @@
<draggable v-model="metadataSourceMapped" v-bind="dragOptions" class="list-group" draggable=".item" handle=".drag-handle" tag="ul" @start="drag = true" @end="drag = false" @update="draggableUpdate">
<transition-group type="transition" :name="!drag ? 'flip-list' : null">
<li v-for="(source, index) in metadataSourceMapped" :key="source.id" :class="source.include ? 'item' : 'opacity-50'" class="w-full px-2 flex items-center relative border border-white/10">
<span class="material-icons drag-handle text-xl text-gray-400 hover:text-gray-50 mr-2 md:mr-4">reorder</span>
<span class="material-symbols drag-handle text-xl text-gray-400 hover:text-gray-50 mr-2 md:mr-4">reorder</span>
<div class="text-center py-1 w-8 min-w-8">
{{ source.include ? getSourceIndex(source.id) : '' }}
</div>

View File

@@ -5,7 +5,7 @@
<ui-tooltip :text="$strings.LabelSettingsSquareBookCoversHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsSquareBookCovers }}
<span class="material-icons icon-text text-sm">info_outlined</span>
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
@@ -22,7 +22,7 @@
<ui-tooltip :text="$strings.LabelSettingsAudiobooksOnlyHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsAudiobooksOnly }}
<span class="material-icons icon-text text-sm">info_outlined</span>
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
@@ -44,7 +44,7 @@
<ui-tooltip :text="$strings.LabelSettingsHideSingleBookSeriesHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsHideSingleBookSeries }}
<span class="material-icons icon-text text-sm">info_outlined</span>
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
@@ -55,7 +55,7 @@
<ui-tooltip :text="$strings.LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsOnlyShowLaterBooksInContinueSeries }}
<span class="material-icons icon-text text-sm">info_outlined</span>
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>
@@ -66,7 +66,7 @@
<ui-tooltip :text="$strings.LabelSettingsEpubsAllowScriptedContentHelp">
<p class="pl-4 text-base">
{{ $strings.LabelSettingsEpubsAllowScriptedContent }}
<span class="material-icons icon-text text-sm">info_outlined</span>
<span class="material-symbols icon-text text-sm">info</span>
</p>
</ui-tooltip>
</div>

View File

@@ -10,10 +10,10 @@
<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">
<span class="material-icons text-2xl text-success">play_arrow</span>
<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">
<span class="material-icons text-2xl text-error">close</span>
<span class="material-symbols text-2xl text-error">close</span>
</button>
</div>
<p v-else class="text-gray-400 text-sm text-right">{{ durationPretty }}</p>

View File

@@ -8,8 +8,8 @@
<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-icons 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-icons text-2xl pt-px">remove</span></ui-btn>
<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>
</div>
</div>
</template>

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-icons 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 text-opacity-50 hover:text-opacity-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-icons 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 text-opacity-50 hover:text-opacity-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

@@ -20,7 +20,7 @@
@click="toggleSelectEpisode(episode)"
>
<div class="absolute top-0 left-0 h-full flex items-center p-2">
<span v-if="getIsEpisodeDownloaded(episode)" class="material-icons text-success text-xl">download_done</span>
<span v-if="getIsEpisodeDownloaded(episode)" class="material-symbols text-success text-xl">download_done</span>
<ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
</div>
<div class="px-8 py-2">

View File

@@ -16,11 +16,18 @@
</div>
</div>
<p class="text-lg font-semibold mb-2">{{ $strings.HeaderPodcastsToAdd }}</p>
<p class="text-lg font-semibold mb-1">{{ $strings.HeaderPodcastsToAdd }}</p>
<p class="text-sm text-gray-300 mb-4">{{ $strings.MessageOpmlPreviewNote }}</p>
<div class="w-full overflow-y-auto" style="max-height: 50vh">
<template v-for="(feed, index) in feedMetadata">
<cards-podcast-feed-summary-card :key="index" :feed="feed" :library-folder-path="selectedFolderPath" class="my-1" />
<template v-for="(feed, index) in feeds">
<div :key="index" class="py-1 flex items-center">
<p class="text-lg font-semibold">{{ index + 1 }}.</p>
<div class="pl-2">
<p v-if="feed.title" class="text-sm font-semibold">{{ feed.title }}</p>
<p class="text-xs text-gray-400">{{ feed.feedUrl }}</p>
</div>
</div>
</template>
</div>
</div>
@@ -45,9 +52,7 @@ export default {
return {
processing: false,
selectedFolderId: null,
fullPath: null,
autoDownloadEpisodes: false,
feedMetadata: []
autoDownloadEpisodes: false
}
},
watch: {
@@ -96,73 +101,36 @@ export default {
}
},
methods: {
toFeedMetadata(feed) {
const metadata = feed.metadata
return {
title: metadata.title,
author: metadata.author,
description: metadata.description,
releaseDate: '',
genres: [...metadata.categories],
feedUrl: metadata.feedUrl,
imageUrl: metadata.image,
itunesPageUrl: '',
itunesId: '',
itunesArtistId: '',
language: '',
numEpisodes: feed.numEpisodes
}
},
init() {
this.feedMetadata = this.feeds.map(this.toFeedMetadata)
if (this.folderItems[0]) {
this.selectedFolderId = this.folderItems[0].value
}
},
async submit() {
this.processing = true
const newFeedPayloads = this.feedMetadata.map((metadata) => {
return {
path: `${this.selectedFolderPath}/${this.$sanitizeFilename(metadata.title)}`,
folderId: this.selectedFolderId,
libraryId: this.currentLibrary.id,
media: {
metadata: {
...metadata
},
autoDownloadEpisodes: this.autoDownloadEpisodes
}
}
})
console.log('New feed payloads', newFeedPayloads)
for (const podcastPayload of newFeedPayloads) {
await this.$axios
.$post('/api/podcasts', podcastPayload)
.then(() => {
this.$toast.success(`${podcastPayload.media.metadata.title}: ${this.$strings.ToastPodcastCreateSuccess}`)
})
.catch((error) => {
var errorMsg = error.response && error.response.data ? error.response.data : this.$strings.ToastPodcastCreateFailed
console.error('Failed to create podcast', podcastPayload, error)
this.$toast.error(`${podcastPayload.media.metadata.title}: ${errorMsg}`)
})
const payload = {
feeds: this.feeds.map((f) => f.feedUrl),
folderId: this.selectedFolderId,
libraryId: this.currentLibrary.id,
autoDownloadEpisodes: this.autoDownloadEpisodes
}
this.processing = false
this.show = false
this.$axios
.$post('/api/podcasts/opml/create', payload)
.then(() => {
this.show = false
})
.catch((error) => {
const errorMsg = error.response?.data || this.$strings.ToastPodcastCreateFailed
console.error('Failed to create podcast', payload, error)
this.$toast.error(errorMsg)
})
.finally(() => {
this.processing = false
})
}
},
mounted() {}
}
</script>
<style scoped>
#podcast-wrapper {
min-height: 400px;
max-height: 80vh;
}
#episodes-scroll {
max-height: calc(80vh - 200px);
}
</style>

View File

@@ -132,7 +132,7 @@ export default {
this.searchedTitle = this.episodeTitle
this.isProcessing = true
this.$axios
.$get(`/api/podcasts/${this.libraryItem.id}/search-episode?title=${this.$encodeUriPath(this.episodeTitle)}`)
.$get(`/api/podcasts/${this.libraryItem.id}/search-episode?title=${encodeURIComponent(this.episodeTitle)}`)
.then((results) => {
this.episodesFound = results.episodes.map((ep) => ep.episode)
console.log('Episodes found', this.episodesFound)
@@ -153,4 +153,4 @@ export default {
},
mounted() {}
}
</script>
</script>

View File

@@ -12,7 +12,7 @@
<div class="w-full relative">
<ui-text-input v-model="currentFeed.feedUrl" readonly />
<span class="material-icons absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(currentFeed.feedUrl)">content_copy</span>
<span class="material-symbols absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(currentFeed.feedUrl)">content_copy</span>
</div>
<div v-if="currentFeed.meta" class="mt-5">

View File

@@ -6,7 +6,7 @@
<div class="w-full relative">
<ui-text-input v-model="feed.feedUrl" readonly />
<span class="material-icons absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(feed.feedUrl)">content_copy</span>
<span class="material-symbols absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(feed.feedUrl)">content_copy</span>
</div>
<div v-if="feed.meta" class="mt-5">

View File

@@ -4,32 +4,32 @@
<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">
<span class="material-icons text-2xl sm:text-3xl">first_page</span>
<span class="material-symbols text-2xl sm:text-3xl">first_page</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="$strings.ButtonJumpBackward">
<button :aria-label="$strings.ButtonJumpBackward" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<span class="material-icons text-2xl sm:text-3xl">replay_10</span>
<ui-tooltip direction="top" :text="jumpBackwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpBackward">
<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">
<span class="material-icons text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
<span class="material-symbols fill text-2xl">{{ seekLoading ? 'autorenew' : paused ? 'play_arrow' : 'pause' }}</span>
</button>
<ui-tooltip direction="top" :text="$strings.ButtonJumpForward">
<button :aria-label="$strings.ButtonJumpForward" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-icons text-2xl sm:text-3xl">forward_10</span>
<ui-tooltip direction="top" :text="jumpForwardText">
<button :aria-label="jumpForwardText" class="text-gray-300" @mousedown.prevent @mouseup.prevent @click.stop="jumpForward">
<span class="material-symbols text-2xl sm:text-3xl">forward_media</span>
</button>
</ui-tooltip>
<ui-tooltip direction="top" :text="$strings.ButtonNextChapter" class="ml-4 lg:ml-8">
<button :aria-label="$strings.ButtonNextChapter" :disabled="!hasNextChapter" :class="hasNextChapter ? 'text-gray-300' : 'text-gray-500'" @mousedown.prevent @mouseup.prevent @click.stop="nextChapter">
<span class="material-icons text-2xl sm:text-3xl">last_page</span>
<span class="material-symbols text-2xl sm:text-3xl">last_page</span>
</button>
</ui-tooltip>
<controls-playback-speed-control v-model="playbackRateInput" @input="playbackRateUpdated" @change="playbackRateChanged" />
</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">
<span class="material-icons">autorenew</span>
<span class="material-symbols text-2xl">autorenew</span>
</div>
</template>
<div class="flex-grow" />
@@ -56,6 +56,12 @@ export default {
set(val) {
this.$emit('update:playbackRate', val)
}
},
jumpForwardText() {
return this.getJumpText('jumpForwardAmount', this.$strings.ButtonJumpForward)
},
jumpBackwardText() {
return this.getJumpText('jumpBackwardAmount', this.$strings.ButtonJumpBackward)
}
},
methods: {
@@ -83,8 +89,22 @@ export default {
this.$store.dispatch('user/updateUserSettings', { playbackRate }).catch((err) => {
console.error('Failed to update settings', err)
})
},
getJumpText(setting, prefix) {
const amount = this.$store.getters['user/getUserSetting'](setting)
if (!amount) return prefix
let formattedTime = ''
if (amount <= 60) {
formattedTime = this.$getString('LabelTimeDurationXSeconds', [amount])
} else {
const minutes = Math.floor(amount / 60)
formattedTime = this.$getString('LabelTimeDurationXMinutes', [minutes])
}
return `${prefix} - ${formattedTime}`
}
},
mounted() {}
}
</script>
</script>

View File

@@ -2,7 +2,7 @@
<div class="w-full -mt-6">
<div class="w-full relative mb-1">
<div class="absolute -top-10 lg:top-0 right-0 lg:right-2 flex items-center h-full">
<!-- <span class="material-icons text-2xl cursor-pointer" @click="toggleFullscreen(true)">expand_less</span> -->
<!-- <span class="material-symbols text-2xl cursor-pointer" @click="toggleFullscreen(true)">expand_less</span> -->
<ui-tooltip direction="top" :text="$strings.LabelVolume">
<controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden sm:block" />
@@ -10,35 +10,35 @@
<ui-tooltip v-if="!hideSleepTimer" direction="top" :text="$strings.LabelSleepTimer">
<button :aria-label="$strings.LabelSleepTimer" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')">
<span v-if="!sleepTimerSet" class="material-icons text-2xl">snooze</span>
<span v-if="!sleepTimerSet" class="material-symbols text-2xl">snooze</span>
<div v-else class="flex items-center">
<span class="material-icons text-lg text-warning">snooze</span>
<p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p>
<span class="material-symbols text-lg text-warning">snooze</span>
<p class="text-sm sm:text-lg text-warning font-mono font-semibold text-center px-0.5 sm:pb-0.5 sm:min-w-8">{{ sleepTimerRemainingString }}</p>
</div>
</button>
</ui-tooltip>
<ui-tooltip v-if="!isPodcast && !hideBookmarks" direction="top" :text="$strings.LabelViewBookmarks">
<button :aria-label="$strings.LabelViewBookmarks" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')">
<span class="material-icons text-2xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
<span class="material-symbols text-2xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span>
</button>
</ui-tooltip>
<ui-tooltip v-if="chapters.length" direction="top" :text="$strings.LabelViewChapters">
<button :aria-label="$strings.LabelViewChapters" class="text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters">
<span class="material-icons text-2xl">format_list_bulleted</span>
<span class="material-symbols text-2xl">format_list_bulleted</span>
</button>
</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')">
<span class="material-icons text-2.5xl sm:text-3xl">playlist_play</span>
<span class="material-symbols text-2.5xl sm:text-3xl">playlist_play</span>
</button>
</ui-tooltip>
<ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack">
<button :aria-label="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack" class="text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="setUseChapterTrack">
<span class="material-icons text-2xl sm:text-3xl transform transition-transform" :class="useChapterTrack ? 'rotate-180' : ''">timelapse</span>
<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="$emit('showPlayerSettings')">
<span class="material-symbols text-2xl sm:text-2.5xl">settings_slow_motion</span>
</button>
</ui-tooltip>
</div>
@@ -72,12 +72,14 @@ export default {
type: Array,
default: () => []
},
currentChapter: Object,
bookmarks: {
type: Array,
default: () => []
},
sleepTimerSet: Boolean,
sleepTimerRemaining: Number,
sleepTimerType: String,
isPodcast: Boolean,
hideBookmarks: Boolean,
hideSleepTimer: Boolean
@@ -90,27 +92,34 @@ export default {
seekLoading: false,
showChaptersModal: false,
currentTime: 0,
duration: 0,
useChapterTrack: false
duration: 0
}
},
watch: {
playbackRate() {
this.updateTimestamp()
},
useChapterTrack() {
if (this.$refs.trackbar) this.$refs.trackbar.setUseChapterTrack(this.useChapterTrack)
this.updateTimestamp()
}
},
computed: {
sleepTimerRemainingString() {
var rounded = Math.round(this.sleepTimerRemaining)
if (rounded < 90) {
return `${rounded}s`
if (this.sleepTimerType === this.$constants.SleepTimerTypes.CHAPTER) {
return 'EoC'
} else {
var rounded = Math.round(this.sleepTimerRemaining)
if (rounded < 90) {
return `${rounded}s`
}
var minutesRounded = Math.round(rounded / 60)
if (minutesRounded <= 90) {
return `${minutesRounded}m`
}
var hoursRounded = Math.round(minutesRounded / 60)
return `${hoursRounded}h`
}
var minutesRounded = Math.round(rounded / 60)
if (minutesRounded < 90) {
return `${minutesRounded}m`
}
var hoursRounded = Math.round(minutesRounded / 60)
return `${hoursRounded}h`
},
token() {
return this.$store.getters['user/getToken']
@@ -135,9 +144,6 @@ export default {
if (!duration) return 0
return Math.round((100 * time) / duration)
},
currentChapter() {
return this.chapters.find((chapter) => chapter.start <= this.currentTime && this.currentTime < chapter.end)
},
currentChapterName() {
return this.currentChapter ? this.currentChapter.title : ''
},
@@ -162,6 +168,10 @@ export default {
},
playerQueueItems() {
return this.$store.state.playerQueueItems || []
},
useChapterTrack() {
const _useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack') || false
return this.chapters.length ? _useChapterTrack : false
}
},
methods: {
@@ -310,9 +320,6 @@ export default {
init() {
this.playbackRate = this.$store.getters['user/getUserSetting']('playbackRate') || 1
const _useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack') || false
this.useChapterTrack = this.chapters.length ? _useChapterTrack : false
if (this.$refs.trackbar) this.$refs.trackbar.setUseChapterTrack(this.useChapterTrack)
this.setPlaybackRate(this.playbackRate)
},

View File

@@ -15,13 +15,13 @@
</div>
<div v-if="numPages" class="absolute top-0 left-4 sm:left-8 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowPageMenu">
<span class="material-icons text-xl">menu</span>
<span class="material-symbols text-xl">menu</span>
</div>
<div v-if="comicMetadata" class="absolute top-0 left-16 sm:left-20 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowInfoMenu">
<span class="material-icons text-xl">more</span>
<span class="material-symbols text-xl">more</span>
</div>
<a v-if="pages && numPages" :href="mainImg" :download="pages[page - 1]" class="absolute top-0 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" :class="comicMetadata ? 'left-28 sm:left-32' : 'left-16 sm:left-20'">
<span class="material-icons text-xl">download</span>
<span class="material-symbols text-xl">download</span>
</a>
<div v-if="numPages" class="absolute top-0 right-14 sm:right-16 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20">
@@ -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-icons 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 cursor-pointer text-opacity-30 hover:text-opacity-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-icons 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 cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
</div>
</div>
<div ref="imageContainer" class="w-full h-full relative overflow-auto">

View File

@@ -2,13 +2,13 @@
<div id="epub-reader" class="h-full w-full">
<div class="h-full flex items-center justify-center">
<button type="button" aria-label="Previous page" class="w-24 max-w-24 h-full hidden sm:flex items-center overflow-x-hidden justify-center opacity-50 hover:opacity-100">
<span v-if="hasPrev" class="material-icons text-6xl" @mousedown.prevent @click="prev">chevron_left</span>
<span v-if="hasPrev" class="material-symbols text-6xl" @mousedown.prevent @click="prev">chevron_left</span>
</button>
<div id="frame" class="w-full" style="height: 80%">
<div id="viewer"></div>
</div>
<button type="button" aria-label="Next page" class="w-24 max-w-24 h-full hidden sm:flex items-center justify-center overflow-x-hidden opacity-50 hover:opacity-100">
<span v-if="hasNext" class="material-icons text-6xl" @mousedown.prevent @click="next">chevron_right</span>
<span v-if="hasNext" class="material-symbols text-6xl" @mousedown.prevent @click="next">chevron_right</span>
</button>
</div>
</div>

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-icons 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 cursor-pointer text-opacity-30 hover:text-opacity-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-icons 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 cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_forward_ios</span>
</div>
</div>

View File

@@ -2,10 +2,10 @@
<div v-if="show" id="reader" :data-theme="ereaderTheme" class="group absolute top-0 left-0 w-full z-60 data-[theme=dark]:bg-primary data-[theme=dark]:text-white data-[theme=light]:bg-white data-[theme=light]:text-black" :class="{ 'reader-player-open': !!streamLibraryItem }">
<div class="absolute top-4 left-4 z-20 flex items-center">
<button v-if="isEpub" @click="toggleToC" type="button" aria-label="Table of contents menu" class="inline-flex opacity-80 hover:opacity-100">
<span class="material-icons text-2xl">menu</span>
<span class="material-symbols text-2xl">menu</span>
</button>
<button v-if="hasSettings" @click="openSettings" type="button" aria-label="Ereader settings" class="mx-4 inline-flex opacity-80 hover:opacity-100">
<span class="material-icons text-1.5xl">settings</span>
<span class="material-symbols text-1.5xl">settings</span>
</button>
</div>
@@ -19,7 +19,7 @@
<div class="absolute top-4 right-4 z-20">
<button @click="close" type="button" aria-label="Close ereader" class="inline-flex opacity-80 hover:opacity-100">
<span class="material-icons text-2xl">close</span>
<span class="material-symbols text-2xl">close</span>
</button>
</div>
@@ -31,7 +31,7 @@
<div class="flex flex-col p-4 h-full">
<div class="flex items-center mb-2">
<button @click.stop.prevent="toggleToC" type="button" aria-label="Close table of contents" class="inline-flex opacity-80 hover:opacity-100">
<span class="material-icons text-2xl">arrow_back</span>
<span class="material-symbols text-2xl">arrow_back</span>
</button>
<p class="text-lg font-semibold ml-2">{{ $strings.HeaderTableOfContents }}</p>

View File

@@ -11,7 +11,7 @@
</div>
<div class="flex p-2">
<span class="material-icons text-5xl py-1">show_chart</span>
<span class="material-symbols text-5xl py-1">show_chart</span>
<div class="px-1">
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalTime) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ useOverallHours ? $strings.LabelStatsOverallHours : $strings.LabelStatsOverallDays }}</p>
@@ -29,7 +29,7 @@
</div>
<div class="flex p-2">
<span class="material-icons-outlined text-5xl pt-1">insert_drive_file</span>
<span class="material-symbols-outlined text-5xl pt-1">insert_drive_file</span>
<div class="px-1">
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(totalSizeNum) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelSize }} ({{ totalSizeMod }})</p>
@@ -37,7 +37,7 @@
</div>
<div class="flex p-2">
<span class="material-icons-outlined text-5xl pt-1">audio_file</span>
<span class="material-symbols-outlined text-5xl pt-1">audio_file</span>
<div class="px-1">
<p class="text-4.5xl leading-none font-bold">{{ $formatNumber(numAudioTracks) }}</p>
<p class="text-xs md:text-sm text-white text-opacity-80">{{ $strings.LabelStatsAudioTracks }}</p>

View File

@@ -73,7 +73,7 @@ export default {
const addIcon = (icon, color, fontSize, x, y) => {
ctx.fillStyle = color
ctx.font = `${fontSize} Material Icons Outlined`
ctx.font = `${fontSize} Material Symbols Outlined`
ctx.fillText(icon, x, y)
}

View File

@@ -2,15 +2,14 @@
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-1 sm:p-4 mb-4">
<!-- hack to get icon fonts loaded on init -->
<div class="h-0 w-0 overflow-hidden opacity-0">
<span class="material-icons-outlined">close</span>
<span class="material-symbols-outlined">close</span>
<span class="abs-icons icon-audiobookshelf" />
</div>
<div class="flex items-center">
<p class="hidden md:block text-xl font-semibold">{{ $getString('HeaderYearReview', [yearInReviewYear]) }}</p>
<div class="hidden md:block flex-grow" />
<ui-btn class="w-full md:w-auto" @click.stop="clickShowYearInReview">{{ showYearInReview ? $strings.LabelYearReviewHide :
$strings.LabelYearReviewShow }}</ui-btn>
<ui-btn class="w-full md:w-auto" @click.stop="clickShowYearInReview">{{ showYearInReview ? $strings.LabelYearReviewHide : $strings.LabelYearReviewShow }}</ui-btn>
</div>
<!-- your year in review -->
@@ -20,29 +19,26 @@
<div class="flex items-center justify-center mb-2 max-w-[800px] mx-auto">
<!-- previous button -->
<ui-btn small :disabled="!yearInReviewVariant || processingYearInReview" class="inline-flex items-center font-semibold" @click="yearInReviewVariant--">
<span class="material-icons text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
<span class="material-symbols text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
<span class="hidden sm:inline-block pr-2">{{ $strings.ButtonPrevious }}</span>
</ui-btn>
<!-- share button -->
<ui-btn v-if="showShareButton" small :disabled="processingYearInReview" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReview">{{
$strings.ButtonShare }}
</ui-btn>
<ui-btn v-if="showShareButton" small :disabled="processingYearInReview" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReview">{{ $strings.ButtonShare }} </ui-btn>
<div class="flex-grow" />
<p class="hidden sm:block text-lg font-semibold">{{ $getString('LabelPersonalYearReview', [yearInReviewVariant + 1]) }}
</p>
<p class="hidden sm:block text-lg font-semibold">{{ $getString('LabelPersonalYearReview', [yearInReviewVariant + 1]) }}</p>
<p class="block sm:hidden text-lg font-semibold">{{ yearInReviewVariant + 1 }}</p>
<div class="flex-grow" />
<!-- refresh button -->
<ui-btn small :disabled="processingYearInReview" class="inline-flex items-center font-semibold mr-1 sm:mr-2" @click="refreshYearInReview">
<span class="hidden sm:inline-block">{{ $strings.ButtonRefresh }}</span>
<span class="material-icons sm:!hidden text-lg py-px">refresh</span>
<span class="material-symbols sm:!hidden text-lg py-px">refresh</span>
</ui-btn>
<!-- next button -->
<ui-btn small :disabled="yearInReviewVariant >= 2 || processingYearInReview" class="inline-flex items-center font-semibold" @click="yearInReviewVariant++">
<span class="hidden sm:inline-block pl-2">{{ $strings.ButtonNext }}</span>
<span class="material-icons-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
<span class="material-symbols-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
</ui-btn>
</div>
<stats-year-in-review ref="yearInReview" :variant="yearInReviewVariant" :year="yearInReviewYear" :processing.sync="processingYearInReview" />
@@ -59,12 +55,11 @@
<div class="flex items-center justify-center mb-2">
<!-- previous button -->
<ui-btn small :disabled="!yearInReviewServerVariant || processingYearInReviewServer" class="inline-flex items-center font-semibold" @click="yearInReviewServerVariant--">
<span class="material-icons text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
<span class="material-symbols text-lg sm:pr-1 py-px sm:py-0">chevron_left</span>
<span class="hidden sm:inline-block pr-2">{{ $strings.ButtonPrevious }}</span>
</ui-btn>
<!-- share button -->
<ui-btn v-if="showShareButton" small :disabled="processingYearInReviewServer" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReviewServer">{{ $strings.ButtonShare }}
</ui-btn>
<ui-btn v-if="showShareButton" small :disabled="processingYearInReviewServer" class="inline-flex sm:hidden items-center font-semibold ml-1 sm:ml-2" @click="shareYearInReviewServer">{{ $strings.ButtonShare }} </ui-btn>
<div class="flex-grow" />
<p class="hidden sm:block text-lg font-semibold">{{ $getString('LabelServerYearReview', [yearInReviewServerVariant + 1]) }}</p>
@@ -74,12 +69,12 @@
<!-- refresh button -->
<ui-btn small :disabled="processingYearInReviewServer" class="inline-flex items-center font-semibold mr-1 sm:mr-2" @click="refreshYearInReviewServer">
<span class="hidden sm:inline-block">{{ $strings.ButtonRefresh }}</span>
<span class="material-icons sm:!hidden text-lg py-px">refresh</span>
<span class="material-symbols sm:!hidden text-lg py-px">refresh</span>
</ui-btn>
<!-- next button -->
<ui-btn small :disabled="yearInReviewServerVariant >= 2 || processingYearInReviewServer" class="inline-flex items-center font-semibold" @click="yearInReviewServerVariant++">
<span class="hidden sm:inline-block pl-2">{{ $strings.ButtonNext }}</span>
<span class="material-icons-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
<span class="material-symbols-outlined text-lg sm:pl-1 py-px sm:py-0">chevron_right</span>
</ui-btn>
</div>
</div>

View File

@@ -64,7 +64,7 @@ export default {
const addIcon = (icon, color, fontSize, x, y) => {
ctx.fillStyle = color
ctx.font = `${fontSize} Material Icons Outlined`
ctx.font = `${fontSize} Material Symbols Outlined`
ctx.fillText(icon, x, y)
}

View File

@@ -23,12 +23,12 @@
<div class="w-full flex flex-row items-center justify-center">
<ui-btn v-if="backup.serverVersion && backup.key" small color="primary" @click="applyBackup(backup)">{{ $strings.ButtonRestore }}</ui-btn>
<ui-tooltip v-else text="This backup was created with an old version of audiobookshelf no longer supported" direction="bottom" class="mx-2 flex items-center">
<span class="material-icons-outlined text-2xl text-error">error_outline</span>
<span class="material-symbols-outlined text-2xl text-error">error_outline</span>
</ui-tooltip>
<button aria-label="Download Backup" class="inline-flex material-icons text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button>
<button aria-label="Download Backup" class="inline-flex material-symbols text-xl mx-1 mt-1 text-white/70 hover:text-white/100" @click.stop="downloadBackup(backup)">download</button>
<button aria-label="Delete Backup" class="inline-flex material-icons text-xl mx-1 text-white/70 hover:text-error" @click="deleteBackupClick(backup)">delete</button>
<button aria-label="Delete Backup" class="inline-flex material-symbols text-xl mx-1 text-white/70 hover:text-error" @click="deleteBackupClick(backup)">delete</button>
</div>
</td>
</tr>

View File

@@ -6,7 +6,7 @@
<div class="flex-grow" />
<ui-btn v-if="userCanUpdate" small :to="`/audiobook/${libraryItemId}/chapters`" color="primary" class="mr-2" @click="clickEditChapters">{{ $strings.ButtonEditChapters }}</ui-btn>
<div v-if="!keepOpen" class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="expanded ? 'transform rotate-180' : ''">
<span class="material-icons text-4xl">expand_more</span>
<span class="material-symbols text-4xl">expand_more</span>
</div>
</div>
<transition name="slide">

View File

@@ -15,7 +15,7 @@
</td>
<td class="py-0">
<div class="h-8 w-8 flex items-center justify-center text-white text-opacity-50 hover:text-error cursor-pointer" @click.stop="removeProvider(provider)">
<button type="button" :aria-label="$strings.ButtonDelete" class="material-icons text-base">delete</button>
<button type="button" :aria-label="$strings.ButtonDelete" class="material-symbols text-base">delete</button>
</div>
</td>
</tr>

View File

@@ -8,7 +8,7 @@
<div class="flex-grow" />
<ui-btn v-if="userIsAdmin" small :color="showFullPath ? 'gray-600' : 'primary'" class="mr-2 hidden md:block" @click.stop="toggleFullPath">{{ $strings.ButtonFullPath }}</ui-btn>
<div class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="showFiles ? 'transform rotate-180' : ''">
<span class="material-icons text-4xl">expand_more</span>
<span class="material-symbols text-4xl">expand_more</span>
</div>
</div>
<transition name="slide">
@@ -18,7 +18,7 @@
<th class="text-left px-4">{{ $strings.LabelPath }}</th>
<th class="text-left w-24 min-w-24">{{ $strings.LabelSize }}</th>
<th class="text-left px-4 w-24">
{{ $strings.LabelRead }} <ui-tooltip :text="$strings.LabelReadEbookWithoutProgress" direction="top" class="inline-block"><span class="material-icons-outlined text-sm align-middle">info</span></ui-tooltip>
{{ $strings.LabelRead }} <ui-tooltip :text="$strings.LabelReadEbookWithoutProgress" direction="top" class="inline-block"><span class="material-symbols-outlined text-sm align-middle">info</span></ui-tooltip>
</th>
<th v-if="showMoreColumn" class="text-center w-16"></th>
</tr>

View File

@@ -1,7 +1,7 @@
<template>
<tr>
<td class="px-4">
{{ showFullPath ? file.metadata.path : file.metadata.relPath }} <ui-tooltip :text="$strings.LabelPrimaryEbook" class="inline-block"><span v-if="isPrimary" class="material-icons-outlined text-success align-text-bottom">check_circle</span></ui-tooltip>
{{ showFullPath ? file.metadata.path : file.metadata.relPath }} <ui-tooltip :text="$strings.LabelPrimaryEbook" class="inline-block"><span v-if="isPrimary" class="material-symbols-outlined text-success align-text-bottom">check_circle</span></ui-tooltip>
</td>
<td>
{{ $bytesPretty(file.metadata.size) }}

View File

@@ -8,7 +8,7 @@
<div class="flex-grow" />
<ui-btn v-if="userIsAdmin" small :color="showFullPath ? 'gray-600' : 'primary'" class="mr-2 hidden md:block" @click.stop="toggleFullPath">{{ $strings.ButtonFullPath }}</ui-btn>
<div class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="showFiles ? 'transform rotate-180' : ''">
<span class="material-icons text-4xl">expand_more</span>
<span class="material-symbols text-4xl">expand_more</span>
</div>
</div>
<transition name="slide">

View File

@@ -11,7 +11,7 @@
<ui-btn small color="primary">{{ $strings.ButtonManageTracks }}</ui-btn>
</nuxt-link>
<div class="cursor-pointer h-10 w-10 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="showTracks ? 'transform rotate-180' : ''">
<span class="material-icons text-4xl">expand_more</span>
<span class="material-symbols text-4xl">expand_more</span>
</div>
</div>
<transition name="slide">

View File

@@ -5,7 +5,7 @@
<span class="bg-black-400 rounded-xl py-0.5 px-2 text-sm font-mono">{{ files.length }}</span>
<div class="flex-grow" />
<div class="cursor-pointer h-9 w-9 rounded-full hover:bg-black-400 flex justify-center items-center duration-500" :class="expand ? 'transform rotate-180' : ''">
<span class="material-icons text-3xl">expand_more</span>
<span class="material-symbols text-3xl">expand_more</span>
</div>
</div>
<transition name="slide">

View File

@@ -42,10 +42,10 @@
<div class="w-full flex justify-left">
<!-- Dont show edit for non-root users -->
<div v-if="user.type !== 'root' || userIsRoot" class="h-8 w-8 flex items-center justify-center text-white text-opacity-50 hover:text-opacity-100 cursor-pointer" @click.stop="editUser(user)">
<button type="button" :aria-label="$getString('ButtonUserEdit', [user.username])" class="material-icons text-base">edit</button>
<button type="button" :aria-label="$getString('ButtonUserEdit', [user.username])" class="material-symbols text-base">edit</button>
</div>
<div v-show="user.type !== 'root'" class="h-8 w-8 flex items-center justify-center text-white text-opacity-50 hover:text-error cursor-pointer" @click.stop="deleteUserClick(user)">
<button type="button" :aria-label="$getString('ButtonUserDelete', [user.username])" class="material-icons text-base">delete</button>
<button type="button" :aria-label="$getString('ButtonUserDelete', [user.username])" class="material-symbols text-base">delete</button>
</div>
</div>
</td>

View File

@@ -3,7 +3,7 @@
<div v-if="book" class="flex h-18 md:h-[5.5rem]">
<div class="w-10 min-w-10 md:w-16 md:max-w-16 h-full">
<div class="flex h-full items-center justify-center">
<span class="material-icons drag-handle text-lg md:text-xl">menu</span>
<span class="material-symbols drag-handle text-lg md:text-xl">menu</span>
</div>
</div>
<div class="h-full flex items-center" :style="{ width: coverWidth + 'px', minWidth: coverWidth + 'px', maxWidth: coverWidth + 'px' }">
@@ -11,7 +11,7 @@
<covers-book-cover :library-item="book" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="absolute top-0 left-0 flex items-center justify-center bg-black bg-opacity-50 h-full w-full z-10" v-show="isHovering && showPlayBtn">
<div class="w-8 h-8 bg-white bg-opacity-20 rounded-full flex items-center justify-center hover:bg-opacity-40 cursor-pointer" @click="playClick">
<span class="material-icons text-2xl">play_arrow</span>
<span class="material-symbols fill text-2xl">play_arrow</span>
</div>
</div>
</div>

View File

@@ -16,14 +16,14 @@
<ui-context-menu-dropdown v-if="!isScanning && !isDeleting" :items="contextMenuItems" :icon-class="`text-1.5xl text-gray-${isHovering ? 50 : 400}`" class="!hidden md:!block" @action="contextMenuAction" />
<!-- Mobile context menu icon -->
<span v-if="!isScanning && !isDeleting" class="!block md:!hidden material-icons text-xl text-gray-300 ml-3 cursor-pointer" @click.stop="showMenu">more_vert</span>
<span v-if="!isScanning && !isDeleting" class="!block md:!hidden material-symbols text-xl text-gray-300 ml-3 cursor-pointer" @click.stop="showMenu">more_vert</span>
<div v-show="isDeleting" class="text-xl text-gray-300 ml-3 animate-spin">
<svg viewBox="0 0 24 24" class="w-6 h-6">
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
</svg>
</div>
<span class="material-icons drag-handle text-xl text-gray-400 hover:text-gray-50 ml-2 md:ml-4">reorder</span>
<span class="material-symbols drag-handle text-xl text-gray-400 hover:text-gray-50 ml-2 md:ml-4">reorder</span>
<!-- For mobile -->
<modals-dialog v-model="showMobileMenu" :title="menuTitle" :items="contextMenuItems" @action="contextMenuAction" />

View File

@@ -3,14 +3,14 @@
<div v-if="item" class="flex h-16 md:h-20">
<div class="w-10 min-w-10 md:w-16 md:max-w-16 h-full">
<div class="flex h-full items-center justify-center">
<span class="material-icons drag-handle text-lg md:text-xl">menu</span>
<span class="material-symbols drag-handle text-lg md:text-xl">menu</span>
</div>
</div>
<div class="h-full relative flex items-center" :style="{ width: coverWidth + 'px', minWidth: coverWidth + 'px', maxWidth: coverWidth + 'px' }">
<covers-book-cover :library-item="libraryItem" :width="coverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="absolute top-0 left-0 bg-black bg-opacity-50 flex items-center justify-center h-full w-full z-10" v-show="isHovering && showPlayBtn">
<div class="w-8 h-8 bg-white bg-opacity-20 rounded-full flex items-center justify-center hover:bg-opacity-40 cursor-pointer" @click="playClick">
<span class="material-icons text-2xl">play_arrow</span>
<span class="material-symbols fill text-2xl">play_arrow</span>
</div>
</div>
</div>

View File

@@ -21,7 +21,7 @@
<div class="flex items-center pt-2">
<button class="h-8 px-4 border border-white border-opacity-20 hover:bg-white hover:bg-opacity-10 rounded-full flex items-center justify-center cursor-pointer focus:outline-none" :class="userIsFinished ? 'text-white text-opacity-40' : ''" @click.stop="playClick">
<span class="material-icons text-2xl" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
<span class="material-symbols fill text-2xl" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
<p class="pl-2 pr-1 text-sm font-semibold">{{ timeRemaining }}</p>
</button>

View File

@@ -10,7 +10,8 @@
<button v-else class="abs-btn outline-none rounded-md shadow-md relative border border-gray-600" :disabled="disabled || loading" :type="type" :class="classList" @mousedown.prevent @click="click">
<slot />
<div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100">
<svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
<span v-if="progress">{{ progress }}</span>
<svg v-else class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
</svg>
</div>
@@ -33,7 +34,8 @@ export default {
paddingY: Number,
small: Boolean,
loading: Boolean,
disabled: Boolean
disabled: Boolean,
progress: String
},
data() {
return {}

View File

@@ -2,7 +2,7 @@
<label class="flex justify-start items-center" :class="!disabled ? 'cursor-pointer' : ''">
<div class="border-2 rounded flex flex-shrink-0 justify-center items-center" :class="wrapperClass">
<input v-model="selected" :disabled="disabled" type="checkbox" class="opacity-0 absolute" :class="!disabled ? 'cursor-pointer' : ''" />
<span v-if="partial" class="material-icons text-base leading-none text-gray-400">remove</span>
<span v-if="partial" class="material-symbols text-base leading-none text-gray-400">remove</span>
<svg v-else-if="selected" class="fill-current pointer-events-none" :class="svgClass" viewBox="0 0 20 20"><path d="M0 11l2-2 5 5L18 3l2 2L7 18z" /></svg>
</div>
<div v-if="label" class="select-none" :class="[labelClassname, disabled ? 'text-gray-400' : 'text-gray-100']">{{ label }}</div>

View File

@@ -2,7 +2,7 @@
<div class="relative h-9 w-9" v-click-outside="clickOutsideObj">
<slot :disabled="disabled" :showMenu="showMenu" :clickShowMenu="clickShowMenu" :processing="processing">
<button v-if="!processing" type="button" :disabled="disabled" class="relative h-full w-full flex items-center justify-center shadow-sm pl-3 pr-3 text-left focus:outline-none cursor-pointer text-gray-100 hover:text-gray-200 rounded-full hover:bg-white/5" aria-haspopup="listbox" :aria-expanded="showMenu" @click.stop.prevent="clickShowMenu">
<span class="material-icons" :class="iconClass">more_vert</span>
<span class="material-symbols text-2xl" :class="iconClass">more_vert</span>
</button>
<div v-else class="h-full w-full flex items-center justify-center">
<widgets-loading-spinner />
@@ -116,4 +116,4 @@ export default {
},
mounted() {}
}
</script>
</script>

View File

@@ -8,7 +8,7 @@
<span v-if="selectedSubtext" class="font-normal block truncate font-sans text-sm text-gray-400">{{ selectedSubtext }}</span>
</span>
<span class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<span class="material-icons text-2xl">expand_more</span>
<span class="material-symbols text-2xl">expand_more</span>
</span>
</button>

View File

@@ -5,7 +5,7 @@
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
</svg>
</div>
<span v-else :class="outlined ? 'material-icons-outlined' : 'material-icons'" :style="{ fontSize }">{{ icon }}</span>
<span v-else :class="outlined ? 'material-symbols-outlined' : 'material-symbols'" :style="{ fontSize }">{{ icon }}</span>
</button>
</template>

View File

@@ -15,7 +15,7 @@
<span class="font-normal ml-3 block truncate">{{ item }}</span>
</div>
<span v-if="input === item" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
<span class="material-icons text-xl">checkmark</span>
<span class="material-symbols text-xl">check</span>
</span>
</li>
</template>

View File

@@ -92,12 +92,13 @@ export default {
if (this.$route.name.startsWith('config')) {
// No need to refresh
} else if (this.$route.name.startsWith('library') && this.$route.name !== 'library-library-series-id') {
const newRoute = this.$route.path.replace(currLibraryId, library.id)
this.$router.push(newRoute)
} else if (this.$route.name === 'library-library-series-id' && library.mediaType === 'book') {
// For series item page redirect to root series page
this.$router.push(`/library/${library.id}/bookshelf/series`)
} else if (this.$route.name === 'library-library-search') {
this.$router.push(this.$route.fullPath.replace(currLibraryId, library.id))
} else if (this.$route.name.startsWith('library')) {
this.$router.push(this.$route.path.replace(currLibraryId, library.id))
} else {
this.$router.push(`/library/${library.id}`)
}

View File

@@ -6,8 +6,8 @@
<div ref="inputWrapper" style="min-height: 36px" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-1" :class="wrapperClass" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent>
<div v-for="item in selected" :key="item" class="rounded-full px-2 py-1 mx-0.5 my-0.5 text-xs bg-bg flex flex-nowrap break-all items-center relative">
<div v-if="!disabled" class="w-full h-full rounded-full absolute top-0 left-0 px-1 bg-bg bg-opacity-75 flex items-center justify-end opacity-0 hover:opacity-100">
<span v-if="showEdit" class="material-icons text-white hover:text-warning cursor-pointer" style="font-size: 1.1rem" @click.stop="editItem(item)">edit</span>
<span class="material-icons text-white hover:text-error cursor-pointer" style="font-size: 1.1rem" @click.stop="removeItem(item)">close</span>
<span v-if="showEdit" class="material-symbols text-white hover:text-warning cursor-pointer" style="font-size: 1.1rem" @click.stop="editItem(item)">edit</span>
<span class="material-symbols text-white hover:text-error cursor-pointer" style="font-size: 1.1rem" @click.stop="removeItem(item)">close</span>
</div>
{{ item }}
</div>
@@ -22,7 +22,7 @@
<span class="font-normal ml-3 block truncate">{{ item }}</span>
</div>
<span v-if="selected.includes(item)" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
<span class="material-icons text-xl">checkmark</span>
<span class="material-symbols text-xl">check</span>
</span>
</li>
</template>

View File

@@ -5,7 +5,7 @@
<div ref="inputWrapper" style="min-height: 40px" class="flex-wrap relative w-full shadow-sm flex items-center bg-primary border border-gray-600 rounded-md px-2 py-1 cursor-pointer" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent>
<div v-for="item in selectedItems" :key="item.value" class="rounded-full px-2 py-1 ma-0.5 text-xs bg-bg flex flex-nowrap whitespace-nowrap items-center relative">
<div class="w-full h-full rounded-full absolute top-0 left-0 opacity-0 hover:opacity-100 px-1 bg-bg bg-opacity-75 flex items-center justify-end cursor-pointer">
<span class="material-icons text-white hover:text-error" style="font-size: 1.1rem" @click.stop="removeItem(item.value)">close</span>
<span class="material-symbols text-white hover:text-error" style="font-size: 1.1rem" @click.stop="removeItem(item.value)">close</span>
</div>
{{ item.text }}
</div>
@@ -18,7 +18,7 @@
<p class="font-normal ml-3 block truncate">{{ item.text }}</p>
<div v-if="selected.includes(item.value)" class="text-yellow-400 absolute inset-y-0 right-0 my-auto w-5 h-5 mr-3 overflow-hidden">
<span class="material-icons text-xl">checkmark</span>
<span class="material-symbols text-xl">check</span>
</div>
</li>
</template>
@@ -139,4 +139,4 @@ export default {
if (this.menu) this.menu.remove()
}
}
</script>
</script>

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