Compare commits

..

277 Commits

Author SHA1 Message Date
Joshua M. Boniface
8e37078b60 Bump version to 10.8.13
Some checks failed
Lint / Run eslint (push) Has been cancelled
Lint / Run stylelint (css) (push) Has been cancelled
Lint / Run stylelint (scss) (push) Has been cancelled
2023-11-28 22:22:14 -05:00
Joshua M. Boniface
a5184bb843 Merge pull request #5019 from joshuaboniface/set-ffmpeg-disabled
Set FFmpeg path disabled
2023-11-28 22:19:29 -05:00
Joshua M. Boniface
b9ee65e49d Set FFmpeg path disabled 2023-11-27 12:04:19 -05:00
Bill Thornton
d5ebc64fcb Merge pull request #4992 from rafma0/release-10.8.z 2023-11-14 18:36:22 -05:00
Bill Thornton
f4c49427fd Merge pull request #4993 from rafma0/backport-fix-tizen-flac 2023-11-14 18:35:52 -05:00
rafma0
6c97b7a6d8 remove video audio flac on tizen 2023-11-12 23:43:10 -03:00
rafma0
0859d4d881 fix jittering in checkboxes on tv ui 2023-11-12 23:17:29 -03:00
Joshua M. Boniface
4b6bbcfe26 Bump version to 10.8.12 2023-11-04 14:42:47 -04:00
Bill Thornton
1a1735340f Merge pull request #4912 from dmitrylyzo/fix-imports
Fix imports
2023-10-24 13:13:10 -04:00
Dmitry Lyzo
5dad4b4486 Fix imports in Playback/Queue controller
According to the template.
2023-10-24 14:10:33 +03:00
Dmitry Lyzo
201aec56c6 Fix imports in RemoteControl 2023-10-24 14:10:33 +03:00
Bill Thornton
d274d7f741 Merge pull request #4892 from thornbill/backport-4860 2023-10-19 23:04:38 -04:00
cbe
b270d4051b Fix language/subtitle switcher when using gamepad
Raising the event code `13` (Enter) should be a lot more stable since `0`
is just the default and not assigned to any actual key [1]. This keycode
also has been standardized early enough to change it at this crucial part
of gamepad handling in my opinion.

[1] https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

Fixes #3755
2023-10-19 19:18:29 -04:00
Bill Thornton
aa09fa710e Merge pull request #4893 from thornbill/backport-4754 2023-10-19 19:01:52 -04:00
Marcus Nilsson
c33c2b1e26 Change Subtitle Sync slider to go from -300 to 300
This patch changes the subtitle sync from using a procentage to a
'slider value' that ranges from -300 to 300. The reasons for this is that
WebOS doesn't jump in 0.1 increments but instead jumps 1.0 increments in
the slider, which results in subtitle sync jumping 0.6s per increment.
Using a value from -300 to 300 makes LG WebOS jump 0.1s instead.
2023-10-19 12:53:23 -04:00
Bill Thornton
eb7fb6b39d Merge pull request #4837 from dmitrylyzo/fix-noitem-play 2023-10-04 08:04:12 -04:00
Dmitry Lyzo
5fd5292f6d Fix playing empty item set 2023-10-04 13:49:23 +03:00
Bill Thornton
1250c76567 Merge pull request #4813 from dmitrylyzo/backport/slider-force-change 2023-09-29 07:55:45 -04:00
Bill Thornton
1ba6bde32a Merge pull request #4797 from Mauroq/release-10.8.z 2023-09-29 07:54:50 -04:00
Dmitry Lyzo
3c80bf7b19 Handle pressing Enter to finish keyboard dragging of slider
(cherry picked from commit 9024ebea39)
2023-09-29 13:19:48 +03:00
Joshua M. Boniface
1e1af1c67f Merge pull request #4787 from dmitrylyzo/fix-legacy 2023-09-23 21:49:36 -04:00
Joshua M. Boniface
8d1c34f80e Bump version to 10.8.11 2023-09-23 21:41:43 -04:00
Dmitry Lyzo
d3e3bc7282 Fix homesections in legacy browser 2023-09-23 23:26:48 +03:00
Dennis de Lange
0ad87f3b87 fix: Remove h264-10bit support from Tizen 2023-09-22 17:41:26 +02:00
Bill Thornton
9e871d43ec Merge pull request #4632 from dmitrylyzo/multi-purpose-keys 2023-09-21 12:23:04 -04:00
Dmitry Lyzo
11ae2ff43f Add KeyboardEvent constructor polyfill 2023-09-14 01:28:19 +03:00
Dmitry Lyzo
76c55116ce Change behavior of arrow keys and Enter when OSD is hidden 2023-09-14 01:28:19 +03:00
Dmitry Lyzo
884ce171ea Focus on corresponding button 2023-09-14 01:26:55 +03:00
Dmitry Lyzo
51bd2bef1a Don't show OSD for Fullscreen and Mute 2023-09-14 01:26:55 +03:00
Bill Thornton
0bfe91b0fa Merge pull request #4757 from dmitrylyzo/fix-samsung_tv-dolbyvision
Remove Dolby Vision support on Samsung TV (Tizen)
2023-09-08 16:56:26 -04:00
Bill Thornton
a5feddb48b Merge pull request #4758 from dmitrylyzo/fix-slider-step 2023-09-08 07:52:20 -04:00
Dmitry Lyzo
7d27596d6b Fix slider step
Use the value of the `step` attribute if no keyboard steps are specified.
2023-08-24 00:23:11 +03:00
Dmitry Lyzo
e0cb79088b fix: Remove Dolby Vision support on Samsung TV 2023-08-23 00:18:25 +03:00
Bill Thornton
b2f3720282 Merge pull request #4709 from thornbill/backport-4692
Fix scheduled task time limit key
2023-07-07 20:16:52 -04:00
hikaps
b67f4eccfb Fix scheduled task time limit key 2023-07-07 19:42:53 -04:00
Bill Thornton
331fa87216 Merge pull request #4654 from joshuaboniface/additionalPluginVerification
Add confirmation for 3rd party repos
2023-07-01 01:32:05 -04:00
Joshua M. Boniface
59813ee0ea Merge branch 'release-10.8.z' into additionalPluginVerification 2023-06-28 23:01:40 -04:00
Joshua M. Boniface
eaae0f3c55 Add translation of "unknown" repo details 2023-06-28 22:49:58 -04:00
Bill Thornton
6304e27940 Merge pull request #4688 from dmitrylyzo/fix-macos-alac 2023-06-14 13:06:50 -04:00
Dmitry Lyzo
7ada8796a7 Disable ALAC on MacOS in non-Safari browsers 2023-06-14 11:30:22 +03:00
Bill Thornton
eab36f9934 Merge pull request #4685 from hurani/backport-directory-viewer-parent-fix 2023-06-12 16:39:52 -04:00
Bill Thornton
b044bc25de Merge pull request #4171 from nielsvanvelzen/directory-browser-go-up
Fix going to parent folder in directory browser
2023-06-11 22:34:23 -07:00
Bill Thornton
5cc91f2ee0 Merge pull request #4657 from thornbill/subs-xss
Fix xss in custom subtitles element
2023-06-01 02:13:28 -04:00
Bill Thornton
2ffb833daf Fix xss in custom subtitles element 2023-06-01 01:33:59 -04:00
Joshua M. Boniface
93d63330fd Fix conditional formatting and add fallback 2023-05-30 10:39:27 -04:00
Joshua M. Boniface
f1b0b504dd Check for pkg.versions.length
Co-authored-by: Niels van Velzen <nielsvanvelzen@users.noreply.github.com>
2023-05-30 10:35:46 -04:00
Joshua M. Boniface
919be18c84 Fix syntax error in HTML 2023-05-30 10:13:21 -04:00
Joshua M. Boniface
cf530b30d5 Fix linting errors
* Remove superfluous variable
* Remove extra random spaces from editor
* Use single-quotes around text
2023-05-30 09:35:23 -04:00
Joshua M. Boniface
dd004ec06b Add labels to i8n 2023-05-30 09:25:32 -04:00
Joshua M. Boniface
a11d74ae68 Use camelCase variable name 2023-05-30 09:21:41 -04:00
Joshua M. Boniface
da9eece6c0 Remove extra console.log 2023-05-29 11:19:13 -04:00
Joshua M. Boniface
509cbabedb Add confirmation for 3rd party repos
Adds a confirmation similar to the one performed during plugin
installation, when adding a 3rd party repository.

The safe domain is hardcoded to be "repo.jellyfin.org" as this is very
stable and we have no plans to change it. Individual mirrors don't need
to be specified since this is user-input content and they should be
using the main URL not the URL of a specific mirror.

The confirmation message makes explicit mention of the possibility of
malicious code from 3rd party repositories as well as updates that may
bring it in, and suggests only adding 3rd parties from trusted people.

The plugin install confirmation is also modified to use the same
conditional and an altered message similar to the above, again to
emphasize the potential security risks of 3rd party plugins.

Finally, some additional information is added to the Developer Info
section of the plugin page; specifically, the name of the repository the
plugin is sourced from as well as its URL. How this is obtained is a
hack, since these should probably be part of the main information about
the plugin and not each specific version, but this is worked around by
only showing the information from the first (i.e. newest) version.
2023-05-29 10:59:21 -04:00
Bill Thornton
62246fe0a9 Merge pull request #4628 from dmitrylyzo/fix-volume-slider 2023-05-21 12:32:19 -04:00
Bill Thornton
35a7dfbed6 Merge pull request #4627 from dmitrylyzo/fix-slider-bubble 2023-05-21 01:43:51 -04:00
Dmitry Lyzo
b93221a9b2 Fix initial state of slider 2023-05-20 23:36:20 +03:00
Dmitry Lyzo
1a858d9dda Fix overlap of slider bubble 2023-05-20 23:04:14 +03:00
Bill Thornton
07ce5c44a1 Merge pull request #4591 from thornbill/fix-es-quick-connect 2023-05-12 10:07:58 -04:00
Bill Thornton
2eda12ba8f Fix QuickConnect code not displaying in Spanish 2023-05-12 08:31:04 -04:00
Bill Thornton
9266e51aaf Merge pull request #4589 from thornbill/fix-api-key-xss 2023-05-11 17:10:22 -04:00
Bill Thornton
069ea049eb Fix xss in api key page 2023-05-11 16:48:06 -04:00
Bill Thornton
56af039fb9 Merge pull request #4561 from dmitrylyzo/fix-bottom-hide
Fix bottom video controls don't auto-hide
2023-05-02 16:54:10 -04:00
Dmitry Lyzo
1fb5c4d95d Fix query selector target 2023-05-02 22:36:51 +03:00
Bill Thornton
828fa340d5 Merge pull request #4553 from dmitrylyzo/fix-tv-episode-autoplay 2023-05-02 00:53:01 -04:00
Dmitry Lyzo
a77a8c7aec fix: Unhide PlayNextEpisodeAutomatically on TV 2023-05-01 02:17:50 +03:00
Joshua M. Boniface
55714d5341 Bump version to 10.8.10 2023-04-23 11:01:37 -04:00
Joshua M. Boniface
b88a5951e1 Merge pull request from GHSA-89hp-h43h-r5pq
Escape device id in raw HTML
2023-04-23 11:00:06 -04:00
Ian Walton
bd480aa1db Escape device id in raw HTML. 2023-04-22 10:12:58 -04:00
Bill Thornton
cf0cf93e47 Merge pull request #4492 from nyanmisaka/tonemap-mode-options
Add the tonemap mode options
2023-04-16 23:18:43 -04:00
nyanmisaka
f942072e53 Update translations for tonemapping
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2023-04-14 18:55:08 +08:00
nyanmisaka
f28db6699d Add tonemap mode options
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2023-04-14 18:50:24 +08:00
Bill Thornton
a447786a24 Merge pull request #4487 from thornbill/backport-4485
Fix dead documentation link
2023-04-12 11:01:56 -04:00
Bill Thornton
c9e5b35e42 Merge pull request #4385 from nyanmisaka/drop-progressive
Drop progressive transcoding in web client
2023-04-12 10:15:52 -04:00
Brett Petch
a2ffa9dfaf Update src/controllers/dashboard/encodingsettings.html
Co-authored-by: Bill Thornton <thornbill@users.noreply.github.com>
2023-04-12 10:08:25 -04:00
Brett Petch
3c8c6ad469 fix: dead link 2023-04-12 10:08:11 -04:00
nyanmisaka
62dbf0d106 Drop progressive transcoding in web client
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2023-03-14 20:38:08 +08:00
Bill Thornton
22d1f40587 Merge pull request #4395 from thornbill/fix-installed-plugin-version
Fix installed plugin version html
2023-03-07 16:27:42 -05:00
Bill Thornton
909f03460c Fix installed plugin version html 2023-03-06 16:27:35 -05:00
Bill Thornton
7e99e3ec51 Merge pull request #4330 from dmitrylyzo/fix-navigation-input
Fix navigation for some types of INPUT
2023-02-27 10:33:01 -05:00
Bill Thornton
af27e084d5 Merge pull request #4362 from dmitrylyzo/fix-reset-subtitleoffset
Fix subtitle offset reset when seeking progressive stream
2023-02-23 16:46:26 -05:00
Bill Thornton
8f75a42669 Merge pull request #4356 from dmitrylyzo/babel-libass 2023-02-18 22:10:44 -05:00
Dmitry Lyzo
1040685c1e Fix subtitle offset reset when seeking progressive stream 2023-02-19 01:41:23 +03:00
Dmitry Lyzo
38aebf4e42 Babelify @jellyfin/libass-wasm 2023-02-14 23:13:03 +03:00
Dmitry Lyzo
62d0354fe0 Simplify adding modules to babel-loader 2023-02-14 23:13:03 +03:00
Dmitry Lyzo
e580f0c869 Fix navigation for some types of INPUT
Regression from 36fce00270
2023-02-06 15:27:42 +03:00
Bill Thornton
551f12fdfb Merge pull request #4312 from dmitrylyzo/backport-4150
Backport PR #4150 to 10.8.z branch
2023-01-31 12:20:44 -05:00
Bill Thornton
00d59c546d Merge pull request #4310 from jsayol/patch-1
Backport PR #4147 to 10.8.z branch
2023-01-31 12:19:23 -05:00
Dmitry Lyzo
0864432105 Fix file name escaping
(cherry picked from commit 45542a67a4)
2023-01-28 23:30:07 +03:00
Dmitry Lyzo
0c3c47b8b4 Fix Subtitle Uploder navigation in TV mode
(cherry picked from commit 78bbac8ca7)
2023-01-28 23:30:07 +03:00
Josep Sayol
5f7d2659dc Backport PR #4147 to 10.8.z branch 2023-01-28 08:22:38 +01:00
Joshua M. Boniface
72c66e91ed Bump version to 10.8.9 2023-01-22 14:09:15 -05:00
Bill Thornton
acb6519ef9 Merge pull request #4288 from dmitrylyzo/fix-es-419
Fix loading Spanish (Latin America) (es-419)
2023-01-19 15:40:35 -05:00
Joshua M. Boniface
0ff86d9ea0 Merge pull request #4274 from dmitrylyzo/fix-hisense-back
Fix back action on Hisense TV
2023-01-19 15:32:51 -05:00
Dmitry Lyzo
b94d14399f fix loading Spanish (Latin America) (es-419) 2023-01-15 20:15:39 +03:00
Dmitry Lyzo
f4c8dd6b1f fix back action on Hisense TV 2023-01-13 14:05:25 +03:00
Bill Thornton
9139153d16 Merge pull request #4263 from dmitrylyzo/fix-change-track 2023-01-12 17:39:02 -05:00
Dmitry Lyzo
0ff3cf321c fix change audio track during playback 2023-01-11 20:17:24 +03:00
Dmitry Lyzo
fe65e0c3b3 refactor: decrease the number of iterations 2023-01-10 23:13:37 +03:00
Dmitry Lyzo
a7bd7e30c6 fix filtering of supported audio tracks 2023-01-10 23:13:37 +03:00
Bill Thornton
ea79d2651a Merge pull request #4267 from dmitrylyzo/fix-audiocontext-limit
Fix AudioContext limit exceeded
2023-01-10 10:00:55 -05:00
Joshua M. Boniface
21a3bae204 Merge pull request #4269 from thornbill/fix-plugin-xss
Fix XSS vulnerability in plugin repo pages
2023-01-09 12:15:18 -05:00
Bill Thornton
4bc0eebee0 Fix XSS vulnerability in plugin repo pages 2023-01-09 11:11:33 -05:00
Dmitry Lyzo
21cf0f5f8e fix AudioContext limit exceeded 2023-01-07 22:36:54 +03:00
Bill Thornton
96234eafb7 Merge pull request #4240 from dmitrylyzo/fix-audiotracks
Fix detection of SecondaryAudio support
2023-01-05 11:19:08 -05:00
Bill Thornton
ae907cd8a6 Merge pull request #4243 from dmitrylyzo/fix-escape-html 2023-01-04 19:41:39 -05:00
Dmitry Lyzo
0ce839c4a8 Fix HTML escaping in MediaSession and on remote page 2023-01-05 01:52:47 +03:00
Dmitry Lyzo
a3e64088b1 Fix detection of SecondaryAudio support 2023-01-05 01:05:27 +03:00
Bill Thornton
255df81375 Merge pull request #4238 from thornbill/fix-router-xss
Fix XSS vulnerability in router
2023-01-04 16:34:45 -05:00
Bill Thornton
5bfffd6209 Merge pull request #4182 from nyanmisaka/fix-progressive-mp4
Fix the progressive mp4 transcoding profile
2023-01-04 16:33:44 -05:00
Bill Thornton
8877e2f758 Fix XSS vulnerability in router 2023-01-04 13:55:25 -05:00
Joshua M. Boniface
b909369127 Bump version to 10.8.8 2022-11-29 13:43:09 -05:00
nyanmisaka
869cbef571 Fix the progressive mp4 transcoding profile
mp4 should be used instead of webm for the maximum compatibility.

Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2022-11-20 20:10:53 +08:00
Bill Thornton
ff4e6a6778 Merge pull request #4172 from thornbill/fix-items-banner-alignment
Fix item details banner image alignment
2022-11-16 22:56:16 -05:00
Bill Thornton
fda6d3c969 Fix item details banner image alignment on mobile 2022-11-15 13:22:52 -05:00
Bill Thornton
8a549eb45b Fix item details banner image alignment 2022-11-15 11:38:34 -05:00
Bill Thornton
4f3ac34739 Merge pull request #4166 from dmitrylyzo/fix-input-navigation 2022-11-10 18:09:08 -05:00
Dmitry Lyzo
36fce00270 Fix keyboard navigation for INPUT and TEXTAREA 2022-11-10 16:31:31 +03:00
Joshua M. Boniface
de8ee44b22 Bump version to 10.8.7 2022-10-31 23:06:37 -04:00
Joshua M. Boniface
1b33cb6f9b Bump version to 10.8.6 2022-10-28 22:44:19 -04:00
Bill Thornton
eb47e5c374 Merge pull request #4117 from thornbill/backport-4094
Backport PR #4094 to 10.8 release
2022-10-28 16:04:23 -04:00
CrispyBaguette
aca8b0ed18 Append new contributor 2022-10-28 15:52:20 -04:00
CrispyBaguette
e516014dc0 Accept arbitrary precision for community rating 2022-10-28 15:52:10 -04:00
Bill Thornton
4c99480c42 Merge pull request #4069 from peterspenler/fix/lazyload-image-intersection 2022-10-26 17:34:47 -04:00
peterspenler
c200a7d2c6 Use isIntersecting instead of intersectionRatio 2022-10-26 23:56:45 +03:00
Bill Thornton
aa009091d5 Merge pull request #4089 from dmitrylyzo/fix-escape-html 2022-10-22 15:38:31 -04:00
Dmitry Lyzo
f81f0ef9d7 fix double escape HTML 2022-10-22 22:31:18 +03:00
Bill Thornton
66baa9e069 Merge pull request #4047 from thornbill/backport-4028 2022-10-14 11:17:51 -04:00
Urtzi Odriozola
375bf86a22 Update displaySettings.template.html
"Euskara" is the native way of naming Basque
2022-10-13 10:31:38 -04:00
Bill Thornton
fb39a56700 Merge pull request #4039 from thornbill/fix-details-card-crop
Fix card cropping on item details page
2022-10-13 10:17:53 -04:00
Bill Thornton
0b9415a041 Fix card cropping on item details page 2022-10-13 00:47:06 -04:00
Bill Thornton
b14d76a3d4 Merge pull request #4005 from dmitrylyzo/fix-last-seen
Fix locale with suffix
2022-10-06 17:04:16 -04:00
Bill Thornton
1263468d49 Merge pull request #4004 from cvium/add_basque_10.8
chore: add Basque display language option
2022-10-06 11:26:41 -04:00
Dmitry Lyzo
fe4ee0c101 fix locale with suffix
"Locale with suffix" is constructed only once with the initial
(browser) locale.
2022-10-06 11:48:15 +03:00
Claus Vium
30df221bbb Update dfnshelper.js 2022-10-06 10:07:23 +02:00
cvium
dfa9b33949 chore: add Basque display language option 2022-10-06 09:42:04 +02:00
Joshua M. Boniface
354157c003 Bump version to 10.8.5 2022-09-24 22:02:30 -04:00
Bill Thornton
72d538e902 Merge pull request #3877 from Callum17/bugfix/release-fix_itemcontextmenu_fails_to_update_for_items_with_no_image_metadata 2022-09-23 23:39:54 -04:00
callum
f1574e0f42 Merge branch 'bugfix/release-fix_itemcontextmenu_fails_to_update_for_items_with_no_image_metadata' of github.com:Callum17/jellyfin-web into bugfix/release-fix_itemcontextmenu_fails_to_update_for_items_with_no_image_metadata 2022-09-10 20:21:46 +01:00
callum
ebfd28d396 Avoid unnecessary DOM update if concurrent played items lack image metadata. 2022-09-10 20:21:37 +01:00
Callum
1861605958 Merge branch 'release-10.8.z' into bugfix/release-fix_itemcontextmenu_fails_to_update_for_items_with_no_image_metadata 2022-09-06 23:35:29 +01:00
Joshua M. Boniface
f85f7d2fe6 Merge pull request #3878 from thornbill/fix-sdk-imports 2022-09-06 01:20:16 -04:00
Bill Thornton
03ce4210af Fix sdk imports to improve build size 2022-09-06 00:43:08 -04:00
callum
689a65cc92 Style. Prefer to use nowPlayingImageUrl to load image in order to make intent clear. 2022-09-04 08:50:20 +01:00
callum
44a5d7bb8d Ensure nowPlayingBar updates correctly when navigating from a song with no image back to a previous song with an image. 2022-09-04 08:50:20 +01:00
callum
072e20b585 Fixed: itemContextMenu opened from bottom media control bar applying operations to the wrong item when items have no image metadata. Can manifest as the wrong song being added to a playlist, deleted, or receiving user supplied metadata, etc. 2022-09-04 08:50:20 +01:00
Bill Thornton
89ec4f4e8a Merge pull request #3849 from dmitrylyzo/fix-cursor-hide 2022-08-24 12:58:33 -04:00
Bill Thornton
89d92e738f Merge pull request #3848 from dmitrylyzo/fix-screensaver 2022-08-24 12:56:45 -04:00
Dmitry Lyzo
c9f4e3c301 Fix unexpected cursor hiding
Hide cursor in case of:
- TV layout
- Active playback
- Active screensaver
2022-08-24 15:56:27 +03:00
Dmitry Lyzo
a80fa25a68 Fix idle function call 2022-08-24 15:55:58 +03:00
Joshua Boniface
bb040b90d1 Bump version to 10.8.4 2022-08-13 21:52:06 -04:00
Bill Thornton
747f7beae7 Merge pull request #3789 from thornbill/fix-cards-xss
Fix XSS in card aria labels
2022-08-02 14:36:36 -04:00
Bill Thornton
eb4159788d Fix XSS in card aria labels 2022-08-02 13:51:20 -04:00
Joshua Boniface
2feaff3648 Bump version to 10.8.3 2022-08-01 20:22:02 -04:00
Joshua M. Boniface
45fe89c26f Bump version to 10.8.2 2022-08-01 14:27:58 -04:00
Bill Thornton
b167bf2d37 Merge pull request #3781 from yahuli/fix-type-error 2022-07-29 18:32:10 -04:00
Bill Thornton
6fe43e45e9 Merge pull request #3778 from nielsvanvelzen/itemdetails-download-missing-args
Add title and filename to download request in item details
2022-07-27 09:41:40 -04:00
dumbfox
d23aa6ada4 Fix TypeError 2022-07-27 10:49:13 +00:00
Niels van Velzen
7936502047 Fix serverId in item details download request
Co-authored-by: Bill Thornton <thornbill@users.noreply.github.com>
2022-07-27 08:59:47 +02:00
Bill Thornton
79c53c6458 Merge pull request #3775 from daullmer/remote-user-config
Fix user specific remote access
2022-07-27 00:15:35 -04:00
Niels van Velzen
5489f34b92 Add title and filename to download request in item details 2022-07-26 22:16:06 +02:00
David Ullmer
5f659b0ef6 Fix user specific remote access 2022-07-23 14:30:41 +02:00
Bill Thornton
db346e4c05 Merge pull request #3760 from nielsvanvelzen/fix-logs-submit
Fix saving log settings not working
2022-07-11 14:28:24 -04:00
Niels van Velzen
b05c7e3309 Fix saving log settings not working 2022-07-10 17:21:59 +02:00
Bill Thornton
8e7a3045dd Merge pull request #3751 from nielsvanvelzen/splashscreen-config-fix
Fix splash screen checkbox always unchecked
2022-07-06 16:28:23 -04:00
Niels van Velzen
7c33579260 Fix splash screen checkbox always unchecked 2022-07-06 13:34:56 +02:00
Bill Thornton
a38d0bbacc Merge pull request #3730 from SenorSmartyPants/CardbuilderEpisodeWithNoName
CardBuilder: Test episode number in addition to episode name
2022-07-02 00:30:31 -04:00
SenorSmartyPants
f4ee7076dd Test episode number in addition to episode name
Display episode number on card if present. Some episodes can have no name, but are still numbered (in my EPG data)
2022-06-28 14:16:09 -05:00
Bill Thornton
42bec6c11e Merge pull request #3729 from SenorSmartyPants/SeriesImageDownload
Save series images under season, when browsing parent images for season
2022-06-28 13:43:36 -04:00
SenorSmartyPants
b93c244e2d Save series images under season, when browsing parent images for season 2022-06-27 14:41:53 -05:00
Joshua M. Boniface
e1ed816a13 Bump version to 10.8.1 2022-06-26 21:00:43 -04:00
Joshua M. Boniface
bc48691738 Merge pull request #3724 from samcon/fix_resume_webos 2022-06-26 20:58:23 -04:00
Joshua M. Boniface
ae83d1d356 Merge pull request #3720 from Shadowghost/device-logo-fix 2022-06-26 20:56:17 -04:00
Joshua M. Boniface
66b86044a9 Merge pull request #3719 from nyanmisaka/dovi-meta 2022-06-26 20:55:44 -04:00
Joshua M. Boniface
d967ce860c Merge pull request #3721 from Shadowghost/fix-stream-autoselect 2022-06-26 20:54:51 -04:00
Joshua M. Boniface
d11b51d0f1 Update src/plugins/htmlVideoPlayer/plugin.js
Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com>
2022-06-26 20:54:12 -04:00
Joshua M. Boniface
0e0dd46c1b Merge pull request #3722 from nyanmisaka/fix-audio-ch 2022-06-26 20:53:47 -04:00
Nyanmisaka
5978d157e7 Remove unnecessary check 2022-06-27 06:23:00 +08:00
Nyanmisaka
ba34384b71 Apply suggestion from code review 2022-06-27 05:54:36 +08:00
Shadowghost
a792737add Use stream defaults in stream auto selection if previous source had no stream of that kind 2022-06-26 23:30:42 +02:00
Shadowghost
22a77ce54e Apply suggestions from code review
Co-authored-by: Dmitry Lyzo <56478732+dmitrylyzo@users.noreply.github.com>
2022-06-26 22:40:28 +02:00
Samcon
cab6e34390 Fix setCurrentTimeIfNeeded calc + change preload to auto to resume in WebOS 2022-06-26 14:37:04 +03:00
Cody Robibero
c20243c8bf Merge pull request #3571 from mihawk90/fedora-spec-rework
Cleanup and standardise Fedora build (web)
2022-06-23 08:06:21 -06:00
nyanmisaka
54e3276ba2 Fix 6ch audio is disabled on AC3 supported browsers 2022-06-23 21:44:18 +08:00
Joshua M. Boniface
f1ff81884a Merge pull request #3717 from dmitrylyzo/resolution 2022-06-20 10:01:01 -04:00
Shadowghost
2b0091eca2 Fix stream selection remembering 2022-06-19 17:42:00 +02:00
Shadowghost
37b4203967 Fix device logos for various clients 2022-06-19 16:34:25 +02:00
Tarulia
08d77664a7 Add make in Fedora Docker install
Fedora 36 doesn't seem to ship make, so add it manually.
2022-06-18 20:29:32 +02:00
nyanmisaka
b0ab4d3e63 Expose DoVi metadata and VideoRangeType 2022-06-18 22:16:10 +08:00
Dmitry Lyzo
f06cd961d5 Add maximum allowed video resolution selector 2022-06-17 22:14:14 +03:00
Bill Thornton
c8590d37ed Merge pull request #3713 from nyanmisaka/video-range-condition
Add VideoRangeType condition for web client
2022-06-17 13:10:36 -04:00
Nyanmisaka
8544bf08ac Merge branch 'release-10.8.z' into video-range-condition 2022-06-18 00:58:08 +08:00
Bill Thornton
6142283e99 Merge pull request #3714 from nyanmisaka/vpp-tm-configs
Expose VPP TM brightness/contrast gain options
2022-06-17 12:52:15 -04:00
Bill Thornton
721bc54dbd Merge pull request #3716 from Orhideous/fix/mimetype
Recognize MIME for transcoded audio in streams
2022-06-17 10:05:22 -04:00
Andriy Kushnir (Orhideous)
f2a826bb5d Left old MIME type for backward compatibility 2022-06-17 13:38:43 +03:00
nyanmisaka
8660f72915 Add translate 2022-06-17 17:06:37 +08:00
Andriy Kushnir (Orhideous)
8d531976a1 Recognize MIME for transcoded audio in streams
Fixes: #3663
See: jellyfin/jellyfin#6941
2022-06-16 22:40:31 +03:00
nyanmisaka
cbfa0acfb3 Expose VPP TM brightness/contrast gain options 2022-06-16 23:55:55 +08:00
nyanmisaka
7195e8a15d Add VideoRangeType condition for HDR content 2022-06-16 21:34:52 +08:00
Tarulia
26ec0e8e4a Use Fedora 36 image in Fedora Docker builds
* fixes #2351
2022-06-16 03:30:47 +02:00
Tarulia
5d5be25008 Move web-files to default location
* when running Jellyfin as a user from a terminal without passing
  arguments, it would not find the web-files. This moves them to the
  expected/default location.
* fixes #2059
2022-06-16 03:29:49 +02:00
Tarulia
390a0edf70 Standardise Fedora spec to packaging guidelines
* move actual building process to %build
* remove AutoReqProv as the package purely contains text files and
  fonts. There's no dependencies to begin with. This feature is also
  intended as sort of a "last resort" and we don't need this here.
* define LICENSE as %license, which automatically puts it in a
  standardised directory
2022-06-16 03:29:49 +02:00
Tarulia
2896cfbdb2 Adjust license in Fedora Spec according to LICENSE 2022-06-16 03:29:49 +02:00
Tarulia
2ff0a67e10 Rewrite Fedora build version detection
Rewrite so we don't need to constantly update with every new Fedora
release. This is especially useful when Fedora and Jellyfin release
cycles don't line up.

Version selection is as follows:
* TARGET environment variable, which is currently used already
* Currently running Fedora version
* Hardcoded Fallback version that can be updated occasionally
2022-06-16 03:29:49 +02:00
Bill Thornton
0bd774dd45 Merge pull request #3694 from crobibero/disable-splashscreen
Add the ability to enable/disable the splashscreen
2022-06-14 10:28:33 -04:00
Bill Thornton
4fdfcde018 Merge pull request #3701 from nyanmisaka/patch-1
Remove MPEG4 hwaccel from AMF
2022-06-14 10:14:00 -04:00
Bill Thornton
eaefc7bea1 Merge pull request #3704 from zhuangzhuang/release-10.8.z
Fix empty  avatar when upload same profile image
2022-06-14 10:12:22 -04:00
zhuangzhuang1988
427bdb2203 Fix empty avatar when upload same profile image
(cherry picked from commit 9cb5b03edc605fb62d0b664dee26df27540ae532)
2022-06-14 09:57:30 +08:00
Nyanmisaka
e8e531ffb2 Remove MPEG4 hwaccel from AMF 2022-06-14 02:19:09 +08:00
Cody Robibero
a3f9f45c54 Add strings 2022-06-12 09:22:58 -06:00
Cody Robibero
801d656d48 Add the ability to enable/disable the splashscreen 2022-06-12 08:50:49 -06:00
Joshua M. Boniface
e727eed1d1 Bump version to 10.8.0 2022-06-10 22:17:34 -04:00
Joshua M. Boniface
0701c4dff3 Merge pull request #3668 from dmitrylyzo/show-play-settings 2022-06-06 11:52:20 -04:00
Bill Thornton
57312e5cd5 Merge pull request #3662 from 1337joe/fix-tv-guide-search-2
Support searching for tv programs
2022-05-25 11:46:16 -04:00
Joe Rogers
07d2537de6 Restrict series/programs to uncategorized items 2022-05-24 18:43:06 +02:00
Bill Thornton
46f31b3f15 Merge pull request #3664 from dmitrylyzo/fix-memory-leak 2022-05-23 08:03:05 -04:00
Dmitry Lyzo
abbc5963f2 Show PlaySettings button even if transcoding isn't supported 2022-05-22 14:55:59 +03:00
Cody Robibero
f1274041ce Merge pull request #3667 from dmitrylyzo/release-lint-job
Enable Lint job on release branches
2022-05-22 05:27:54 -06:00
grafixeyehero
f2c747ce19 Fix lint 2022-05-22 13:57:22 +03:00
Dmitry Lyzo
8f4e87dd1f Enable Lint job on release branches 2022-05-22 13:51:24 +03:00
Dmitry Lyzo
3df8bd8be8 Bump @jellyfin/libass-wasm from 4.1.0 to 4.1.1
Bumps [@jellyfin/libass-wasm](https://github.com/jellyfin/JavascriptSubtitlesOctopus) from 4.1.0 to 4.1.1.
    - [Release notes](https://github.com/jellyfin/JavascriptSubtitlesOctopus/releases)
    - [Commits](https://github.com/jellyfin/JavascriptSubtitlesOctopus/compare/v4.1.0...v4.1.1)
2022-05-21 19:24:49 +03:00
Joe Rogers
46daea5238 Support searching for tv programs 2022-05-21 12:26:35 +02:00
Bill Thornton
1be3d30027 Merge pull request #3658 from thornbill/fix-channel-cards
Add workaround for channel card images
2022-05-20 15:34:01 -04:00
Bill Thornton
6b362fb591 Merge pull request #3657 from dmitrylyzo/unhighlight-play
Remove play button highlighting
2022-05-20 13:33:53 -04:00
Bill Thornton
cd31ae7afe Add workaround for channel card images 2022-05-20 13:26:57 -04:00
Dmitry Lyzo
a3fd4ba62f Remove play button highlighting 2022-05-20 10:32:14 +03:00
Bill Thornton
9b8507706c Merge pull request #3647 from 1337joe/disable_auto_collection
Disable "Automatically add to collection" by default
2022-05-17 11:38:34 -04:00
Joe Rogers
ea0161d132 Disable "Automatically add to collection" by default 2022-05-16 16:47:24 +02:00
Joshua M. Boniface
a36f515b30 Merge pull request #3644 from dmitrylyzo/bump-jso 2022-05-15 22:10:42 -04:00
Joshua M. Boniface
3ed1a9d098 Merge branch 'release-10.8.z' into bump-jso 2022-05-15 20:29:25 -04:00
Joshua M. Boniface
a07d5b7bd1 Merge pull request #3642 from taku0/font-family-by-language 2022-05-15 20:27:54 -04:00
taku0
88e129f793 Change CSS font-family by language preference
Fixes https://github.com/jellyfin/jellyfin-web/issues/913.

- Update `lang` attribute of `html` element on user preference change.
- Choose appropriate `font-family` depending on `lang` attribute using
  attribute selectors in CSS.
- Add `Noto Sans TC`, the Traditional Chinese (Taiwan variant).
- Fix event listener registration in `globalize.js`.
2022-05-16 09:18:06 +09:00
Joshua M. Boniface
4582e6185a Bump version to 10.8.0-beta3 2022-05-15 20:17:00 -04:00
Dmitry Lyzo
3ef4622772 Migrate to @jellyfin/libass-wasm@4.1.0 2022-05-16 01:51:47 +03:00
Bill Thornton
0895163344 Merge pull request #3643 from dmitrylyzo/fix-tizen5 2022-05-15 15:20:46 -04:00
Dmitry Lyzo
44b990e331 Resolve worker URLs
Worker in Tizen 5 doesn't resolve relative path with async request.
2022-05-15 18:10:55 +03:00
Bill Thornton
ae32ece346 Merge pull request #3637 from MinecraftPlaye/add-webp 2022-05-14 10:43:29 -04:00
Patrick Farwick
c929b6946d Add WebP to the page detection list for comics
The WebP image format can be used to store comic pages.
2022-05-14 11:09:19 +00:00
Bill Thornton
ff95eba35f Merge pull request #3639 from rhld16/unused-languages
Add missing languages
2022-05-14 02:26:29 -04:00
Bill Thornton
38ef0e2bf2 Merge pull request #3635 from dmitrylyzo/fix-ssa-font
Fix SSA/ASS missing font
2022-05-14 02:19:13 -04:00
rhld16
a8c20ca35b Add other date-fns valid languages 2022-05-13 19:30:19 +00:00
rhld16
790b2a0b14 Add Welsh (Cymraeg) to Web 2022-05-13 19:30:09 +00:00
Dmitry Lyzo
cf7a93cd80 Add font MIME type 2022-05-13 17:15:06 +03:00
Bill Thornton
d606a2aad5 Merge pull request #3628 from thornbill/actually-fix-artist-albums 2022-05-11 23:13:02 -04:00
Bill Thornton
91c9a26e4d Fix artists album lists 2022-05-11 12:40:42 -04:00
Bill Thornton
add924e35e Merge pull request #3622 from thornbill/restore-external-links
Restore external links on mobile
2022-05-09 10:00:48 -04:00
Bill Thornton
3d901d3680 Restore external links on mobile 2022-05-09 09:30:46 -04:00
Bill Thornton
f2226ee745 Merge pull request #3604 from thornbill/fix-custom-elements
Fix custom element creation
2022-05-06 13:30:10 -04:00
Bill Thornton
f2c27dc1b5 Merge pull request #3618 from thornbill/fix-backdrop-mobile
Fix backdrop being used on large screens in mobile layout
2022-05-05 16:01:34 -04:00
Bill Thornton
d43418bf05 Merge pull request #3617 from thornbill/fix-ipados-icon
Fix iPadOS icon
2022-05-05 16:00:27 -04:00
Bill Thornton
d0ee66c2ce Fix backdrop being used on large screens in mobile layout 2022-05-05 10:38:45 -04:00
Bill Thornton
bd25a4bdab Merge pull request #3616 from thornbill/revert-translations
Revert some poor translations
2022-05-05 10:19:13 -04:00
Bill Thornton
a088f6afb3 Fix iPad app icon 2022-05-05 10:07:28 -04:00
Bill Thornton
bc5c7817a8 Fix custom element creation 2022-05-05 02:12:38 -04:00
Bill Thornton
76ca94094b Merge pull request #3614 from dmitrylyzo/fix-undefined-streaminfo-url
Check undefined streamInfo.url
2022-05-05 01:51:08 -04:00
Bill Thornton
191c6dd678 Revert some poor translations 2022-05-05 01:38:35 -04:00
Dmitry Lyzo
a5163d0be4 fix: Check undefined streamInfo.url
When remuxing and transcoding are disabled and the media
cannot be played direct, `streamInfo.url` is not set.
2022-05-03 21:47:52 +03:00
Bill Thornton
4792631f06 Merge pull request #3543 from Shadowghost/strm-display-fix 2022-04-29 14:09:05 -04:00
Bill Thornton
f7e2f07c05 Merge pull request #3597 from nielsvanvelzen/qc-text-input
Use text input in Quick Connect page
2022-04-27 15:23:47 -04:00
Joshua M. Boniface
bc0288e57f Merge pull request #3601 from thornbill/fix-repositories-xss 2022-04-27 12:37:16 -04:00
Bill Thornton
ee3c4a2681 Fix XSS in repositories list 2022-04-27 10:19:14 -04:00
Niels van Velzen
b00e93dd3d Require at least 6 characters in Quick Connect input 2022-04-27 10:25:00 +02:00
Niels van Velzen
d03aed23c8 Use text input in Quick Connect page 2022-04-26 19:35:57 +02:00
Bill Thornton
9b697ce832 Merge pull request #3577 from thornbill/fix-sd-filter-backport
Fix SD filter state
2022-04-25 10:37:29 -04:00
Bill Thornton
f2b9dc3aaa Fix SD filter state 2022-04-20 00:14:17 -04:00
Joshua M. Boniface
9c9b2721c5 Bump version to 10.8.0-beta2 2022-04-17 15:53:31 -04:00
Bill Thornton
dace55907f Merge pull request #3535 from dmitrylyzo/fix-poster-resume
Fix 'resume' when clicking on item details poster
2022-04-12 16:08:42 -04:00
Bill Thornton
5ede3c8e47 Merge pull request #3547 from thornbill/touching-books
Fix touch events in epub player
2022-04-12 16:08:00 -04:00
Shadowghost
b69b9227c4 Add track sorting to mediainfo and player track selection 2022-04-07 11:37:57 +02:00
Shadowghost
dc956eb48c Restore sort order after jellyfin/jellyfin#7529, allow subtitle selector display whithout video stream 2022-04-06 23:53:42 +02:00
Bill Thornton
5820416ede Fix touch events in epub player 2022-04-05 17:02:17 -04:00
Dmitry Lyzo
e7b80b7fa2 fix: Fix 'resume' when clicking on item details poster
Find the element with 'action'.
2022-03-31 21:05:01 +03:00
Bill Thornton
33b1f039ea Merge pull request #3527 from thornbill/fix-rewatching-next-up
Fix rewatching next up status
2022-03-31 13:58:40 -04:00
Bill Thornton
a514d168bf Fix rewatching in next up checked when disabled 2022-03-31 11:56:55 -04:00
Bill Thornton
847a81afd3 Merge pull request #3525 from whiteowl3/patch-3
Correct Typo
2022-03-31 11:43:25 -04:00
Bill Thornton
ef811e699c Merge pull request #3519 from dmitrylyzo/fix-escapehtml
Escape HTML
2022-03-31 11:42:34 -04:00
whiteowl3
d13ea90c23 Correct Typo 2022-03-29 06:43:12 -04:00
Dmitry Lyzo
9338dd082b fix: Escape HTML 2022-03-29 02:25:54 +03:00
Joshua M. Boniface
b4fce063b0 Bump packaging version to 10.8.0~beta1 2022-03-27 12:12:46 -04:00
1230 changed files with 82026 additions and 176392 deletions

View File

@@ -0,0 +1,57 @@
jobs:
- job: Build
displayName: 'Build'
strategy:
matrix:
Development:
BuildConfiguration: development
Production:
BuildConfiguration: production
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Install Node'
inputs:
versionSpec: '12.x'
- task: Cache@2
displayName: 'Cache node_modules'
inputs:
key: 'npm | package-lock.json'
path: 'node_modules'
- script: 'npm ci --no-audit'
displayName: 'Install Dependencies'
env:
SKIP_PREPARE: 'true'
- script: 'npm run build:development'
displayName: 'Build Development'
condition: eq(variables['BuildConfiguration'], 'development')
- script: 'npm run build:production'
displayName: 'Build Production'
condition: eq(variables['BuildConfiguration'], 'production')
- script: 'test -d dist'
displayName: 'Check Build'
- script: 'mv dist jellyfin-web'
displayName: 'Rename Directory'
- task: ArchiveFiles@2
displayName: 'Archive Directory'
inputs:
rootFolderOrFile: 'jellyfin-web'
includeRootFolder: true
archiveFile: 'jellyfin-web-$(BuildConfiguration)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Release'
inputs:
targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip'
artifactName: 'jellyfin-web-$(BuildConfiguration)'

View File

@@ -0,0 +1,126 @@
jobs:
- job: BuildPackage
displayName: 'Build Packages'
strategy:
matrix:
CentOS:
BuildConfiguration: centos
Debian:
BuildConfiguration: debian
Fedora:
BuildConfiguration: fedora
Portable:
BuildConfiguration: portable
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
displayName: Set release version (stable)
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-web-$(BuildConfiguration) deployment'
displayName: 'Build Dockerfile'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
displayName: 'Run Dockerfile (unstable)'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
displayName: 'Run Dockerfile (stable)'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
- task: PublishPipelineArtifact@1
displayName: 'Publish Release'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
artifactName: 'jellyfin-web-$(BuildConfiguration)'
- task: SSH@0
displayName: 'Create target directory on repository server'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
- task: CopyFilesOverSSH@0
displayName: 'Upload artifacts to repository server'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
sshEndpoint: repository
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
contents: '**'
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
- job: BuildDocker
displayName: 'Build Docker'
pool:
vmImage: 'ubuntu-latest'
variables:
- name: JellyfinVersion
value: 0.0.0
steps:
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
displayName: Set release version (stable)
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
- task: Docker@2
displayName: 'Push Unstable Image'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
inputs:
repository: 'jellyfin/jellyfin-web'
command: buildAndPush
buildContext: '.'
Dockerfile: 'deployment/Dockerfile.docker'
containerRegistry: Docker Hub
tags: |
unstable-$(Build.BuildNumber)
unstable
- task: Docker@2
displayName: 'Push Stable Image'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
repository: 'jellyfin/jellyfin-web'
command: buildAndPush
buildContext: '.'
Dockerfile: 'deployment/Dockerfile.docker'
containerRegistry: Docker Hub
tags: |
stable-$(Build.BuildNumber)
$(JellyfinVersion)
- job: CollectArtifacts
displayName: 'Collect Artifacts'
dependsOn:
- BuildPackage
- BuildDocker
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
pool:
vmImage: 'ubuntu-latest'
steps:
- task: SSH@0
displayName: 'Update Unstable Repository'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable'
- task: SSH@0
displayName: 'Update Stable Repository'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) $(Build.SourceBranch)'

16
.ci/azure-pipelines.yml Normal file
View File

@@ -0,0 +1,16 @@
trigger:
batch: true
branches:
include:
- '*'
tags:
include:
- '*'
pr:
branches:
include:
- '*'
jobs:
- template: azure-pipelines-build.yml
- template: azure-pipelines-package.yml

1
.copr Symbolic link
View File

@@ -0,0 +1 @@
fedora/

View File

@@ -1,23 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
"name": "Node.js",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bullseye",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
//https://github.com/microsoft/vscode-dev-containers/issues/559
"postCreateCommand": "source $NVM_DIR/nvm.sh && nvm install 20"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -8,5 +8,5 @@ trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
[*.{json,yaml,yml}]
[*.json]
indent_size = 2

View File

@@ -1,9 +0,0 @@
{
"ecmaVersion": "es5",
"files": "./dist/**/*.js",
"not": [
"./dist/libraries/pdf.worker.js",
"./dist/libraries/worker-bundle.js",
"./dist/serviceworker.js"
]
}

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
dist
.idea
.vscode

232
.eslintrc.js Normal file
View File

@@ -0,0 +1,232 @@
const restrictedGlobals = require('confusing-browser-globals');
module.exports = {
root: true,
plugins: [
'@babel',
'react',
'promise',
'import',
'eslint-comments'
],
env: {
node: true,
es6: true,
es2017: true,
es2020: true
},
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
impliedStrict: true,
jsx: true
}
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
// 'plugin:promise/recommended',
'plugin:import/errors',
'plugin:eslint-comments/recommended',
'plugin:compat/recommended'
],
rules: {
'array-callback-return': ['error'],
'block-spacing': ['error'],
'brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
'comma-dangle': ['error', 'never'],
'comma-spacing': ['error'],
'default-case-last': ['error'],
'eol-last': ['error'],
'indent': ['error', 4, { 'SwitchCase': 1 }],
'jsx-quotes': ['error', 'prefer-single'],
'keyword-spacing': ['error'],
'max-statements-per-line': ['error'],
'no-empty-function': ['error'],
'no-floating-decimal': ['error'],
'no-multi-spaces': ['error'],
'no-multiple-empty-lines': ['error', { 'max': 1 }],
'no-restricted-globals': ['error'].concat(restrictedGlobals),
'no-trailing-spaces': ['error'],
'@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
'one-var': ['error', 'never'],
'padded-blocks': ['error', 'never'],
'prefer-const': ['error', {'destructuring': 'all'}],
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
'@babel/semi': ['error'],
'no-var': ['error'],
'space-before-blocks': ['error'],
'space-infix-ops': 'error',
'yoda': 'error'
},
settings: {
react: {
version: 'detect'
},
'import/extensions': [
'.js',
'.ts',
'.jsx',
'.tsx'
],
'import/parsers': {
'@typescript-eslint/parser': [ '.ts', '.tsx' ]
},
polyfills: [
// Native Promises Only
'Promise',
// whatwg-fetch
'fetch',
// document-register-element
'document.registerElement',
// resize-observer-polyfill
'ResizeObserver',
// fast-text-encoding
'TextEncoder',
// intersection-observer
'IntersectionObserver',
// Core-js
'Object.assign',
'Object.is',
'Object.setPrototypeOf',
'Object.toString',
'Object.freeze',
'Object.seal',
'Object.preventExtensions',
'Object.isFrozen',
'Object.isSealed',
'Object.isExtensible',
'Object.getOwnPropertyDescriptor',
'Object.getPrototypeOf',
'Object.keys',
'Object.entries',
'Object.getOwnPropertyNames',
'Function.name',
'Function.hasInstance',
'Array.from',
'Array.arrayOf',
'Array.copyWithin',
'Array.fill',
'Array.find',
'Array.findIndex',
'Array.iterator',
'String.fromCodePoint',
'String.raw',
'String.iterator',
'String.codePointAt',
'String.endsWith',
'String.includes',
'String.repeat',
'String.startsWith',
'String.trim',
'String.anchor',
'String.big',
'String.blink',
'String.bold',
'String.fixed',
'String.fontcolor',
'String.fontsize',
'String.italics',
'String.link',
'String.small',
'String.strike',
'String.sub',
'String.sup',
'RegExp',
'Number',
'Math',
'Date',
'async',
'Symbol',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'DataView',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'Reflect',
// Temporary while eslint-compat-plugin is buggy
'document.querySelector'
]
},
overrides: [
{
files: [
'./src/**/*.js',
'./src/**/*.ts'
],
parser: '@babel/eslint-parser',
env: {
node: false,
amd: true,
browser: true,
es6: true,
es2017: true,
es2020: true
},
globals: {
// Browser globals
'MediaMetadata': 'readonly',
// Tizen globals
'tizen': 'readonly',
'webapis': 'readonly',
// WebOS globals
'webOS': 'readonly',
// Dependency globals
'$': 'readonly',
'jQuery': 'readonly',
// Jellyfin globals
'ApiClient': 'writable',
'Events': 'writable',
'chrome': 'writable',
'DlnaProfilePage': 'writable',
'DashboardPage': 'writable',
'Emby': 'readonly',
'getParameterByName': 'writable',
'getWindowLocationSearch': 'writable',
'Globalize': 'writable',
'Hls': 'writable',
'dfnshelper': 'writable',
'LibraryMenu': 'writable',
'LinkParser': 'writable',
'LiveTvHelpers': 'writable',
'Loading': 'writable',
'MetadataEditor': 'writable',
'PlaylistViewer': 'writable',
'ServerNotifications': 'writable',
'TaskButton': 'writable',
'UserParentalControlPage': 'writable',
'Windows': 'readonly'
},
rules: {
}
},
{
files: [
'./src/**/*.ts',
'./src/**/*.tsx'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-comments/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended'
]
}
]
};

7
.github/CODEOWNERS vendored
View File

@@ -1 +1,6 @@
* @jellyfin/web
.ci @dkanada @EraYaN
.github @jellyfin/core
fedora @joshuaboniface
debian @joshuaboniface
.copr @joshuaboniface
deployment @joshuaboniface

32
.github/ISSUE_TEMPLATE/1-bug-report.md vendored Normal file
View File

@@ -0,0 +1,32 @@
---
name: Bug Report
about: You have noticed a general issue or regression, and would like to report it
labels: bug
---
**Describe The Bug**
<!-- A clear and concise description of what the bug is. -->
**Steps To Reproduce**
<!-- Steps to reproduce the behavior: -->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected Behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Logs**
<!-- Please paste any log errors. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**System (please complete the following information):**
- Platform: [e.g. Linux, Windows, iPhone, Tizen]
- Browser: [e.g. Firefox, Chrome, Safari]
- Jellyfin Version: [e.g. 10.6.0]
**Additional Context**
<!-- Add any other context about the problem here. -->

View File

@@ -1,122 +0,0 @@
name: Bug Report
description: You have noticed a general issue or regression, and would like to report it
labels:
- bug
body:
- type: checkboxes
id: before-posting
attributes:
label: "This issue respects the following points:"
description: All conditions are **required**.
options:
- label: This issue is **not** already reported on [GitHub](https://github.com/jellyfin/jellyfin-web/issues?q=is%3Aissue) _(I've searched it)_.
required: true
- label: I agree to follow Jellyfin's [Code of Conduct](https://jellyfin.org/docs/general/community-standards.html#code-of-conduct).
required: true
- label: This report addresses only a single issue; If you encounter multiple issues, kindly create separate reports for each one.
required: true
- type: markdown
attributes:
value: |
## Bug information
- type: textarea
id: description
attributes:
label: Describe the bug
description: |
A clear and concise description of the bug.
You can also attach screenshots or screen recordings here to help explain your issue.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction Steps
description: |
Steps to reproduce the behavior:
placeholder: |
1. Go to …
2. Click on …
3. Scroll down to …
4. See error / the app crashes
validations:
required: true
- type: textarea
id: behaviour
attributes:
label: Expected/Actual behaviour
description: |
Describe the behavior you were expecting versus what actually occurred.
placeholder: |
I expected the app to... However, the actual behavior was that...
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs
description: |
Please paste any log errors.
placeholder: Paste logs…
- type: markdown
attributes:
value: |
## Environment
- type: markdown
attributes:
value: |
### Server
You will find these values in your Admin Dashboard
- type: input
id: server-version
attributes:
label: Server version
placeholder: 10.10.2
validations:
required: true
- type: input
id: web-version
attributes:
label: Web version
placeholder: 10.10.2
validations:
required: true
- type: input
id: build-version
attributes:
label: Build version
placeholder: 10.10.2
validations:
required: true
- type: markdown
attributes:
value: |
### Client
Information about the device you are seeing the issue on
- type: input
id: platform
attributes:
label: Platform
description: Specify the operating system or device where the issue occurs. If relevant, include details like version or model.
placeholder: e.g. Linux, Windows, iPhone, Tizen
validations:
required: true
- type: input
id: browser
attributes:
label: Browser
description: Indicate which browser you're using when encountering the issue. If possible, mention the browser version as well.
placeholder: e.g. Firefox, Chrome, Safari
validations:
required: true
- type: markdown
attributes:
value: |
## Additional
- type: textarea
attributes:
label: Additional information
description: Include any relevant details, resources, or screenshots that might help in understanding or implementing the request.
placeholder: Add any additional context here.
validations:
required: false

View File

@@ -0,0 +1,22 @@
---
name: Playback Issue
about: You have playback issues with some files
labels: playback
---
**Describe The Bug**
<!-- A clear and concise description of what the bug is. -->
**Media Information**
<!-- Please paste any ffprobe or MediaInfo logs. -->
**Screenshots**
<!-- Add screenshots from the Playback Data and Media Info. -->
**System (please complete the following information):**
- Platform: [e.g. Linux, Windows, iPhone, Tizen]
- Browser: [e.g. Firefox, Chrome, Safari]
- Jellyfin Version: [e.g. 10.6.0]
**Additional Context**
<!-- Add any other context about the problem here. -->

View File

@@ -1,145 +0,0 @@
name: Playback Issue
description: Create a bug report related to media playback
labels:
- bug
- playback
body:
- type: checkboxes
id: before-posting
attributes:
label: "This issue respects the following points:"
description: All conditions are **required**.
options:
- label: This issue is **not** already reported on [GitHub](https://github.com/jellyfin/jellyfin-web/issues?q=is%3Aissue) _(I've searched it)_.
required: true
- label: I agree to follow Jellyfin's [Code of Conduct](https://jellyfin.org/docs/general/community-standards.html#code-of-conduct).
required: true
- label: This report addresses only a single issue; If you encounter multiple issues, kindly create separate reports for each one.
required: true
- type: markdown
attributes:
value: |
## Bug information
- type: textarea
id: description
attributes:
label: Describe the bug
description: |
A clear and concise description of the bug.
You can also attach screenshots or screen recordings here to help explain your issue.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction Steps
description: |
Steps to reproduce the behavior:
placeholder: |
1. Go to …
2. Click on …
3. Scroll down to …
4. See error / the app crashes
validations:
required: true
- type: textarea
id: behaviour
attributes:
label: Expected/Actual behaviour
description: |
Describe the behavior you were expecting versus what actually occurred.
placeholder: |
I expected the app to... However, the actual behavior was that...
validations:
required: true
- type: textarea
id: mediainfo
attributes:
label: Media info of the file
description: |
Please share the media information for the file causing issues. You can use a variety of tools to retrieve this information.
- Use ffprobe (`ffprobe ./file.mp4`)
- Copy the media info from the web interface
placeholder: Paste media info…
render: shell
- type: markdown
attributes:
value: |
## Logs
- type: textarea
id: logs
attributes:
label: Logs
description: |
Please paste your logs here if applicable.
placeholder: Paste logs…
- type: textarea
id: logs-ffmpeg
attributes:
label: FFmpeg logs
description: |
Please paste your FFmpeg logs here if available. You can find these in your servers dashboard under "logs".
placeholder: Paste logs…
render: shell
- type: markdown
attributes:
value: |
## Environment
- type: markdown
attributes:
value: |
### Server
You will find these values in your Admin Dashboard
- type: input
id: server-version
attributes:
label: Server version
placeholder: 10.10.2
validations:
required: true
- type: input
id: web-version
attributes:
label: Web version
placeholder: 10.10.2
validations:
required: true
- type: input
id: build-version
attributes:
label: Build version
placeholder: 10.10.2
validations:
required: true
- type: markdown
attributes:
value: |
### Client
Information about the device you are seeing the issue on
- type: input
id: platform
attributes:
label: Platform
description: Specify the operating system or device where the issue occurs. If relevant, include details like version or model.
placeholder: e.g. Linux, Windows, iPhone, Tizen
validations:
required: true
- type: input
id: browser
attributes:
label: Browser
description: Indicate which browser you're using when encountering the issue. If possible, mention the browser version as well.
placeholder: e.g. Firefox, Chrome, Safari
validations:
required: true
- type: markdown
attributes:
value: |
## Additional
- type: textarea
attributes:
label: Additional information
description: Include any relevant details, resources, or screenshots that might help in understanding or implementing the request.
placeholder: Add any additional context here.
validations:
required: false

View File

@@ -0,0 +1,13 @@
---
name: Technical Discussion
about: You want to discuss technical aspects of changes you intend to make
labels: enhancement
---
<!-- Explain the change and the motivations behind it.
For example, if you plan to rely on a new dependency, explain why and what
it brings to the project.
If you plan to make significant changes, go roughly over the steps you intend
to take and how you would divide the change in PRs of a manageable size. -->

View File

@@ -0,0 +1,9 @@
---
name: Meta Issue
about: You want to track a number of other issues as part of a larger project
labels: meta
---
* [ ] Issue 1 [#123]
* [ ] Issue 2 [#456]
* [ ] ...

2
.github/SUPPORT.md vendored
View File

@@ -7,7 +7,7 @@ When looking for support or information, please first search for your
question in these venues:
* [Jellyfin Forum](https://forum.jellyfin.org)
* [Jellyfin Documentation](https://jellyfin.org/docs/)
* [Jellyfin Documentation](https://docs.jellyfin.org)
* [Open or **closed** issues in the organization](https://github.com/issues?q=sort%3Aupdated-desc+org%3Ajellyfin+is%3Aissue+)
If you didn't find an answer in the resources above, contributors and other

View File

@@ -1,6 +1,6 @@
<!--
Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y).
For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.org/docs/general/contributing/issues page.
For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://docs.jellyfin.org/general/contributing/issues.html page.
-->
**Changes**

67
.github/renovate.json vendored
View File

@@ -1,28 +1,51 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>jellyfin/.github//renovate-presets/nodejs",
":dependencyDashboard"
"packageRules": [
{
"matchManagers": ["npm"],
"addLabels": ["javascript"]
},
{
"description": "Adds label to dev dependency updates",
"matchDepTypes": ["devDependencies"],
"addLabels": ["dev-deps"]
},
{
"description": "Collects and groups dev dependency updates",
"matchDepTypes": ["devDependencies"],
"groupName": "development dependencies",
"groupSlug": "dev-deps"
},
{
"description": "Collects and groups npm dependency updates",
"matchDepTypes": ["dependencies"],
"groupName": "dependencies",
"groupSlug": "deps"
},
{
"description": "Collects and groups GitHub Action dependency updates",
"matchDepTypes": ["action"],
"addLabels": ["github_actions"],
"groupName": "CI dependencies",
"groupSlug": "ci-deps"
},
{
"description": "Disables HLS.js major updates",
"matchPackageNames": ["hls.js"],
"matchUpdateTypes": "major",
"enabled": false
}
],
"vulnerabilityAlerts": {
"addLabels": ["security"]
},
"dependencyDashboard": false,
"ignoreDeps": ["npm", "node"],
"lockFileMaintenance": {
"enabled": false
},
"packageRules": [
{
"matchPackageNames": [ "@jellyfin/sdk" ],
"followTag": "unstable",
"minimumReleaseAge": null,
"prPriority": 5,
"schedule": [ "after 7:00 am" ]
},
{
"matchPackageNames": [ "dompurify" ],
"matchUpdateTypes": [ "major" ],
"enabled": false
},
{
"matchPackageNames": [ "hls.js" ],
"prPriority": 5
}
]
"enabledManagers": ["npm", "github-actions"],
"labels": ["dependencies"],
"prHourlyLimit": 2,
"rebaseWhen": "conflicted",
"rangeStrategy": "pin"
}

View File

@@ -1,15 +0,0 @@
name: Automation 🎛️
on:
workflow_call:
jobs:
conflicts:
name: Merge conflict labeling 🏷️
runs-on: ubuntu-latest
steps:
- uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
with:
dirtyLabel: 'merge conflict'
commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.'
repoToken: ${{ secrets.JF_BOT_TOKEN }}

View File

@@ -1,40 +0,0 @@
name: GitHub CodeQL 🔬
on:
workflow_call:
inputs:
commit:
required: true
type: string
jobs:
analyze:
name: Analyze ${{ matrix.language }} 🔬
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language:
- javascript-typescript
steps:
- name: Checkout repository ⬇️
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Initialize CodeQL 🛠️
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
queries: security-and-quality
languages: ${{ matrix.language }}
- name: Autobuild 📦
uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
- name: Perform CodeQL Analysis 🧪
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
category: '/language:${{matrix.language}}'

View File

@@ -1,59 +0,0 @@
name: Deploy 🏗️
on:
workflow_call:
inputs:
branch:
required: true
type: string
commit:
required: false
type: string
comment:
required: false
type: boolean
artifact_name:
required: false
type: string
default: frontend
jobs:
cf-pages:
name: CloudFlare Pages 📃
runs-on: ubuntu-latest
environment:
name: ${{ inputs.branch == 'master' && 'Production' || 'Preview' }}
url: ${{ steps.cf.outputs.deployment-url }}
outputs:
url: ${{ steps.cf.outputs.deployment-url }}
steps:
- name: Download workflow artifact ⬇️
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: ${{ inputs.artifact_name }}
path: dist
- name: Publish to Cloudflare Pages 📃
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1
id: cf
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=jellyfin-web --branch=${{ inputs.branch }}
compose-comment:
name: Compose and push comment 📝
# Always run so the comment is composed for the workflow summary
if: ${{ always() }}
uses: ./.github/workflows/__job_messages.yml
secrets: inherit
needs:
- cf-pages
with:
branch: ${{ inputs.branch }}
commit: ${{ inputs.commit }}
preview_url: ${{ needs.cf-pages.outputs.url }}
in_progress: false
comment: ${{ inputs.comment }}

View File

@@ -1,65 +0,0 @@
name: Job messages ⚙️
on:
workflow_call:
inputs:
branch:
required: false
type: string
commit:
required: true
type: string
preview_url:
required: false
type: string
in_progress:
required: true
type: boolean
comment:
required: false
type: boolean
marker:
description: Hidden marker to detect PR comments composed by the bot
required: false
type: string
default: "CFPages-deployment"
jobs:
cf_pages_msg:
name: CloudFlare Pages deployment 📃🚀
runs-on: ubuntu-latest
steps:
- name: Compose message 📃
if: ${{ always() }}
id: compose
env:
COMMIT: ${{ inputs.commit }}
PREVIEW_URL: ${{ inputs.preview_url != '' && (inputs.branch != 'master' && inputs.preview_url || format('https://jellyfin-web.pages.dev ({0})', inputs.preview_url)) || 'Not available' }}
DEPLOY_STATUS: ${{ inputs.in_progress && '🔄 Deploying...' || (inputs.preview_url != '' && '✅ Deployed!' || '❌ Failure. Check workflow logs for details') }}
DEPLOYMENT_TYPE: ${{ inputs.branch != 'master' && '🔀 Preview' || '⚙️ Production' }}
WORKFLOW_RUN: ${{ !inputs.in_progress && format('**[View build logs](https://github.com/{0}/actions/runs/{1})**', github.repository, github.run_id) || '' }}
# EOF is needed for multiline environment variables in a GitHub Actions context
run: |
echo "## Cloudflare Pages deployment" > $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| **Latest commit** | <code>${COMMIT::7}</code> |" >> $GITHUB_STEP_SUMMARY
echo "|------------------------- |:----------------------------: |" >> $GITHUB_STEP_SUMMARY
echo "| **Status** | $DEPLOY_STATUS |" >> $GITHUB_STEP_SUMMARY
echo "| **Preview URL** | $PREVIEW_URL |" >> $GITHUB_STEP_SUMMARY
echo "| **Type** | $DEPLOYMENT_TYPE |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "$WORKFLOW_RUN" >> $GITHUB_STEP_SUMMARY
COMPOSED_MSG=$(cat $GITHUB_STEP_SUMMARY)
echo "msg<<EOF" >> $GITHUB_ENV
echo "$COMPOSED_MSG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Push comment to Pull Request 🔼
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1
if: ${{ inputs.comment && steps.compose.conclusion == 'success' }}
with:
github-token: ${{ secrets.JF_BOT_TOKEN }}
message: ${{ env.msg }}
comment-tag: ${{ inputs.marker }}

View File

@@ -1,45 +0,0 @@
name: Packaging 📦
on:
workflow_call:
inputs:
commit:
required: false
type: string
jobs:
run-build-prod:
name: Run production build 🏗️
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ inputs.commit || github.sha }}
- name: Setup node environment
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20
cache: npm
check-latest: true
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run a production build
env:
JELLYFIN_VERSION: ${{ inputs.commit || github.sha }}
run: npm run build:production
- name: Update config.json for testing
run: |
jq '.multiserver=true | .servers=["https://demo.jellyfin.org/unstable"]' dist/config.json > dist/config.tmp.json
mv dist/config.tmp.json dist/config.json
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: frontend
path: dist

View File

@@ -1,61 +0,0 @@
name: Quality checks 👌🧪
on:
workflow_call:
inputs:
commit:
required: true
type: string
workflow_dispatch:
jobs:
dependency-review:
name: Vulnerable dependencies 🔎
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Scan
uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3
with:
## Workaround from https://github.com/actions/dependency-review-action/issues/456
## TODO: Remove when necessary
base-ref: ${{ github.event.pull_request.base.sha || 'master' }}
head-ref: ${{ github.event.pull_request.head.sha || github.ref }}
quality:
name: Run ${{ matrix.command }} 🕵️‍♂️
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
command:
- build:es-check
- lint
- stylelint
- build:check
- test
steps:
- name: Checkout ⬇️
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ inputs.commit }}
show-progress: false
- name: Setup node environment ⚙️
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20
cache: npm
check-latest: true
- name: Install dependencies 📦
run: npm ci --no-audit
- name: Run ${{ matrix.command }} ⚙️
run: npm run ${{ matrix.command }}

20
.github/workflows/automation.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: 'Automation'
on:
push:
branches:
- master
pull_request_target:
types:
- synchronize
jobs:
triage:
name: 'Merge conflict labeling'
runs-on: ubuntu-latest
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
steps:
- uses: eps1lon/actions-label-merge-conflict@v2.0.1
with:
dirtyLabel: 'merge conflict'
repoToken: ${{ secrets.JF_BOT_TOKEN }}

31
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '30 7 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

28
.github/workflows/commands.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Commands
on:
issue_comment:
types:
- created
- edited
jobs:
rebase:
name: Rebase
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '@jellyfin-bot rebase') && github.event.comment.author_association == 'MEMBER'
runs-on: ubuntu-latest
steps:
- name: Notify as seen
uses: peter-evans/create-or-update-comment@v1.4.5
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ github.event.comment.id }}
reactions: '+1'
- name: Checkout the latest code
uses: actions/checkout@v3.0.0
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.5
env:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}

119
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,119 @@
name: Lint
on:
push:
branches: [ master, release* ]
pull_request:
branches: [ master, release* ]
jobs:
run-eslint:
name: Run eslint
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup node environment
uses: actions/setup-node@v3.0.0
with:
node-version: 12
check-latest: true
- name: Get npm cache directory path
id: npm-cache-dir-path
run: echo "::set-output name=dir::$(npm config get cache)"
- name: Cache node_modules
uses: actions/cache@v3.0.0
id: npm-cache
with:
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install Node.js dependencies
run: npm ci --no-audit
env:
SKIP_PREPARE: true
- name: Run eslint
run: npm run lint
run-stylelint-css:
name: Run stylelint (css)
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup node environment
uses: actions/setup-node@v3.0.0
with:
node-version: 12
check-latest: true
- name: Get npm cache directory path
id: npm-cache-dir-path
run: echo "::set-output name=dir::$(npm config get cache)"
- name: Cache node_modules
uses: actions/cache@v3.0.0
id: npm-cache
with:
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Set up stylelint matcher
uses: xt0rted/stylelint-problem-matcher@v1
- name: Install Node.js dependencies
run: npm ci --no-audit
env:
SKIP_PREPARE: true
- name: Run stylelint
run: npm run stylelint:css
run-stylelint-scss:
name: Run stylelint (scss)
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup node environment
uses: actions/setup-node@v3.0.0
with:
node-version: 12
check-latest: true
- name: Get npm cache directory path
id: npm-cache-dir-path
run: echo "::set-output name=dir::$(npm config get cache)"
- name: Cache node_modules
uses: actions/cache@v3.0.0
id: npm-cache
with:
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Set up stylelint matcher
uses: xt0rted/stylelint-problem-matcher@v1
- name: Install Node.js dependencies
run: npm ci --no-audit
env:
SKIP_PREPARE: true
- name: Run stylelint
run: npm run stylelint:scss

View File

@@ -1,100 +0,0 @@
name: Pull Request 📥
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
on:
pull_request_target:
branches:
- master
- release*
paths-ignore:
- '**/*.md'
merge_group:
jobs:
push-comment:
name: Create comments ✍️
if: ${{ always() && !cancelled() && github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__job_messages.yml
secrets: inherit
with:
commit: ${{ github.event.pull_request.head.sha }}
in_progress: true
comment: true
build:
name: Build 🏗️
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__package.yml
with:
commit: ${{ github.event.pull_request.head.sha }}
automation:
name: Automation 🎛️
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__automation.yml
secrets: inherit
quality_checks:
name: Quality checks 👌🧪
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__quality_checks.yml
permissions: {}
with:
commit: ${{ github.event.pull_request.head.sha }}
codeql:
name: GitHub CodeQL 🔬
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__codeql.yml
permissions:
actions: read
contents: read
security-events: write
with:
commit: ${{ github.event.pull_request.head.sha }}
deploy:
name: Deploy 🚀
uses: ./.github/workflows/__deploy.yml
if: ${{ always() && !cancelled() && needs.build.result == 'success' && github.repository == 'jellyfin/jellyfin-web' }}
needs:
- push-comment
- build
permissions:
contents: read
deployments: write
secrets: inherit
with:
# If the PR is from the master branch of a fork, append the fork's name to the branch name
branch: ${{ github.event.pull_request.head.repo.full_name != github.repository && github.event.pull_request.head.ref == 'master' && format('{0}/{1}', github.event.pull_request.head.repo.full_name, github.event.pull_request.head.ref) || github.event.pull_request.head.ref }}
comment: true
commit: ${{ github.event.pull_request.head.sha }}
run-eslint:
name: Run eslint suggestions
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup node environment
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20
cache: npm
check-latest: true
- name: Install Node.js dependencies
run: npm ci --no-audit
- name: Run eslint
uses: CatChen/eslint-suggestion-action@4dda35decf912ab18ea3e071acec2c6c2eda00b6 # v4.1.18
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,58 +0,0 @@
name: Push & Release 🌍
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.ref }}
cancel-in-progress: true
on:
push:
branches:
- master
- release*
paths-ignore:
- '**/*.md'
jobs:
automation:
name: Automation 🎛️
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__automation.yml
secrets: inherit
main:
name: 'Unstable release 🚀⚠️'
uses: ./.github/workflows/__package.yml
with:
commit: ${{ github.sha }}
quality_checks:
name: Quality checks 👌🧪
if: ${{ always() && !cancelled() }}
uses: ./.github/workflows/__quality_checks.yml
permissions: {}
with:
commit: ${{ github.sha }}
codeql:
name: GitHub CodeQL 🔬
uses: ./.github/workflows/__codeql.yml
permissions:
actions: read
contents: read
security-events: write
with:
commit: ${{ github.sha }}
deploy:
name: Deploy 🚀
if: ${{ github.repository == 'jellyfin/jellyfin-web' }}
uses: ./.github/workflows/__deploy.yml
needs:
- main
permissions:
contents: read
deployments: write
secrets: inherit
with:
branch: ${{ github.ref_name }}
comment: false

View File

@@ -1,38 +1,27 @@
name: Scheduled tasks 🕑
name: Issue Stale Check
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
issues:
name: Check stale issues and PRs
stale:
runs-on: ubuntu-latest
if: ${{ contains(github.repository, 'jellyfin/') }}
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
- uses: actions/stale@v5.0.0
with:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
operations-per-run: 75
# Issues receive a stale warning after 120 days and close after an additional 21 days
days-before-stale: 120
days-before-pr-stale: -1
days-before-close: 21
days-before-pr-close: -1
exempt-issue-labels: regression,security,roadmap,future,feature,enhancement,confirmed
stale-issue-label: stale
stale-issue-message: |-
This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or master branch, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.org/contact).
# PRs are closed after having unresolved merge conflicts for 90 days
days-before-pr-stale: 0
days-before-pr-close: 90
only-pr-labels: merge conflict
stale-pr-label: stale
close-pr-message: |-
This PR has been closed due to having unresolved merge conflicts.
This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).

16
.gitignore vendored
View File

@@ -3,21 +3,19 @@ dist
web
node_modules
# test coverage
coverage
# config
config.json
# ide
.idea
.vs
.vscode
# log
yarn-error.log
# vim
*.sw?
# direnv
.direnv/
# environment related
.envrc
# build artifacts
fedora/jellyfin-web-*.src.rpm
fedora/jellyfin-web-*.tar.gz

1
.npmrc
View File

@@ -1,3 +1,2 @@
engine-strict=true
fund=false
save-exact=true

1
.nvmrc
View File

@@ -1 +0,0 @@
20

View File

@@ -1,3 +0,0 @@
# Exclude test files from Sonar sources
# See: https://docs.sonarcloud.io/advanced-setup/analysis-scope/#file-exclusion-and-inclusion
sonar.exclusions=src/**/*.test.js,src/**/*.test.ts

View File

@@ -1,8 +1,7 @@
{
"plugins": [
"@stylistic/stylelint-plugin",
"stylelint-no-browser-hacks/lib"
],
"stylelint-no-browser-hacks/lib"
],
"rules": {
"at-rule-empty-line-before": [ "always", {
"except": [
@@ -11,20 +10,19 @@
],
"ignore": ["after-comment"]
} ],
"@stylistic/at-rule-name-case": "lower",
"@stylistic/at-rule-name-space-after": "always-single-line",
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-no-unknown": true,
"at-rule-no-vendor-prefix": true,
"@stylistic/at-rule-semicolon-newline-after": "always",
"@stylistic/block-closing-brace-empty-line-before": "never",
"@stylistic/block-closing-brace-newline-after": "always",
"@stylistic/block-closing-brace-newline-before": "always-multi-line",
"@stylistic/block-closing-brace-space-before": "always-single-line",
"at-rule-semicolon-newline-after": "always",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always-multi-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"@stylistic/block-opening-brace-newline-after": "always-multi-line",
"@stylistic/block-opening-brace-space-after": "always-single-line",
"@stylistic/block-opening-brace-space-before": "always",
"@stylistic/color-hex-case": "lower",
"block-opening-brace-newline-after": "always-multi-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"comment-empty-line-before": [ "always", {
@@ -43,8 +41,8 @@
"inside-single-line-block"
]
} ],
"@stylistic/declaration-bang-space-after": "never",
"@stylistic/declaration-bang-space-before": "always",
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [
true,
{
@@ -52,52 +50,51 @@
}
],
"declaration-block-no-shorthand-property-overrides": true,
"@stylistic/declaration-block-semicolon-newline-after": "always-multi-line",
"@stylistic/declaration-block-semicolon-space-after": "always-single-line",
"@stylistic/declaration-block-semicolon-space-before": "never",
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"@stylistic/declaration-block-trailing-semicolon": "always",
"@stylistic/declaration-colon-newline-after": "always-multi-line",
"@stylistic/declaration-colon-space-after": "always-single-line",
"@stylistic/declaration-colon-space-before": "never",
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"@stylistic/function-comma-newline-after": "always-multi-line",
"@stylistic/function-comma-space-after": "always-single-line",
"@stylistic/function-comma-space-before": "never",
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"@stylistic/function-max-empty-lines": 0,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"@stylistic/function-parentheses-newline-inside": "always-multi-line",
"@stylistic/function-parentheses-space-inside": "never-single-line",
"@stylistic/function-whitespace-after": "always",
"@stylistic/indentation": 4,
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"@stylistic/max-empty-lines": 1,
"@stylistic/media-feature-colon-space-after": "always",
"@stylistic/media-feature-colon-space-before": "never",
"@stylistic/media-feature-name-case": "lower",
"max-empty-lines": 1,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-unknown": true,
"media-feature-name-no-vendor-prefix": true,
"@stylistic/media-feature-parentheses-space-inside": "never",
"@stylistic/media-feature-range-operator-space-after": "always",
"@stylistic/media-feature-range-operator-space-before": "always",
"@stylistic/media-query-list-comma-newline-after": "always-multi-line",
"@stylistic/media-query-list-comma-space-after": "always-single-line",
"@stylistic/media-query-list-comma-space-before": "never",
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-descending-specificity": true,
"no-duplicate-at-import-rules": true,
"no-duplicate-selectors": true,
"no-empty-source": true,
"@stylistic/no-eol-whitespace": true,
"@stylistic/no-extra-semicolons": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"@stylistic/no-missing-end-of-source-newline": true,
"@stylistic/number-leading-zero": "always",
"@stylistic/number-no-trailing-zeros": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"plugin/no-browser-hacks": true,
"@stylistic/property-case": "lower",
"property-case": "lower",
"property-no-unknown": [
true,
{
@@ -106,25 +103,23 @@
]
}
],
"property-no-vendor-prefix": true,
"rule-empty-line-before": [ "always-multi-line", {
"except": ["first-nested"],
"ignore": ["after-comment"]
} ],
"@stylistic/selector-attribute-brackets-space-inside": "never",
"@stylistic/selector-attribute-operator-space-after": "never",
"@stylistic/selector-attribute-operator-space-before": "never",
"@stylistic/selector-combinator-space-after": "always",
"@stylistic/selector-combinator-space-before": "always",
"@stylistic/selector-descendant-combinator-no-non-space": true,
"@stylistic/selector-list-comma-newline-after": "always",
"@stylistic/selector-list-comma-space-before": "never",
"@stylistic/selector-max-empty-lines": 0,
"selector-no-vendor-prefix": true,
"@stylistic/selector-pseudo-class-case": "lower",
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"@stylistic/selector-pseudo-class-parentheses-space-inside": "never",
"@stylistic/selector-pseudo-element-case": "lower",
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": [
true,
@@ -137,27 +132,11 @@
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"string-no-newline": true,
"@stylistic/unit-case": "lower",
"unit-case": "lower",
"unit-no-unknown": true,
"value-no-vendor-prefix": true,
"@stylistic/value-list-comma-newline-after": "always-multi-line",
"@stylistic/value-list-comma-space-after": "always-single-line",
"@stylistic/value-list-comma-space-before": "never",
"@stylistic/value-list-max-empty-lines": 0
},
"overrides": [
{
"files": [
"*.scss",
"**/*.scss"
],
"customSyntax": "postcss-scss",
"plugins": [ "stylelint-scss" ],
"rules": {
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
"plugin/no-browser-hacks": null
}
}
]
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0
}
}

10
.stylelintrc.scss.json Normal file
View File

@@ -0,0 +1,10 @@
{
"extends": [ "./.stylelintrc.json" ],
"customSyntax": "postcss-scss",
"plugins": [ "stylelint-scss" ],
"rules": {
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
"plugin/no-browser-hacks": null
}
}

View File

@@ -1,5 +0,0 @@
{
"recommendations": [
"dbaeumer.vscode-eslint"
]
}

10
.vscode/settings.json vendored
View File

@@ -1,10 +0,0 @@
{
"[json][typescript][typescriptreact][javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.format.enable": true,
"editor.formatOnSave": false
}

View File

@@ -1,171 +1,121 @@
# Jellyfin Contributors
- [JoshuaBoniface](https://github.com/joshuaboniface)
- [nvllsvm](https://github.com/nvllsvm)
- [JustAMan](https://github.com/JustAMan)
- [dcrdev](https://github.com/dcrdev)
- [EraYaN](https://github.com/EraYaN)
- [flemse](https://github.com/flemse)
- [bfayers](https://github.com/bfayers)
- [Bond_009](https://github.com/Bond-009)
- [AnthonyLavado](https://github.com/anthonylavado)
- [dkanada](https://github.com/dkanada)
- [sparky8251](https://github.com/sparky8251)
- [LeoVerto](https://github.com/LeoVerto)
- [cvium](https://github.com/cvium)
- [grafixeyehero](https://github.com/grafixeyehero)
- [Drago96](https://github.com/drago-96)
- [ViXXoR](https://github.com/ViXXoR)
- [nkmerrill](https://github.com/nkmerrill)
- [TtheCreator](https://github.com/Tthecreator)
- [RazeLighter777](https://github.com/RazeLighter777)
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
- [thornbill](https://github.com/thornbill)
- [redSpoutnik](https://github.com/redSpoutnik)
- [DrPandemic](https://github.com/drpandemic)
- [Oddstr13](https://github.com/oddstr13)
- [petermcneil](https://github.com/petermcneil)
- [lewazo](https://github.com/lewazo)
- [Raghu Saxena](https://github.com/ckcr4lyf)
- [Nickbert7](https://github.com/Nickbert7)
- [ferferga](https://github.com/ferferga)
- [bilde2910](https://github.com/bilde2910)
- [Daniel Hartung](https://github.com/dhartung)
- [Ryan Hartzell](https://github.com/ryan-hartzell)
- [Thibault Nocchi](https://github.com/ThibaultNocchi)
- [MrTimscampi](https://github.com/MrTimscampi)
- [artiume](https://github.com/Artiume)
- [ConfusedPolarBear](https://github.com/ConfusedPolarBear)
- [Sarab Singh](https://github.com/sarab97)
- [DesertCookie](https://github.com/desertcookie)
- [GuilhermeHideki](https://github.com/GuilhermeHideki)
- [Andrei Oanca](https://github.com/OancaAndrei)
- [Cromefire_](https://github.com/cromefire)
- [Orry Verducci](https://github.com/orryverducci)
- [Camc314](https://github.com/camc314)
- [danieladov](https://github.com/danieladov)
- [Stephane Senart](https://github.com/ssenart)
- [imchasingshadows](https://github.com/imchasingshadows)
- [Ömer Erdinç Yağmurlu](https://github.com/omeryagmurlu)
- [Keegan Dahm](https://github.com/keegandahm)
- [GodTamIt](https://github.com/GodTamIt)
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
- [Matthew Jones](https://github.com/matthew-jones-uk)
- [taku0](https://github.com/taku0)
- [Viperinius](https://github.com/Viperinius)
- [is343](https://github.com/is343)
- [Meet Pandya](https://github.com/meet-k-pandya)
- [Peter Spenler](https://github.com/peterspenler)
- [jomp16](https://github.com/jomp16)
- [Leon de Klerk](https://github.com/leondeklerk)
- [CrispyBaguette](https://github.com/CrispyBaguette)
- [Vankerkom](https://github.com/vankerkom)
- [edvwib](https://github.com/edvwib)
- [Rob Farraher](https://github.com/farraherbg)
- [TelepathicWalrus](https://github.com/TelepathicWalrus)
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
- [Anantharaju S](https://github.com/Anantharajus)
- [Merlin Sievers](https://github.com/dann-merlin)
- [Fishbigger](https://github.com/fishbigger)
- [sleepycatcoding](https://github.com/sleepycatcoding)
- [TheMelmacian](https://github.com/TheMelmacian)
- [v0idMrK](https://github.com/v0idMrK)
- [tehciolo](https://github.com/tehciolo)
- [scampower3](https://github.com/scampower3)
- [LittleBigOwI](https://github.com/LittleBigOwI/)
- [Nate G](https://github.com/GGProGaming)
- [Grady Hallenbeck](https://github.com/grhallenbeck)
- [DinuD](https://github.com/DinuD)
- [Kevin Tan (Valius)](https://github.com/valius)
- [Rasmus Krämer](https://github.com/rasmuslos)
- [ntarelix](https://github.com/ntarelix)
- [btopherjohnson](https://github.com/btopherjohnson)
- [András Maróy](https://github.com/andrasmaroy)
- [Chris-Codes-It](https://github.com/Chris-Codes-It)
- [Vedant](https://github.com/viktory36)
- [GeorgeH005](https://github.com/GeorgeH005)
- [JPUC1143](https://github.com/Jpuc1143)
- [David Angel](https://github.com/davidangel)
- [Pithaya](https://github.com/Pithaya)
- [Peter Santos](https://github.com/prsantos-com)
- [Chaitanya Shahare](https://github.com/Chaitanya-Shahare)
- [Venkat Karasani](https://github.com/venkat-karasani)
- [Connor Smith](https://github.com/ConnorS1110)
- [iFraan](https://github.com/iFraan)
- [Ali](https://github.com/bu3alwa)
- [K. Kyle Puchkov](https://github.com/kepper104)
- [ItsAllAboutTheCode](https://github.com/ItsAllAboutTheCode)
- [Jxiced](https://github.com/Jxiced)
- [Derek Huber](https://github.com/Derek4aty1)
- [StableCrimson](https://github.com/StableCrimson)
- [diegoeche](https://github.com/diegoeche)
- [Free O'Toole](https://github.com/freeotoole)
- [TheBosZ](https://github.com/thebosz)
- [JoshuaBoniface](https://github.com/joshuaboniface)
- [nvllsvm](https://github.com/nvllsvm)
- [JustAMan](https://github.com/JustAMan)
- [dcrdev](https://github.com/dcrdev)
- [EraYaN](https://github.com/EraYaN)
- [flemse](https://github.com/flemse)
- [bfayers](https://github.com/bfayers)
- [Bond_009](https://github.com/Bond-009)
- [AnthonyLavado](https://github.com/anthonylavado)
- [dkanada](https://github.com/dkanada)
- [sparky8251](https://github.com/sparky8251)
- [LeoVerto](https://github.com/LeoVerto)
- [cvium](https://github.com/cvium)
- [grafixeyehero](https://github.com/grafixeyehero)
- [Drago96](https://github.com/drago-96)
- [ViXXoR](https://github.com/ViXXoR)
- [nkmerrill](https://github.com/nkmerrill)
- [TtheCreator](https://github.com/Tthecreator)
- [RazeLighter777](https://github.com/RazeLighter777)
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
- [thornbill](https://github.com/thornbill)
- [redSpoutnik](https://github.com/redSpoutnik)
- [DrPandemic](https://github.com/drpandemic)
- [Oddstr13](https://github.com/oddstr13)
- [petermcneil](https://github.com/petermcneil)
- [lewazo](https://github.com/lewazo)
- [Raghu Saxena](https://github.com/ckcr4lyf)
- [Nickbert7](https://github.com/Nickbert7)
- [ferferga](https://github.com/ferferga)
- [bilde2910](https://github.com/bilde2910)
- [Daniel Hartung](https://github.com/dhartung)
- [Ryan Hartzell](https://github.com/ryan-hartzell)
- [Thibault Nocchi](https://github.com/ThibaultNocchi)
- [MrTimscampi](https://github.com/MrTimscampi)
- [artiume](https://github.com/Artiume)
- [ConfusedPolarBear](https://github.com/ConfusedPolarBear)
- [Sarab Singh](https://github.com/sarab97)
- [DesertCookie](https://github.com/desertcookie)
- [GuilhermeHideki](https://github.com/GuilhermeHideki)
- [Andrei Oanca](https://github.com/OancaAndrei)
- [Cromefire_](https://github.com/cromefire)
- [Orry Verducci](https://github.com/orryverducci)
- [Camc314](https://github.com/camc314)
- [danieladov](https://github.com/danieladov)
- [Stephane Senart](https://github.com/ssenart)
- [imchasingshadows](https://github.com/imchasingshadows)
- [Ömer Erdinç Yağmurlu](https://github.com/omeryagmurlu)
- [Keegan Dahm](https://github.com/keegandahm)
- [GodTamIt](https://github.com/GodTamIt)
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
- [Matthew Jones](https://github.com/matthew-jones-uk)
- [taku0](https://github.com/taku0)
- [Peter Spenler](https://github.com/peterspenler)
## Emby Contributors
# Emby Contributors
- [LukePulverenti](https://github.com/LukePulverenti)
- [ebr11](https://github.com/ebr11)
- [lalmanzar](https://github.com/lalmanzar)
- [schneifu](https://github.com/schneifu)
- [Mark2xv](https://github.com/Mark2xv)
- [ScottRapsey](https://github.com/ScottRapsey)
- [skynet600](https://github.com/skynet600)
- [Cheesegeezer](https://githum.com/Cheesegeezer)
- [Radeon](https://github.com/radeonorama)
- [gcw07](https://github.com/gcw07)
- [SivaramAdhiappan](https://github.com/shivaram1190)
- [CWatkinsNash](https://github.com/CWatkinsNash)
- [sfnetwork](https://github.com/sfnetwork)
- [Logos302](https://github.com/Logos302)
- [TheWorkz](https://github.com/TheWorkz)
- [mboehler](https://github.com/mboehler)
- [KaHooli](https://github.com/KaHooli)
- [xzener](https://github.com/xzener)
- [CBers](https://github.com/CBers)
- [Sagaia](https://github.com/Sagaia)
- [JHawk111](https://github.com/JHawk111)
- [David3663](https://github.com/david3663)
- [Smyken](https://github.com/Smyken)
- [doron1](https://github.com/doron1)
- [brainfryd](https://github.com/brainfryd)
- [DGMayor](http://github.com/DGMayor)
- [Jon-theHTPC](https://github.com/Jon-theHTPC)
- [aspdend](https://github.com/aspdend)
- [RedshirtMB](https://github.com/RedshirtMB)
- [thealienamongus](https://github.com/thealienamongus)
- [brocass](https://github.com/brocass)
- [pjrollo2000](https://github.com/pjrollo2000)
- [abobader](https://github.com/abobader)
- [milli260876](https://github.com/milli260876)
- [vileboy](https://github.com/vileboy)
- [starkadius](https://github.com/starkadius)
- [wraslor](https://github.com/wraslor)
- [mrwebsmith](https://github.com/mrwebsmith)
- [rickster53](https://github.com/rickster53)
- [Tharnax](https://github.com/Tharnax)
- [0sm0](https://github.com/0sm0)
- [swhitmore](https://github.com/swhitmore)
- [DigiTM](https://github.com/DigiTM)
- [crisliv / xliv](https://github.com/crisliv)
- [Yogi](https://github.com/yogi12)
- [madFloyd](https://github.com/madFloyd)
- [yardameus](https://github.com/yardameus)
- [rrb008](https://github.com/rrb008)
- [Toonguy](https://github.com/Toonguy)
- [Alwin Hummels](https://github.com/AlwinHummels)
- [trooper11](https://github.com/trooper11)
- [danlotfy](https://github.com/danlotfy)
- [jordy1955](https://github.com/jordy1955)
- [JoshFink](https://github.com/JoshFink)
- [Detector1](https://github.com/Detector1)
- [BlackIce013](https://github.com/blackice013)
- [mporcas](https://github.com/mporcas)
- [tikuf](https://github.com/tikuf/)
- [Tim Hobbs](https://github.com/timhobbs)
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
<!--
NOTE: This is the end of the list of past Emby Contributors.
New Jellyfin contributors should add their name to the end
of the list of Jellyfin Contributors above. NOT HERE ;)
-->
- [LukePulverenti](https://github.com/LukePulverenti)
- [ebr11](https://github.com/ebr11)
- [lalmanzar](https://github.com/lalmanzar)
- [schneifu](https://github.com/schneifu)
- [Mark2xv](https://github.com/Mark2xv)
- [ScottRapsey](https://github.com/ScottRapsey)
- [skynet600](https://github.com/skynet600)
- [Cheesegeezer](https://githum.com/Cheesegeezer)
- [Radeon](https://github.com/radeonorama)
- [gcw07](https://github.com/gcw07)
- [SivaramAdhiappan](https://github.com/shivaram1190)
- [CWatkinsNash](https://github.com/CWatkinsNash)
- [sfnetwork](https://github.com/sfnetwork)
- [Logos302](https://github.com/Logos302)
- [TheWorkz](https://github.com/TheWorkz)
- [mboehler](https://github.com/mboehler)
- [KaHooli](https://github.com/KaHooli)
- [xzener](https://github.com/xzener)
- [CBers](https://github.com/CBers)
- [Sagaia](https://github.com/Sagaia)
- [JHawk111](https://github.com/JHawk111)
- [David3663](https://github.com/david3663)
- [Smyken](https://github.com/Smyken)
- [doron1](https://github.com/doron1)
- [brainfryd](https://github.com/brainfryd)
- [DGMayor](http://github.com/DGMayor)
- [Jon-theHTPC](https://github.com/Jon-theHTPC)
- [aspdend](https://github.com/aspdend)
- [RedshirtMB](https://github.com/RedshirtMB)
- [thealienamongus](https://github.com/thealienamongus)
- [brocass](https://github.com/brocass)
- [pjrollo2000](https://github.com/pjrollo2000)
- [abobader](https://github.com/abobader)
- [milli260876](https://github.com/milli260876)
- [vileboy](https://github.com/vileboy)
- [starkadius](https://github.com/starkadius)
- [wraslor](https://github.com/wraslor)
- [mrwebsmith](https://github.com/mrwebsmith)
- [rickster53](https://github.com/rickster53)
- [Tharnax](https://github.com/Tharnax)
- [0sm0](https://github.com/0sm0)
- [swhitmore](https://github.com/swhitmore)
- [DigiTM](https://github.com/DigiTM)
- [crisliv / xliv](https://github.com/crisliv)
- [Yogi](https://github.com/yogi12)
- [madFloyd](https://github.com/madFloyd)
- [yardameus](https://github.com/yardameus)
- [rrb008](https://github.com/rrb008)
- [Toonguy](https://github.com/Toonguy)
- [Alwin Hummels](https://github.com/AlwinHummels)
- [trooper11](https://github.com/trooper11)
- [danlotfy](https://github.com/danlotfy)
- [jordy1955](https://github.com/jordy1955)
- [JoshFink](https://github.com/JoshFink)
- [Detector1](https://github.com/Detector1)
- [BlackIce013](https://github.com/blackice013)
- [mporcas](https://github.com/mporcas)
- [tikuf](https://github.com/tikuf/)
- [Tim Hobbs](https://github.com/timhobbs)
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
- [jomp16](https://github.com/jomp16)
- [Leon de Klerk](https://github.com/leondeklerk)
- [CrispyBaguette](https://github.com/CrispyBaguette)

View File

@@ -70,42 +70,3 @@ Jellyfin Web is the frontend used for most of the clients available for end user
```sh
npm run build:development
```
## Directory Structure
> [!NOTE]
> We are in the process of refactoring to a [new structure](https://forum.jellyfin.org/t-proposed-update-to-the-structure-of-jellyfin-web) based on [Bulletproof React](https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md) architecture guidelines.
> Most new code should be organized under the appropriate app directory unless it is common/shared.
```
.
└── src
├── apps
│   ├── dashboard # Admin dashboard app
│   ├── experimental # New experimental app
│   ├── stable # Classic (stable) app
│   └── wizard # Startup wizard app
├── assets # Static assets
├── components # Higher order visual components and React components
├── constants # Common constant values
├── controllers # Legacy page views and controllers 🧹 ❌
├── elements # Basic webcomponents and React equivalents 🧹
├── hooks # Custom React hooks
├── lib # Reusable libraries
│   ├── globalize # Custom localization library
│   ├── jellyfin-apiclient # Supporting code for the deprecated apiclient package
│   ├── legacy # Polyfills for legacy browsers
│   ├── navdrawer # Navigation drawer library for classic layout
│   └── scroller # Content scrolling library
├── plugins # Client plugins (features dynamically loaded at runtime)
├── scripts # Random assortment of visual components and utilities 🐉 ❌
├── strings # Translation files (only commit changes to en-us.json)
├── styles # Common app Sass stylesheets
├── themes # Sass and MUI themes
├── types # Common TypeScript interfaces/types
└── utils # Utility functions
```
- ❌ &mdash; Deprecated, do **not** create new files here
- 🧹 &mdash; Needs cleanup
- 🐉 &mdash; Serious mess (Here be dragons)

View File

@@ -12,8 +12,18 @@ module.exports = {
corejs: 3
}
],
'@babel/preset-react'
'@babel/preset-react',
[
'@babel/preset-typescript',
{
isTSX: true,
allExtensions: true
}
]
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-private-methods',
'babel-plugin-dynamic-import-polyfill'
]
};

110
build.sh Executable file
View File

@@ -0,0 +1,110 @@
#!/usr/bin/env bash
# build.sh - Build Jellyfin binary packages
# Part of the Jellyfin Project
set -o errexit
set -o pipefail
usage() {
echo -e "build.sh - Build Jellyfin binary packages"
echo -e "Usage:"
echo -e " $0 -t/--type <BUILD_TYPE> -p/--platform <PLATFORM> [-k/--keep-artifacts] [-l/--list-platforms]"
echo -e "Notes:"
echo -e " * BUILD_TYPE can be one of: [native, docker] and must be specified"
echo -e " * native: Build using the build script in the host OS"
echo -e " * docker: Build using the build script in a standardized Docker container"
echo -e " * PLATFORM can be any platform shown by -l/--list-platforms and must be specified"
echo -e " * If -k/--keep-artifacts is specified, transient artifacts (e.g. Docker containers) will be"
echo -e " retained after the build is finished; the source directory will still be cleaned"
echo -e " * If -l/--list-platforms is specified, all other arguments are ignored; the script will print"
echo -e " the list of supported platforms and exit"
}
list_platforms() {
declare -a platforms
platforms=(
$( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; }; if ($4 != ""){ printf "." $4; }; print ""; }' | sort )
)
echo -e "Valid platforms:"
echo
for platform in ${platforms[@]}; do
echo -e "* ${platform} : $( grep '^#=' deployment/build.${platform} | sed 's/^#= //' )"
done
}
do_build_native() {
export IS_DOCKER=NO
deployment/build.${PLATFORM}
}
do_build_docker() {
if ! [ $(uname -m) = "x86_64" ]; then
echo "Docker-based builds only support amd64-based cross-building; use a 'native' build instead."
exit 1
fi
if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then
echo "Missing Dockerfile for platform ${PLATFORM}"
exit 1
fi
if [[ ${KEEP_ARTIFACTS} == YES ]]; then
docker_args=""
else
docker_args="--rm"
fi
docker build . -t "jellyfin-builder.${PLATFORM}" -f deployment/Dockerfile.${PLATFORM}
mkdir -p ${ARTIFACT_DIR}
docker run $docker_args -v "${SOURCE_DIR}:/jellyfin" -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}"
}
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-t|--type)
BUILD_TYPE="$2"
shift
shift
;;
-p|--platform)
PLATFORM="$2"
shift
shift
;;
-k|--keep-artifacts)
KEEP_ARTIFACTS=YES
shift
;;
-l|--list-platforms)
list_platforms
exit 0
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option $1"
usage
exit 1
;;
esac
done
if [[ -z ${BUILD_TYPE} || -z ${PLATFORM} ]]; then
usage
exit 1
fi
export SOURCE_DIR="$( pwd )"
export ARTIFACT_DIR="${SOURCE_DIR}/../bin/${PLATFORM}"
# Determine build type
case ${BUILD_TYPE} in
native)
do_build_native
;;
docker)
do_build_docker
;;
esac

9
build.yaml Normal file
View File

@@ -0,0 +1,9 @@
---
# We just wrap `build` so this is really it
name: "jellyfin-web"
version: "10.8.13"
packages:
- debian.all
- fedora.all
- centos.all
- portable

View File

@@ -7,7 +7,7 @@ set -o pipefail
set -o xtrace
usage() {
echo -e "bump_version - increase the shared version"
echo -e "bump_version - increase the shared version and generate changelogs"
echo -e ""
echo -e "Usage:"
echo -e " $ bump_version <new_version>"
@@ -18,12 +18,75 @@ if [[ -z $1 ]]; then
exit 1
fi
build_file="./build.yaml"
package_file="./package*.json"
new_version="$1"
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
old_version="$(
grep "version:" ${build_file} \
| sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/'
)"
echo "Old version: ${old_version}"
# Bump the NPM version
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
npm --no-git-tag-version --allow-same-version version v${new_version_sed}
# Set the build.yaml version to the specified new_version
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
sed -i "s/${old_version_sed}/${new_version_sed}/g" ${build_file}
if [[ ${new_version} == *"-"* ]]; then
new_version_pkg="$( sed 's/-/~/g' <<<"${new_version}" )"
new_version_deb_sup=""
else
new_version_pkg="${new_version}"
new_version_deb_sup="-1"
fi
# Write out a temporary Debian changelog with our new stuff appended and some templated formatting
debian_changelog_file="debian/changelog"
debian_changelog_temp="$( mktemp )"
# Create new temp file with our changelog
echo -e "jellyfin-web (${new_version_pkg}${new_version_deb_sup}) unstable; urgency=medium
* New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
" >> ${debian_changelog_temp}
cat ${debian_changelog_file} >> ${debian_changelog_temp}
# Move into place
mv ${debian_changelog_temp} ${debian_changelog_file}
# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting
fedora_spec_file="fedora/jellyfin-web.spec"
fedora_changelog_temp="$( mktemp )"
fedora_spec_temp_dir="$( mktemp -d )"
fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin-web.spec.tmp"
# Make a copy of our spec file for hacking
cp ${fedora_spec_file} ${fedora_spec_temp_dir}/
pushd ${fedora_spec_temp_dir}
# Split out the stuff before and after changelog
csplit jellyfin-web.spec "/^%changelog/" # produces xx00 xx01
# Update the version in xx00
sed -i "s/${old_version_sed}/${new_version_pkg}/g" xx00
# Remove the header from xx01
sed -i '/^%changelog/d' xx01
# Create new temp file with our changelog
echo -e "%changelog
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}" >> ${fedora_changelog_temp}
cat xx01 >> ${fedora_changelog_temp}
# Reassembble
cat xx00 ${fedora_changelog_temp} > ${fedora_spec_temp}
popd
# Move into place
mv ${fedora_spec_temp} ${fedora_spec_file}
# Clean up
rm -rf ${fedora_spec_temp_dir}
# Stage the changed files for commit
git add .
git status -v

View File

@@ -1,10 +0,0 @@
module.exports = {
preset: [
'default',
// Turn off `mergeLonghand` because it combines `padding-*` and `margin-*`,
// breaking fallback styles.
// https://github.com/cssnano/cssnano/issues/1163
// https://github.com/cssnano/cssnano/issues/1192
{ mergeLonghand: false }
]
};

95
debian/changelog vendored Normal file
View File

@@ -0,0 +1,95 @@
jellyfin-web (10.8.13-1) unstable; urgency=medium
* New upstream version 10.8.13; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.13
-- Jellyfin Packaging Team <packaging@jellyfin.org> Tue, 28 Nov 2023 22:21:29 -0500
jellyfin-web (10.8.12-1) unstable; urgency=medium
* New upstream version 10.8.12; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.12
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 04 Nov 2023 14:42:41 -0400
jellyfin-web (10.8.11-1) unstable; urgency=medium
* New upstream version 10.8.11; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.11
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 23 Sep 2023 21:41:40 -0400
jellyfin-web (10.8.10-1) unstable; urgency=medium
* New upstream version 10.8.10; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.10
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 23 Apr 2023 11:01:33 -0400
jellyfin-web (10.8.9-1) unstable; urgency=medium
* New upstream version 10.8.9; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.9
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 22 Jan 2023 14:09:13 -0500
jellyfin-web (10.8.8-1) unstable; urgency=medium
* New upstream version 10.8.8; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.8
-- Jellyfin Packaging Team <packaging@jellyfin.org> Tue, 29 Nov 2022 13:42:54 -0500
jellyfin-web (10.8.7-1) unstable; urgency=medium
* New upstream version 10.8.7; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.7
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 31 Oct 2022 23:06:34 -0400
jellyfin-web (10.8.6-1) unstable; urgency=medium
* New upstream version 10.8.6; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.6
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 28 Oct 2022 22:44:15 -0400
jellyfin-web (10.8.5-1) unstable; urgency=medium
* New upstream version 10.8.5; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.5
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 24 Sep 2022 22:02:26 -0400
jellyfin-web (10.8.4-1) unstable; urgency=medium
* New upstream version 10.8.4; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.4
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sat, 13 Aug 2022 21:52:04 -0400
jellyfin-web (10.8.3-1) unstable; urgency=medium
* New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.3
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 01 Aug 2022 20:22:00 -0400
jellyfin-web (10.8.2-1) unstable; urgency=medium
* New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.2
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 01 Aug 2022 14:27:56 -0400
jellyfin-web (10.8.1-1) unstable; urgency=medium
* New upstream version 10.8.1; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.1
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 26 Jun 2022 20:59:39 -0400
jellyfin-web (10.8.0-1) unstable; urgency=medium
* New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.0
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 10 Jun 2022 22:16:40 -0400
jellyfin-web (10.7.0-1) unstable; urgency=medium
* New upstream version 10.7.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.7.0
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 27 Jul 2020 19:13:31 -0400
jellyfin-web (10.6.0-1) unstable; urgency=medium
* New upstream version 10.6.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.0
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 16 Mar 2020 11:15:00 -0400

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
8

1
debian/conffiles vendored Normal file
View File

@@ -0,0 +1 @@
/usr/share/jellyfin/web/config.json

16
debian/control vendored Normal file
View File

@@ -0,0 +1,16 @@
Source: jellyfin-web
Section: misc
Priority: optional
Maintainer: Jellyfin Team <team@jellyfin.org>
Build-Depends: debhelper (>= 9),
npm | nodejs
Standards-Version: 3.9.4
Homepage: https://jellyfin.org/
Vcs-Git: https://github.org/jellyfin/jellyfin-web.git
Vcs-Browser: https://github.org/jellyfin/jellyfin-web
Package: jellyfin-web
Recommends: jellyfin-server
Architecture: all
Description: Jellyfin is the Free Software Media System.
This package provides the Jellyfin web client.

28
debian/copyright vendored Normal file
View File

@@ -0,0 +1,28 @@
Format: http://dep.debian.net/deps/dep5
Upstream-Name: jellyfin-web
Source: https://github.com/jellyfin/jellyfin-web
Files: *
Copyright: 2018-2020 Jellyfin Team
License: GPL-3.0
Files: debian/*
Copyright: 2020 Joshua Boniface <joshua@boniface.me>
License: GPL-3.0
License: GPL-3.0
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".

6
debian/gbp.conf vendored Normal file
View File

@@ -0,0 +1,6 @@
[DEFAULT]
pristine-tar = False
cleaner = fakeroot debian/rules clean
[import-orig]
filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ]

1
debian/install vendored Normal file
View File

@@ -0,0 +1 @@
web usr/share/jellyfin/

1
debian/po/POTFILES.in vendored Normal file
View File

@@ -0,0 +1 @@
[type: gettext/rfc822deb] templates

57
debian/po/templates.pot vendored Normal file
View File

@@ -0,0 +1,57 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: jellyfin-server\n"
"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n"
"POT-Creation-Date: 2015-06-12 20:51-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#. Type: note
#. Description
#: ../templates:1001
msgid "Jellyfin permission info:"
msgstr ""
#. Type: note
#. Description
#: ../templates:1001
msgid ""
"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the "
"user jellyfin has read and write access to any folders you wish to add to your "
"library. Otherwise please run jellyfin under a different user."
msgstr ""
#. Type: string
#. Description
#: ../templates:2001
msgid "Username to run Jellyfin as:"
msgstr ""
#. Type: string
#. Description
#: ../templates:2001
msgid "The user that jellyfin will run as."
msgstr ""
#. Type: note
#. Description
#: ../templates:3001
msgid "Jellyfin still running"
msgstr ""
#. Type: note
#. Description
#: ../templates:3001
msgid "Jellyfin is currently running. Please close it and try again."
msgstr ""

20
debian/rules vendored Executable file
View File

@@ -0,0 +1,20 @@
#! /usr/bin/make -f
export DH_VERBOSE=1
%:
dh $@
# disable "make check"
override_dh_auto_test:
# disable stripping debugging symbols
override_dh_clistrip:
override_dh_auto_build:
npm ci --no-audit --unsafe-perm
mv $(CURDIR)/dist $(CURDIR)/web
override_dh_auto_clean:
test -d $(CURDIR)/dist && rm -rf '$(CURDIR)/dist' || true
test -d $(CURDIR)/web && rm -rf '$(CURDIR)/web' || true
test -d $(CURDIR)/node_modules && rm -rf '$(CURDIR)/node_modules' || true

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
1.0

7
debian/source/options vendored Normal file
View File

@@ -0,0 +1,7 @@
tar-ignore='.git*'
tar-ignore='**/.git'
tar-ignore='**/.hg'
tar-ignore='**/.vs'
tar-ignore='**/.vscode'
tar-ignore='deployment'
tar-ignore='*.deb'

View File

@@ -0,0 +1,26 @@
FROM centos:7
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
ENV IS_DOCKER=YES
# Prepare CentOS environment
RUN yum update -y \
&& yum install -y epel-release \
&& yum install -y @buildsys-build rpmdevtools git yum-plugins-core autoconf automake glibc-devel gcc-c++ make \
&& curl -fsSL https://rpm.nodesource.com/setup_12.x | bash - \
&& yum install -y nodejs
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos /build.sh
VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"]

View File

@@ -0,0 +1,27 @@
FROM debian:10
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
ENV DEB_BUILD_OPTIONS=noddebs
ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update \
&& apt-get install -y debhelper mmv git curl \
&& curl -fsSL https://deb.nodesource.com/setup_12.x | bash - \
&& apt-get install -y nodejs
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian /build.sh
VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"]

View File

@@ -0,0 +1,11 @@
FROM node:lts-alpine
ARG SOURCE_DIR=/src
ARG ARTIFACT_DIR=/jellyfin-web
RUN apk add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3
WORKDIR ${SOURCE_DIR}
COPY . .
RUN npm ci --no-audit --unsafe-perm && mv dist ${ARTIFACT_DIR}

View File

@@ -0,0 +1,23 @@
FROM fedora:36
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
ENV IS_DOCKER=YES
# Prepare Fedora environment
RUN dnf update -y \
&& dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs autoconf automake glibc-devel make
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora /build.sh
VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"]

View File

@@ -0,0 +1,25 @@
FROM debian:10
# Docker build arguments
ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist
# Docker run environment
ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist
ENV IS_DOCKER=YES
# Prepare Debian build environment
RUN apt-get update \
&& apt-get install -y mmv curl git \
&& curl -fsSL https://deb.nodesource.com/setup_12.x | bash - \
&& apt-get install -y nodejs
# Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh
VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"]

41
deployment/build.centos Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a package-lock.json /tmp/package-lock.json
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd fedora
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
sed -i "/%changelog/q" jellyfin-web.spec
cat <<EOF >>jellyfin-web.spec
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
EOF
popd
fi
# build rpm
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# move the artifacts
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/package-lock.json package-lock.json
popd

39
deployment/build.debian Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a package-lock.json /tmp/package-lock.json
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd debian
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
cat <<EOF >changelog
jellyfin-web (${BUILD_ID}-unstable) unstable; urgency=medium
* Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
EOF
popd
fi
# build deb
dpkg-buildpackage -us -uc --pre-clean --post-clean
mkdir -p ${ARTIFACT_DIR}
mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}
cp -a /tmp/package-lock.json package-lock.json
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
popd

41
deployment/build.fedora Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a package-lock.json /tmp/package-lock.json
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd fedora
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
sed -i "/%changelog/q" jellyfin-web.spec
cat <<EOF >>jellyfin-web.spec
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
EOF
popd
fi
# build rpm
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# move the artifacts
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/package-lock.json package-lock.json
popd

30
deployment/build.portable Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
# get version
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
version="${BUILD_ID}"
else
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
fi
# build archives
npm ci --no-audit --unsafe-perm
mv dist jellyfin-web_${version}
tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version}
rm -rf dist
# move the artifacts
mkdir -p ${ARTIFACT_DIR}
mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
popd

View File

@@ -1,417 +0,0 @@
// @ts-check
import eslint from '@eslint/js';
import comments from '@eslint-community/eslint-plugin-eslint-comments/configs';
import compat from 'eslint-plugin-compat';
import globals from 'globals';
// @ts-expect-error Missing type definition
import importPlugin from 'eslint-plugin-import';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import reactPlugin from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import restrictedGlobals from 'confusing-browser-globals';
import sonarjs from 'eslint-plugin-sonarjs';
import stylistic from '@stylistic/eslint-plugin';
// eslint-disable-next-line import/no-unresolved
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.recommended,
// @ts-expect-error Harmless type mismatch in dependency
comments.recommended,
compat.configs['flat/recommended'],
importPlugin.flatConfigs.errors,
sonarjs.configs.recommended,
reactPlugin.configs.flat.recommended,
{
settings: {
react: {
version: 'detect'
}
}
},
jsxA11y.flatConfigs.recommended,
// Global ignores
{
ignores: [
'node_modules',
'coverage',
'dist',
'.idea',
'.vscode'
]
},
// Global style rules
{
plugins: {
'@stylistic': stylistic
},
extends: [ importPlugin.flatConfigs.typescript ],
rules: {
'array-callback-return': ['error', { 'checkForEach': true }],
'curly': ['error', 'multi-line', 'consistent'],
'default-case-last': 'error',
'max-params': ['error', 7],
'new-cap': [
'error',
{
'capIsNewExceptions': ['jQuery.Deferred'],
'newIsCapExceptionPattern': '\\.default$'
}
],
'no-duplicate-imports': 'error',
'no-empty-function': 'error',
'no-extend-native': 'error',
'no-lonely-if': 'error',
'no-nested-ternary': 'error',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': ['error', { builtinGlobals: false }],
'no-restricted-globals': ['error'].concat(restrictedGlobals),
'no-return-assign': 'error',
'no-return-await': 'error',
'no-sequences': ['error', { 'allowInParentheses': false }],
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-throw-literal': 'error',
'no-undef-init': 'error',
'no-unneeded-ternary': 'error',
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
'no-unused-private-class-members': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-useless-rename': 'error',
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'error',
'no-var': 'error',
'no-void': ['error', { 'allowAsStatement': true }],
'no-warning-comments': ['warn', { 'terms': ['hack', 'xxx'] }],
'one-var': ['error', 'never'],
'prefer-const': ['error', { 'destructuring': 'all' }],
'prefer-promise-reject-errors': ['warn', { 'allowEmptyReject': true }],
'@typescript-eslint/prefer-for-of': 'error',
'radix': 'error',
'yoda': 'error',
'sonarjs/fixme-tag': 'warn',
'sonarjs/todo-tag': 'off',
'sonarjs/deprecation': 'off',
'sonarjs/no-alphabetical-sort': 'warn',
'sonarjs/no-inverted-boolean-check': 'error',
'sonarjs/no-selector-parameter': 'off',
'sonarjs/pseudo-random': 'warn',
// TODO: Enable the following sonarjs rules and fix issues
'sonarjs/no-duplicate-string': 'off',
'sonarjs/no-nested-functions': 'warn',
// TODO: Replace with stylistic.configs.customize()
'@stylistic/block-spacing': 'error',
'@stylistic/brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
'@stylistic/comma-dangle': ['error', 'never'],
'@stylistic/comma-spacing': 'error',
'@stylistic/eol-last': 'error',
'@stylistic/indent': ['error', 4, { 'SwitchCase': 1 }],
'@stylistic/jsx-quotes': ['error', 'prefer-single'],
'@stylistic/keyword-spacing': 'error',
'@stylistic/max-statements-per-line': 'error',
'@stylistic/no-floating-decimal': 'error',
'@stylistic/no-mixed-spaces-and-tabs': 'error',
'@stylistic/no-multi-spaces': 'error',
'@stylistic/no-multiple-empty-lines': ['error', { 'max': 1 }],
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/object-curly-spacing': ['error', 'always'],
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '?': 'after', ':': 'after', '=': 'after' } }],
'@stylistic/padded-blocks': ['error', 'never'],
'@stylistic/quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
'@stylistic/semi': 'error',
'@stylistic/space-before-blocks': 'error',
'@stylistic/space-infix-ops': 'error',
'@typescript-eslint/no-restricted-imports': [
'error',
{
paths: [
{
name: '@jellyfin/sdk/lib/generated-client',
message: 'Use direct file imports for tree-shaking',
allowTypeImports: true
},
{
name: '@jellyfin/sdk/lib/generated-client/api',
message: 'Use direct file imports for tree-shaking',
allowTypeImports: true
},
{
name: '@jellyfin/sdk/lib/generated-client/models',
message: 'Use direct file imports for tree-shaking',
allowTypeImports: true
},
{
name: '@mui/icons-material',
message: 'Use direct file imports for tree-shaking',
allowTypeImports: true
},
{
name: '@mui/material',
message: 'Use direct file imports for tree-shaking',
allowTypeImports: true
}
]
}
]
}
},
// Config files use node globals
{
ignores: [ 'src' ],
languageOptions: {
globals: {
...globals.node
}
}
},
// Config files are commonjs by default
{
files: [ '**/*.{cjs,js}' ],
ignores: [ 'src' ],
languageOptions: {
sourceType: 'commonjs'
},
rules: {
'@typescript-eslint/no-require-imports': 'off'
}
},
// App files
{
files: [
'src/**/*.{js,jsx,ts,tsx}'
],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
},
globals: {
...globals.browser,
// Tizen globals
'tizen': false,
'webapis': false,
// WebOS globals
'webOS': false,
// Dependency globals
'$': false,
'jQuery': false,
// Jellyfin globals
'ApiClient': true,
'Events': true,
'chrome': true,
'Emby': false,
'Hls': true,
'LibraryMenu': true,
'Windows': false,
// Build time definitions
__COMMIT_SHA__: false,
__JF_BUILD_VERSION__: false,
__PACKAGE_JSON_NAME__: false,
__PACKAGE_JSON_VERSION__: false,
__USE_SYSTEM_FONTS__: false,
__WEBPACK_SERVE__: false
}
},
settings: {
'import/resolver': {
node: {
extensions: [
'.js',
'.ts',
'.jsx',
'.tsx'
],
moduleDirectory: [
'node_modules',
'src'
]
}
},
polyfills: [
'Promise',
// whatwg-fetch
'fetch',
// document-register-element
'document.registerElement',
// resize-observer-polyfill
'ResizeObserver',
// fast-text-encoding
'TextEncoder',
// intersection-observer
'IntersectionObserver',
// Core-js
'Object.assign',
'Object.is',
'Object.setPrototypeOf',
'Object.toString',
'Object.freeze',
'Object.seal',
'Object.preventExtensions',
'Object.isFrozen',
'Object.isSealed',
'Object.isExtensible',
'Object.getOwnPropertyDescriptor',
'Object.getPrototypeOf',
'Object.keys',
'Object.entries',
'Object.getOwnPropertyNames',
'Function.name',
'Function.hasInstance',
'Array.from',
'Array.arrayOf',
'Array.copyWithin',
'Array.fill',
'Array.find',
'Array.findIndex',
'Array.iterator',
'String.fromCodePoint',
'String.raw',
'String.iterator',
'String.codePointAt',
'String.endsWith',
'String.includes',
'String.repeat',
'String.startsWith',
'String.trim',
'String.anchor',
'String.big',
'String.blink',
'String.bold',
'String.fixed',
'String.fontcolor',
'String.fontsize',
'String.italics',
'String.link',
'String.small',
'String.strike',
'String.sub',
'String.sup',
'RegExp',
'Number',
'Math',
'Date',
'async',
'Symbol',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'DataView',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'Reflect'
]
},
rules: {
// TODO: Add typescript recommended typed rules
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: [ 'camelCase', 'PascalCase' ],
leadingUnderscore: 'allow'
},
{
selector: 'variable',
format: [ 'camelCase', 'PascalCase', 'UPPER_CASE' ],
leadingUnderscore: 'allowSingleOrDouble',
trailingUnderscore: 'allowSingleOrDouble'
},
{
selector: 'typeLike',
format: [ 'PascalCase' ]
},
{
selector: 'enumMember',
format: [ 'PascalCase', 'UPPER_CASE' ]
},
{
selector: [ 'objectLiteralProperty', 'typeProperty' ],
format: [ 'camelCase', 'PascalCase' ],
leadingUnderscore: 'allowSingleOrDouble',
trailingUnderscore: 'allowSingleOrDouble'
},
// Ignore numbers, locale strings (en-us), aria/data attributes and CSS selectors
{
selector: [ 'objectLiteralProperty', 'typeProperty' ],
format: null,
filter: {
regex: '[ &\\-]|^([0-9]+)$',
match: true
}
}
],
'@typescript-eslint/no-deprecated': 'warn',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/prefer-string-starts-ends-with': 'error'
}
},
// React files
{
files: [ 'src/**/*.{jsx,tsx}' ],
plugins: {
'react-hooks': reactHooks
},
rules: {
'react/jsx-filename-extension': ['error', { 'extensions': ['.jsx', '.tsx'] }],
'react/jsx-no-bind': 'error',
'react/jsx-no-useless-fragment': 'error',
'react/no-array-index-key': 'error',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}
},
// Service worker
{
files: [ 'src/serviceworker.js' ],
languageOptions: {
globals: {
...globals.serviceworker
}
}
},
// Legacy JS (less strict)
{
files: [ 'src/**/*.{js,jsx}' ],
rules: {
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-this-alias': 'off',
'sonarjs/public-static-readonly': 'off',
// TODO: Enable the following rules and fix issues
'sonarjs/cognitive-complexity': 'off',
'sonarjs/constructor-for-side-effects': 'off',
'sonarjs/function-return-type': 'off',
'sonarjs/no-async-constructor': 'off',
'sonarjs/no-duplicate-string': 'off',
'sonarjs/no-ignored-exceptions': 'off',
'sonarjs/no-invariant-returns': 'warn',
'sonarjs/no-nested-functions': 'off',
'sonarjs/void-use': 'off'
}
}
);

48
fedora/Makefile Normal file
View File

@@ -0,0 +1,48 @@
DIR := $(dir $(lastword $(MAKEFILE_LIST)))
# install git and npm
$(info $(shell set -x; if [ "$$(id -u)" = "0" ]; then echo "Installing git"; dnf -y install git npm; fi))
NAME := jellyfin-web
VERSION := $(shell set -x; sed -ne '/^Version:/s/.* *//p' $(DIR)/$(NAME).spec)
RELEASE := $(shell set -x; sed -ne '/^Release:/s/.* *\(.*\)%{.*}.*/\1/p' $(DIR)/$(NAME).spec)
SRPM := jellyfin-web-$(subst -,~,$(VERSION))-$(RELEASE)$(shell rpm --eval %dist).src.rpm
TARBALL :=$(NAME)-$(subst -,~,$(VERSION)).tar.gz
epel-7-x86_64_repos := https://rpm.nodesource.com/pub_16.x/el/\$$releasever/\$$basearch/
fed_ver := $(shell rpm -E %fedora)
# fallback when not running on Fedora
fed_ver ?= 36
TARGET ?= fedora-$(fed_ver)-x86_64
outdir ?= $(PWD)/$(DIR)/
srpm: $(DIR)/$(SRPM)
tarball: $(DIR)/$(TARBALL)
$(DIR)/$(TARBALL):
cd $(DIR)/; \
SOURCE_DIR=.. \
WORKDIR="$${PWD}"; \
version=$(VERSION); \
tar \
--transform "s,^\.,$(NAME)-$(subst -,~,$(VERSION))," \
--exclude='.git*' \
--exclude='**/.git' \
--exclude='**/.hg' \
--exclude=deployment \
--exclude='*.deb' \
--exclude='*.rpm' \
--exclude=$(notdir $@) \
-czf $(notdir $@) \
-C $${SOURCE_DIR} ./
$(DIR)/$(SRPM): $(DIR)/$(TARBALL) $(DIR)/jellyfin-web.spec
cd $(DIR)/; \
rpmbuild -bs $(NAME).spec \
--define "_sourcedir $$PWD/" \
--define "_srcrpmdir $(outdir)"
rpms: $(DIR)/$(SRPM)
mock $(addprefix --addrepo=, $($(TARGET)_repos)) \
--enable-network \
-r $(TARGET) $<

76
fedora/jellyfin-web.spec Normal file
View File

@@ -0,0 +1,76 @@
%global debug_package %{nil}
Name: jellyfin-web
Version: 10.8.13
Release: 1%{?dist}
Summary: The Free Software Media System web client
License: GPLv2
URL: https://jellyfin.org
# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%%{version}.tar.gz`
Source0: jellyfin-web-%{version}.tar.gz
BuildArch: noarch
%if 0%{?rhel} > 0 && 0%{?rhel} < 8
BuildRequires: nodejs
%else
BuildRequires: git
BuildRequires: npm
%endif
%description
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
%prep
%autosetup -n jellyfin-web-%{version} -b 0
%if 0%{?rhel} > 0 && 0%{?rhel} < 8
# Required for CentOS build
chown root:root -R .
%endif
%build
npm ci --no-audit --unsafe-perm
%install
%{__mkdir} -p %{buildroot}%{_libdir}/jellyfin/jellyfin-web
%{__cp} -r dist/* %{buildroot}%{_libdir}/jellyfin/jellyfin-web
%files
%defattr(644,root,root,755)
%{_libdir}/jellyfin/jellyfin-web
%license LICENSE
%changelog
* Tue Nov 28 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.13; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.13
* Sat Nov 04 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.12; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.12
* Sat Sep 23 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.11; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.11
* Sun Apr 23 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.10; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.10
* Sun Jan 22 2023 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.9; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.9
* Tue Nov 29 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.8; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.8
* Mon Oct 31 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.7; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.7
* Fri Oct 28 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.6; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.6
* Sat Sep 24 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.5; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.5
* Sat Aug 13 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.4; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.4
* Mon Aug 01 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.3
* Mon Aug 01 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.2
* Sun Jun 26 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.1; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.1
* Fri Jun 10 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.8.0

60
flake.lock generated
View File

@@ -1,60 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739874174,
"narHash": "sha256-XGxSVtojlwjYRYGvGXex0Cw+/363EVJlbY9TPX9bARk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d2ab2691c798f6b633be91d74b1626980ddaff30",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,34 +0,0 @@
{
description = "jellyfin-web nix flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs {
inherit system;
};
in {
devShell = with pkgs;
mkShell rec {
buildInputs = [
nodejs_20
];
shellHook = ''
# Also see: https://github.com/sass/embedded-host-node/issues/334
echo "Removing sass-embedded from node-modules as its broken on NixOS."
rm -rf node_modules/sass-embedded*
'';
};
}
);
}

41296
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,140 +1,109 @@
{
"name": "jellyfin-web",
"version": "10.11.0",
"version": "10.8.13",
"description": "Web interface for Jellyfin",
"repository": "https://github.com/jellyfin/jellyfin-web",
"license": "GPL-2.0-or-later",
"devDependencies": {
"@babel/core": "7.27.4",
"@babel/plugin-transform-modules-umd": "7.27.1",
"@babel/preset-env": "7.27.2",
"@babel/preset-react": "7.27.1",
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
"@eslint/js": "9.30.1",
"@stylistic/eslint-plugin": "4.4.1",
"@stylistic/stylelint-plugin": "3.1.3",
"@types/dompurify": "3.0.5",
"@types/escape-html": "1.0.4",
"@types/loadable__component": "5.13.9",
"@types/lodash-es": "4.17.12",
"@types/markdown-it": "14.1.2",
"@types/react": "18.3.23",
"@types/react-dom": "18.3.7",
"@types/react-lazy-load-image-component": "1.6.4",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/parser": "8.35.1",
"@babel/core": "7.17.7",
"@babel/eslint-parser": "7.17.0",
"@babel/eslint-plugin": "7.17.7",
"@babel/plugin-proposal-class-properties": "7.16.7",
"@babel/plugin-proposal-private-methods": "7.16.11",
"@babel/plugin-transform-modules-umd": "7.16.7",
"@babel/preset-env": "7.16.11",
"@babel/preset-react": "7.16.7",
"@babel/preset-typescript": "7.16.7",
"@thornbill/jellyfin-sdk": "0.4.1",
"@types/escape-html": "1.0.1",
"@types/lodash-es": "4.17.6",
"@types/react": "17.0.40",
"@types/react-dom": "17.0.13",
"@typescript-eslint/eslint-plugin": "5.15.0",
"@typescript-eslint/parser": "5.15.0",
"@uupaa/dynamic-import-polyfill": "1.0.2",
"@vitest/coverage-v8": "3.2.4",
"autoprefixer": "10.4.21",
"babel-loader": "10.0.0",
"autoprefixer": "10.4.4",
"babel-loader": "8.2.3",
"babel-plugin-dynamic-import-polyfill": "1.0.0",
"clean-webpack-plugin": "4.0.0",
"confusing-browser-globals": "1.0.11",
"copy-webpack-plugin": "13.0.0",
"cross-env": "7.0.3",
"css-loader": "7.1.2",
"cssnano": "7.0.7",
"es-check": "9.1.4",
"eslint": "9.30.1",
"eslint-plugin-compat": "6.0.2",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-sonarjs": "3.0.4",
"expose-loader": "5.0.1",
"fast-glob": "3.3.3",
"fork-ts-checker-webpack-plugin": "9.1.0",
"globals": "16.2.0",
"html-loader": "5.1.0",
"html-webpack-plugin": "5.6.3",
"jsdom": "26.1.0",
"mini-css-extract-plugin": "2.9.2",
"postcss": "8.5.6",
"postcss-loader": "8.1.1",
"postcss-preset-env": "10.2.3",
"postcss-scss": "4.0.9",
"sass": "1.89.2",
"sass-loader": "16.0.5",
"source-map-loader": "5.0.0",
"speed-measure-webpack-plugin": "1.5.0",
"style-loader": "4.0.0",
"stylelint": "16.21.0",
"copy-webpack-plugin": "10.2.4",
"css-loader": "6.7.1",
"cssnano": "5.1.4",
"eslint": "8.11.0",
"eslint-plugin-compat": "4.0.2",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-jsx-a11y": "6.5.1",
"eslint-plugin-promise": "6.0.0",
"eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "4.3.0",
"expose-loader": "3.1.0",
"html-loader": "3.1.0",
"html-webpack-plugin": "5.5.0",
"postcss": "8.4.12",
"postcss-loader": "6.2.1",
"postcss-preset-env": "7.4.2",
"postcss-scss": "4.0.3",
"sass": "1.49.9",
"sass-loader": "12.6.0",
"source-map-loader": "3.0.1",
"style-loader": "3.3.1",
"stylelint": "14.6.0",
"stylelint-config-rational-order": "0.1.2",
"stylelint-no-browser-hacks": "2.0.0",
"stylelint-order": "7.0.0",
"stylelint-scss": "6.12.1",
"ts-loader": "9.5.2",
"typescript": "5.8.3",
"typescript-eslint": "8.35.1",
"vitest": "3.2.4",
"webpack": "5.99.9",
"webpack-bundle-analyzer": "4.10.2",
"webpack-cli": "6.0.1",
"webpack-dev-server": "5.2.2",
"webpack-merge": "6.0.1",
"stylelint-no-browser-hacks": "1.2.1",
"stylelint-order": "5.0.0",
"stylelint-scss": "4.2.0",
"ts-loader": "9.2.8",
"typescript": "4.6.2",
"webpack": "5.70.0",
"webpack-cli": "4.9.2",
"webpack-dev-server": "4.7.4",
"webpack-merge": "5.8.0",
"workbox-webpack-plugin": "6.5.1",
"worker-loader": "3.0.8"
},
"dependencies": {
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0",
"@fontsource/noto-sans": "5.2.7",
"@fontsource/noto-sans-hk": "5.2.6",
"@fontsource/noto-sans-jp": "5.2.6",
"@fontsource/noto-sans-kr": "5.2.6",
"@fontsource/noto-sans-sc": "5.2.6",
"@fontsource/noto-sans-tc": "5.2.6",
"@jellyfin/libass-wasm": "4.2.3",
"@jellyfin/sdk": "0.0.0-unstable.202508300501",
"@jellyfin/ux-web": "1.0.0",
"@mui/icons-material": "6.4.12",
"@mui/material": "6.4.12",
"@mui/x-date-pickers": "7.29.4",
"@react-hook/resize-observer": "2.0.2",
"@tanstack/react-query": "5.80.10",
"@tanstack/react-query-devtools": "5.80.10",
"abortcontroller-polyfill": "1.7.8",
"blurhash": "2.0.5",
"@fontsource/noto-sans": "4.5.1",
"@fontsource/noto-sans-hk": "4.5.2",
"@fontsource/noto-sans-jp": "4.5.2",
"@fontsource/noto-sans-kr": "4.5.2",
"@fontsource/noto-sans-sc": "4.5.2",
"@fontsource/noto-sans-tc": "4.5.2",
"@jellyfin/libass-wasm": "4.1.1",
"blurhash": "1.1.4",
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
"classnames": "2.5.1",
"core-js": "3.43.0",
"date-fns": "2.30.0",
"dompurify": "2.5.8",
"element-closest-polyfill": "1.0.7",
"classnames": "2.3.1",
"core-js": "3.20.2",
"date-fns": "2.28.0",
"dompurify": "2.3.4",
"epubjs": "0.3.93",
"escape-html": "1.0.3",
"fast-text-encoding": "1.0.6",
"fast-text-encoding": "1.0.3",
"flv.js": "1.6.2",
"headroom.js": "0.12.0",
"history": "5.3.0",
"hls.js": "1.6.9",
"intersection-observer": "0.12.2",
"jellyfin-apiclient": "1.11.0",
"jquery": "3.7.1",
"jstree": "3.3.17",
"libarchive.js": "2.0.2",
"libpgs": "0.8.1",
"hls.js": "0.14.17",
"intersection-observer": "0.12.0",
"jellyfin-apiclient": "1.10.0",
"jquery": "3.6.0",
"jstree": "3.3.12",
"libarchive.js": "1.3.0",
"lodash-es": "4.17.21",
"markdown-it": "14.1.0",
"material-design-icons-iconfont": "6.7.0",
"material-react-table": "3.2.1",
"marked": "4.0.10",
"material-design-icons-iconfont": "6.1.1",
"native-promise-only": "0.8.1",
"pdfjs-dist": "3.11.174",
"proxy-polyfill": "0.3.2",
"react": "18.3.1",
"react-blurhash": "0.3.0",
"react-dom": "18.3.1",
"react-lazy-load-image-component": "1.6.3",
"react-router-dom": "6.30.1",
"page": "1.11.6",
"pdfjs-dist": "2.12.313",
"react": "17.0.2",
"react-dom": "17.0.2",
"resize-observer-polyfill": "1.5.1",
"screenfull": "6.0.2",
"sortablejs": "1.15.6",
"swiper": "11.2.8",
"usehooks-ts": "3.1.1",
"screenfull": "6.0.0",
"sortablejs": "1.14.0",
"swiper": "6.8.4",
"webcomponents.js": "0.7.24",
"whatwg-fetch": "3.6.20"
},
"optionalDependencies": {
"sass-embedded": "1.89.2"
"whatwg-fetch": "3.6.2",
"workbox-core": "6.2.4",
"workbox-precaching": "6.2.4"
},
"browserslist": [
"last 2 Firefox versions",
@@ -155,20 +124,15 @@
"scripts": {
"start": "npm run serve",
"serve": "webpack serve --config webpack.dev.js",
"build:analyze": "cross-env NODE_ENV=\"production\" webpack --config webpack.analyze.js",
"prepare": "node ./scripts/prepare.js",
"build:development": "webpack --config webpack.dev.js",
"build:production": "cross-env NODE_ENV=\"production\" webpack --config webpack.prod.js",
"build:check": "tsc --noEmit",
"build:es-check": "npm run build:production && npm run escheck",
"escheck": "es-check",
"lint": "eslint \"./\"",
"test": "vitest --watch=false --config vite.config.ts",
"test:watch": "vitest --config vite.config.ts",
"stylelint": "stylelint \"src/**/*.{css,scss}\""
"build:production": "webpack --config webpack.prod.js",
"lint": "eslint \"src/\"",
"stylelint": "npm run stylelint:css && npm run stylelint:scss",
"stylelint:css": "stylelint \"src/**/*.css\"",
"stylelint:scss": "stylelint --config=\".stylelintrc.scss.json\" \"src/**/*.scss\""
},
"engines": {
"node": ">=20.0.0",
"npm": ">=9.6.4",
"yarn": "YARN NO LONGER USED - use npm instead."
}
}

View File

@@ -7,8 +7,8 @@ const config = () => ({
plugins: [
// Explicitly specify browserslist to override ones from node_modules
// For example, Swiper has it in its package.json
postcssPresetEnv({ browsers: packageConfig.browserslist }),
autoprefixer({ overrideBrowserslist: packageConfig.browserslist }),
postcssPresetEnv({browsers: packageConfig.browserslist}),
autoprefixer({overrideBrowserslist: packageConfig.browserslist}),
cssnano()
]
});

12
scripts/prepare.js Executable file
View File

@@ -0,0 +1,12 @@
const { execSync } = require('child_process');
/**
* The npm `prepare` script needs to run a build to support installing
* a package from git repositories (this is dumb but a limitation of how
* npm behaves). We don't want to run these in CI though because
* building is slow so this script will skip the build when the
* `SKIP_PREPARE` environment variable has been set.
*/
if (!process.env.SKIP_PREPARE) {
execSync('webpack --config webpack.prod.js', { stdio: 'inherit' });
}

View File

@@ -1,31 +0,0 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react';
import { ApiProvider } from 'hooks/useApi';
import { UserSettingsProvider } from 'hooks/useUserSettings';
import { WebConfigProvider } from 'hooks/useWebConfig';
import browser from 'scripts/browser';
import { queryClient } from 'utils/query/queryClient';
import RootAppRouter from 'RootAppRouter';
const useReactQueryDevtools = window.Proxy // '@tanstack/query-devtools' requires 'Proxy', which cannot be polyfilled for legacy browsers
&& !browser.tv; // Don't use devtools on the TV as the navigation is weird
const RootApp = () => (
<QueryClientProvider client={queryClient}>
<ApiProvider>
<UserSettingsProvider>
<WebConfigProvider>
<RootAppRouter />
</WebConfigProvider>
</UserSettingsProvider>
</ApiProvider>
{useReactQueryDevtools && (
<ReactQueryDevtools initialIsOpen={false} />
)}
</QueryClientProvider>
);
export default RootApp;

View File

@@ -1,66 +0,0 @@
import { ThemeProvider } from '@mui/material/styles';
import React from 'react';
import {
RouterProvider,
createHashRouter,
Outlet,
useLocation
} from 'react-router-dom';
import { DASHBOARD_APP_PATHS, DASHBOARD_APP_ROUTES } from 'apps/dashboard/routes/routes';
import { EXPERIMENTAL_APP_ROUTES } from 'apps/experimental/routes/routes';
import { STABLE_APP_ROUTES } from 'apps/stable/routes/routes';
import { WIZARD_APP_ROUTES } from 'apps/wizard/routes/routes';
import AppHeader from 'components/AppHeader';
import Backdrop from 'components/Backdrop';
import BangRedirect from 'components/router/BangRedirect';
import { createRouterHistory } from 'components/router/routerHistory';
import appTheme from 'themes/themes';
const layoutMode = localStorage.getItem('layout');
const isExperimentalLayout = layoutMode === 'experimental';
const router = createHashRouter([
{
element: <RootAppLayout />,
children: [
...(isExperimentalLayout ? EXPERIMENTAL_APP_ROUTES : STABLE_APP_ROUTES),
...DASHBOARD_APP_ROUTES,
...WIZARD_APP_ROUTES,
{
path: '!/*',
Component: BangRedirect
}
]
}
]);
export const history = createRouterHistory(router);
export default function RootAppRouter() {
return <RouterProvider router={router} />;
}
/**
* Layout component that renders legacy components required on all pages.
* NOTE: The app will crash if these get removed from the DOM.
*/
function RootAppLayout() {
const location = useLocation();
const isNewLayoutPath = Object.values(DASHBOARD_APP_PATHS)
.some(path => location.pathname.startsWith(`/${path}`));
return (
<ThemeProvider
theme={appTheme}
defaultMode='dark'
// Disable mui's default saving to local storage
storageManager={null}
>
<Backdrop />
<AppHeader isHidden={isExperimentalLayout || isNewLayoutPath} />
<Outlet />
</ThemeProvider>
);
}

23
src/apiclient.d.ts vendored
View File

@@ -1,3 +1,4 @@
// TODO: Move to jellyfin-apiclient
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module 'jellyfin-apiclient' {
import type {
@@ -66,8 +67,7 @@ declare module 'jellyfin-apiclient' {
UserPolicy,
UtcTimeResponse,
VirtualFolderInfo
} from '@jellyfin/sdk/lib/generated-client';
import type { ConnectionState } from 'lib/jellyfin-apiclient';
} from '@thornbill/jellyfin-sdk/dist/generated-client';
class ApiClient {
constructor(serverAddress: string, appName: string, appVersion: string, deviceName: string, deviceId: string);
@@ -75,7 +75,6 @@ declare module 'jellyfin-apiclient' {
accessToken(): string;
addMediaPath(virtualFolderName: string, mediaPath: string, networkSharePath: string, refreshLibrary?: boolean): Promise<void>;
addVirtualFolder(name: string, type?: string, refreshLibrary?: boolean, libraryOptions?: any): Promise<void>;
ajax(request: any): Promise<any>;
appName(): string;
appVersion(): string;
authenticateUserByName(name: string, password: string): Promise<AuthenticationResult>;
@@ -117,7 +116,6 @@ declare module 'jellyfin-apiclient' {
getCountries(): Promise<CountryInfo[]>;
getCriticReviews(itemId: string, options?: any): Promise<BaseItemDtoQueryResult>;
getCultures(): Promise<CultureDto[]>;
getCurrentUser(cache?: boolean): Promise<UserDto>;
getCurrentUserId(): string;
getDateParamValue(date: Date): string;
getDefaultImageQuality(imageType: ImageType): number;
@@ -181,7 +179,7 @@ declare module 'jellyfin-apiclient' {
getPluginConfiguration(id: string): Promise<any>;
getPublicSystemInfo(): Promise<PublicSystemInfo>;
getPublicUsers(): Promise<UserDto[]>;
getQuickConnect(verb: string): Promise<void | boolean | number | QuickConnectResult | QuickConnectState>;
getQuickConnect(verb: string): Promise<void|boolean|number|QuickConnectResult|QuickConnectState>;
getReadySyncItems(deviceId: string): Promise<any>;
getRecordingFolders(userId: string): Promise<BaseItemDtoQueryResult>;
getRegistrationInfo(feature: string): Promise<any>;
@@ -268,7 +266,7 @@ declare module 'jellyfin-apiclient' {
sendWebSocketMessage(name: string, data: any): void;
serverAddress(val?: string): string;
serverId(): string;
serverVersion(): string;
serverVersion(): string
setAuthenticationInfo(accessKey?: string, userId?: string): void;
setRequestHeaders(headers: any): void;
setSystemInfo(info: SystemInfo): void;
@@ -307,28 +305,22 @@ declare module 'jellyfin-apiclient' {
class AppStore {
constructor();
getItem(name: string): string | null;
getItem(name: string): string|null;
removeItem(name: string): void;
setItem(name: string, value: string): void;
}
interface ConnectResponse {
ApiClient: ApiClient
Servers: any[]
State: ConnectionState
}
class ConnectionManager {
constructor(credentialProvider: Credentials, appName: string, appVersion: string, deviceName: string, deviceId: string, capabilities: ClientCapabilities);
addApiClient(apiClient: ApiClient): void;
clearData(): void;
connect(options?: any): Promise<ConnectResponse>;
connect(options?: any): Promise<any>;
connectToAddress(address: string, options?: any): Promise<any>;
connectToServer(server: any, options?: any): Promise<any>;
connectToServers(servers: any[], options?: any): Promise<any>;
deleteServer(serverId: string): Promise<void>;
getApiClient(item: BaseItemDto | string): ApiClient;
getApiClient(item: BaseItemDto|string): ApiClient;
getApiClients(): ApiClient[];
getAvailableServers(): any[];
getOrCreateApiClient(serverId: string): ApiClient;
@@ -336,7 +328,6 @@ declare module 'jellyfin-apiclient' {
handleMessageReceived(msg: any): void;
logout(): Promise<void>;
minServerVersion(val?: string): string;
updateSavedServerId(server: any): Promise<void>;
user(apiClient: ApiClient): Promise<any>;
}

View File

@@ -1,113 +0,0 @@
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import { type Theme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import React, { FC, StrictMode, useCallback, useEffect, useState } from 'react';
import { Outlet, useLocation } from 'react-router-dom';
import AppBody from 'components/AppBody';
import AppToolbar from 'components/toolbar/AppToolbar';
import ServerButton from 'components/toolbar/ServerButton';
import ElevationScroll from 'components/ElevationScroll';
import { DRAWER_WIDTH } from 'components/ResponsiveDrawer';
import { appRouter } from 'components/router/appRouter';
import ThemeCss from 'components/ThemeCss';
import { useApi } from 'hooks/useApi';
import { useLocale } from 'hooks/useLocale';
import AppTabs from './components/AppTabs';
import AppDrawer from './components/drawer/AppDrawer';
import HelpButton from './components/toolbar/HelpButton';
import { DASHBOARD_APP_PATHS } from './routes/routes';
import './AppOverrides.scss';
export const Component: FC = () => {
const [ isDrawerActive, setIsDrawerActive ] = useState(false);
const location = useLocation();
const { user } = useApi();
const { dateFnsLocale } = useLocale();
const isMediumScreen = useMediaQuery((t: Theme) => t.breakpoints.up('md'));
const isMetadataManager = location.pathname.startsWith(`/${DASHBOARD_APP_PATHS.MetadataManager}`);
const isDrawerAvailable = Boolean(user) && !isMetadataManager;
const isDrawerOpen = isDrawerActive && isDrawerAvailable;
const onToggleDrawer = useCallback(() => {
setIsDrawerActive(!isDrawerActive);
}, [ isDrawerActive, setIsDrawerActive ]);
// Update body class
useEffect(() => {
document.body.classList.add('dashboardDocument');
return () => {
document.body.classList.remove('dashboardDocument');
};
}, []);
return (
<LocalizationProvider dateAdapter={AdapterDateFns} adapterLocale={dateFnsLocale}>
<Box sx={{ display: 'flex' }}>
<StrictMode>
<ElevationScroll elevate={false}>
<AppBar
position='fixed'
sx={{
width: {
xs: '100%',
md: isDrawerAvailable ? `calc(100% - ${DRAWER_WIDTH}px)` : '100%'
},
ml: {
xs: 0,
md: isDrawerAvailable ? DRAWER_WIDTH : 0
}
}}
>
<AppToolbar
isBackButtonAvailable={appRouter.canGoBack()}
isDrawerAvailable={!isMediumScreen && isDrawerAvailable}
isDrawerOpen={isDrawerOpen}
onDrawerButtonClick={onToggleDrawer}
buttons={
<HelpButton />
}
>
{isMetadataManager && (
<ServerButton />
)}
<AppTabs isDrawerOpen={isDrawerOpen} />
</AppToolbar>
</AppBar>
</ElevationScroll>
{
isDrawerAvailable && (
<AppDrawer
open={isDrawerOpen}
onClose={onToggleDrawer}
onOpen={onToggleDrawer}
/>
)
}
</StrictMode>
<Box
component='main'
sx={{
width: '100%',
flexGrow: 1
}}
>
<AppBody>
<Outlet />
</AppBody>
</Box>
</Box>
<ThemeCss dashboard />
</LocalizationProvider>
);
};

View File

@@ -1,38 +0,0 @@
// Default MUI breakpoints
// https://mui.com/material-ui/customization/breakpoints/#default-breakpoints
$mui-bp-sm: 600px;
$mui-bp-md: 900px;
$mui-bp-lg: 1200px;
$mui-bp-xl: 1536px;
$drawer-width: 240px;
// Fix dashboard pages layout to work with drawer
.dashboardDocument {
.mainAnimatedPage:not(.metadataEditorPage) {
@media all and (min-width: $mui-bp-md) {
left: $drawer-width;
}
}
.skinBody {
position: unset !important;
}
// Fix the padding of dashboard pages
.content-primary {
padding-top: 3.25rem;
}
// Tabbed pages
.withTabs .content-primary {
padding-top: 6.5rem;
@media all and (min-width: $mui-bp-lg) {
padding-top: 3.25rem;
}
}
.metadataEditorPage {
padding-top: 3.25rem !important;
}
}

View File

@@ -1,96 +0,0 @@
import { Theme } from '@mui/material/styles';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import useMediaQuery from '@mui/material/useMediaQuery';
import debounce from 'lodash-es/debounce';
import isEqual from 'lodash-es/isEqual';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Link } from 'react-router-dom';
import { EventType } from 'types/eventType';
import Events, { type Event } from 'utils/events';
interface AppTabsParams {
isDrawerOpen: boolean
}
interface TabDefinition {
href: string
name: string
}
const handleResize = debounce(() => window.dispatchEvent(new Event('resize')), 100);
const AppTabs: FC<AppTabsParams> = ({
isDrawerOpen
}) => {
const documentRef = useRef<Document>(document);
const [ activeIndex, setActiveIndex ] = useState(0);
const [ tabs, setTabs ] = useState<TabDefinition[]>();
const isBigScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('sm'));
const onTabsUpdate = useCallback((
_e: Event,
_newView?: string,
newIndex: number | undefined = 0,
newTabs?: TabDefinition[]
) => {
setActiveIndex(newIndex);
if (!isEqual(tabs, newTabs)) {
setTabs(newTabs);
}
}, [ tabs ]);
useEffect(() => {
const doc = documentRef.current;
if (doc) Events.on(doc, EventType.SET_TABS, onTabsUpdate);
return () => {
if (doc) Events.off(doc, EventType.SET_TABS, onTabsUpdate);
};
}, [ onTabsUpdate ]);
// HACK: Force resizing to workaround upstream bug with tab resizing
// https://github.com/mui/material-ui/issues/24011
useEffect(() => {
handleResize();
}, [ isDrawerOpen ]);
if (!tabs?.length) return null;
return (
<Tabs
value={activeIndex}
sx={{
width: '100%',
flexShrink: {
xs: 0,
lg: 'unset'
},
order: {
xs: 100,
lg: 'unset'
}
}}
variant={isBigScreen ? 'standard' : 'scrollable'}
centered={isBigScreen}
>
{
tabs.map(({ href, name }, index) => (
<Tab
key={`tab-${name}`}
label={name}
data-tab-index={`${index}`}
component={Link}
to={href}
/>
))
}
</Tabs>
);
};
export default AppTabs;

View File

@@ -1,93 +0,0 @@
import React from 'react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardHeader from '@mui/material/CardHeader';
import CardMedia from '@mui/material/CardMedia';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { getDefaultBackgroundClass } from 'components/cardbuilder/cardBuilderUtils';
import CardActionArea from '@mui/material/CardActionArea';
import Stack from '@mui/material/Stack';
import { Link, To } from 'react-router-dom';
interface BaseCardProps {
title?: string;
secondaryTitle?: string;
text?: string;
image?: string | null;
icon?: React.ReactNode;
to?: To;
onClick?: () => void;
action?: boolean;
actionRef?: React.MutableRefObject<HTMLButtonElement | null>;
onActionClick?: () => void;
};
const BaseCard = ({ title, secondaryTitle, text, image, icon, to, onClick, action, actionRef, onActionClick }: BaseCardProps) => {
return (
<Card
sx={{
display: 'flex',
flexDirection: 'column',
height: 240
}}
>
<CardActionArea
{...(to && {
component: Link,
to: to
})}
onClick={onClick}
sx={{
display: 'flex',
flexGrow: 1,
alignItems: 'stretch'
}}
>
{image ? (
<CardMedia
sx={{ flexGrow: 1 }}
image={image}
title={title}
/>
) : (
<Box className={getDefaultBackgroundClass(title)} sx={{
flexGrow: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
{icon}
</Box>
)}
</CardActionArea>
<CardHeader
title={
<Stack direction='row' spacing={1} alignItems='center'>
<Typography sx={{
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis'
}}>
{title}
</Typography>
{secondaryTitle && (
<Typography variant='body2' color='text.secondary'>{secondaryTitle}</Typography>
)}
</Stack>
}
subheader={text}
action={
action ? (
<IconButton ref={actionRef} onClick={onActionClick}>
<MoreVertIcon />
</IconButton>
) : null
}
/>
</Card>
);
};
export default BaseCard;

View File

@@ -1,70 +0,0 @@
import Search from '@mui/icons-material/Search';
import InputBase, { type InputBaseProps } from '@mui/material/InputBase';
import { alpha, styled } from '@mui/material/styles';
import React, { type FC } from 'react';
const SearchContainer = styled('div')(({ theme }) => ({
display: 'flex',
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25)
},
width: '100%',
[theme.breakpoints.up('sm')]: {
width: 'auto'
}
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
flexGrow: 1,
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('md')]: {
width: '20ch'
}
}
}));
interface SearchInputProps extends InputBaseProps {
label?: string
}
const SearchInput: FC<SearchInputProps> = ({
label,
...props
}) => {
return (
<SearchContainer>
<SearchIconWrapper>
<Search />
</SearchIconWrapper>
<StyledInputBase
placeholder={label}
inputProps={{
'aria-label': label,
...props.inputProps
}}
{...props}
/>
</SearchContainer>
);
};
export default SearchInput;

View File

@@ -1,35 +0,0 @@
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
import type { SxProps, Theme } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import React, { type FC } from 'react';
import { Link } from 'react-router-dom';
import UserAvatar from 'components/UserAvatar';
interface UserAvatarButtonProps {
user?: UserDto
sx?: SxProps<Theme>
}
const UserAvatarButton: FC<UserAvatarButtonProps> = ({
user,
sx
}) => (
user?.Id ? (
<IconButton
size='large'
color='inherit'
sx={{
padding: 0,
...sx
}}
title={user.Name || undefined}
component={Link}
to={`/dashboard/users/profile?userId=${user.Id}`}
>
<UserAvatar user={user} />
</IconButton>
) : undefined
);
export default UserAvatarButton;

View File

@@ -1,37 +0,0 @@
import ListItem from '@mui/material/ListItem';
import List from '@mui/material/List';
import React, { FC } from 'react';
import DrawerHeaderLink from 'apps/experimental/components/drawers/DrawerHeaderLink';
import ResponsiveDrawer, { ResponsiveDrawerProps } from 'components/ResponsiveDrawer';
import ServerDrawerSection from './sections/ServerDrawerSection';
import DevicesDrawerSection from './sections/DevicesDrawerSection';
import LiveTvDrawerSection from './sections/LiveTvDrawerSection';
import AdvancedDrawerSection from './sections/AdvancedDrawerSection';
import PluginDrawerSection from './sections/PluginDrawerSection';
const AppDrawer: FC<ResponsiveDrawerProps> = ({
open = false,
onClose,
onOpen
}) => (
<ResponsiveDrawer
open={open}
onClose={onClose}
onOpen={onOpen}
>
<List disablePadding>
<ListItem disablePadding>
<DrawerHeaderLink />
</ListItem>
</List>
<ServerDrawerSection />
<DevicesDrawerSection />
<LiveTvDrawerSection />
<PluginDrawerSection />
<AdvancedDrawerSection />
</ResponsiveDrawer>
);
export default AppDrawer;

View File

@@ -1,70 +0,0 @@
import Article from '@mui/icons-material/Article';
import Backup from '@mui/icons-material/Backup';
import Lan from '@mui/icons-material/Lan';
import Schedule from '@mui/icons-material/Schedule';
import VpnKey from '@mui/icons-material/VpnKey';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import ListItemLink from 'components/ListItemLink';
import globalize from 'lib/globalize';
const AdvancedDrawerSection = () => {
return (
<List
aria-labelledby='advanced-subheader'
subheader={
<ListSubheader component='div' id='advanced-subheader'>
{globalize.translate('TabAdvanced')}
</ListSubheader>
}
>
<ListItem disablePadding>
<ListItemLink to='/dashboard/networking'>
<ListItemIcon>
<Lan />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabNetworking')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/keys'>
<ListItemIcon>
<VpnKey />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderApiKeys')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/backups'>
<ListItemIcon>
<Backup />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderBackups')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/logs'>
<ListItemIcon>
<Article />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabLogs')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/tasks'>
<ListItemIcon>
<Schedule />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabScheduledTasks')} />
</ListItemLink>
</ListItem>
</List>
);
};
export default AdvancedDrawerSection;

View File

@@ -1,43 +0,0 @@
import Analytics from '@mui/icons-material/Analytics';
import Devices from '@mui/icons-material/Devices';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import ListItemLink from 'components/ListItemLink';
import globalize from 'lib/globalize';
const DevicesDrawerSection = () => {
return (
<List
aria-labelledby='devices-subheader'
subheader={
<ListSubheader component='div' id='devices-subheader'>
{globalize.translate('HeaderDevices')}
</ListSubheader>
}
>
<ListItem disablePadding>
<ListItemLink to='/dashboard/devices'>
<ListItemIcon>
<Devices />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderDevices')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/activity'>
<ListItemIcon>
<Analytics />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderActivity')} />
</ListItemLink>
</ListItem>
</List>
);
};
export default DevicesDrawerSection;

View File

@@ -1,43 +0,0 @@
import Dvr from '@mui/icons-material/Dvr';
import LiveTv from '@mui/icons-material/LiveTv';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React from 'react';
import ListItemLink from 'components/ListItemLink';
import globalize from 'lib/globalize';
const LiveTvDrawerSection = () => {
return (
<List
aria-labelledby='livetv-subheader'
subheader={
<ListSubheader component='div' id='livetv-subheader'>
{globalize.translate('LiveTV')}
</ListSubheader>
}
>
<ListItem disablePadding>
<ListItemLink to='/dashboard/livetv'>
<ListItemIcon>
<LiveTv />
</ListItemIcon>
<ListItemText primary={globalize.translate('LiveTV')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/livetv/recordings'>
<ListItemIcon>
<Dvr />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderDVR')} />
</ListItemLink>
</ListItem>
</List>
);
};
export default LiveTvDrawerSection;

View File

@@ -1,63 +0,0 @@
import Extension from '@mui/icons-material/Extension';
import Folder from '@mui/icons-material/Folder';
import List from '@mui/material/List';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React, { useEffect } from 'react';
import ListItemLink from 'components/ListItemLink';
import globalize from 'lib/globalize';
import Dashboard from 'utils/dashboard';
import { useConfigurationPages } from 'apps/dashboard/features/plugins/api/useConfigurationPages';
const PluginDrawerSection = () => {
const {
data: pagesInfo,
error
} = useConfigurationPages({ enableInMainMenu: true });
useEffect(() => {
if (error) console.error('[PluginDrawerSection] unable to fetch plugin config pages', error);
}, [ error ]);
return (
<List
aria-labelledby='plugins-subheader'
subheader={
<ListSubheader component='div' id='plugins-subheader'>
{globalize.translate('TabPlugins')}
</ListSubheader>
}
>
<ListItemLink
to='/dashboard/plugins'
includePaths={[
'/configurationpage',
'/dashboard/plugins/repositories'
]}
excludePaths={pagesInfo?.map(p => `/${Dashboard.getPluginUrl(p.Name)}`)}
>
<ListItemIcon>
<Extension />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabPlugins')} />
</ListItemLink>
{pagesInfo?.map(pageInfo => (
<ListItemLink
key={pageInfo.PluginId}
to={`/${Dashboard.getPluginUrl(pageInfo.Name)}`}
>
<ListItemIcon>
{/* TODO: Support different icons? */}
<Folder />
</ListItemIcon>
<ListItemText primary={pageInfo.DisplayName} />
</ListItemLink>
))}
</List>
);
};
export default PluginDrawerSection;

View File

@@ -1,147 +0,0 @@
import Dashboard from '@mui/icons-material/Dashboard';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import LibraryAdd from '@mui/icons-material/LibraryAdd';
import Palette from '@mui/icons-material/Palette';
import People from '@mui/icons-material/People';
import PlayCircle from '@mui/icons-material/PlayCircle';
import Settings from '@mui/icons-material/Settings';
import Collapse from '@mui/material/Collapse';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import ListSubheader from '@mui/material/ListSubheader';
import React, { type MouseEvent, useCallback, useState } from 'react';
import { useLocation } from 'react-router-dom';
import ListItemLink from 'components/ListItemLink';
import globalize from 'lib/globalize';
const LIBRARY_PATHS = [
'/dashboard/libraries',
'/dashboard/libraries/display',
'/dashboard/libraries/metadata',
'/dashboard/libraries/nfo'
];
const PLAYBACK_PATHS = [
'/dashboard/playback/transcoding',
'/dashboard/playback/resume',
'/dashboard/playback/streaming',
'/dashboard/playback/trickplay'
];
const ServerDrawerSection = () => {
const location = useLocation();
const [ isLibrarySectionOpen, setIsLibrarySectionOpen ] = useState(LIBRARY_PATHS.includes(location.pathname));
const [ isPlaybackSectionOpen, setIsPlaybackSectionOpen ] = useState(PLAYBACK_PATHS.includes(location.pathname));
const onLibrarySectionClick = useCallback((e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setIsLibrarySectionOpen(isOpen => !isOpen);
}, []);
const onPlaybackSectionClick = useCallback((e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setIsPlaybackSectionOpen(isOpen => !isOpen);
}, []);
return (
<List
aria-labelledby='server-subheader'
subheader={
<ListSubheader component='div' id='server-subheader'>
{globalize.translate('TabServer')}
</ListSubheader>
}
>
<ListItem disablePadding>
<ListItemLink to='/dashboard'>
<ListItemIcon>
<Dashboard />
</ListItemIcon>
<ListItemText primary={globalize.translate('TabDashboard')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemLink to='/dashboard/settings'>
<ListItemIcon>
<Settings />
</ListItemIcon>
<ListItemText primary={globalize.translate('General')} />
</ListItemLink>
</ListItem>
<ListItemLink to='/dashboard/branding'>
<ListItemIcon>
<Palette />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderBranding')} />
</ListItemLink>
<ListItem disablePadding>
<ListItemLink to='/dashboard/users'>
<ListItemIcon>
<People />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderUsers')} />
</ListItemLink>
</ListItem>
<ListItem disablePadding>
<ListItemButton onClick={onLibrarySectionClick}>
<ListItemIcon>
<LibraryAdd />
</ListItemIcon>
<ListItemText primary={globalize.translate('HeaderLibraries')} />
{isLibrarySectionOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
</ListItem>
<Collapse in={isLibrarySectionOpen} timeout='auto' unmountOnExit>
<List component='div' disablePadding>
<ListItemLink to='/dashboard/libraries' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('HeaderLibraries')} />
</ListItemLink>
<ListItemLink to='/dashboard/libraries/display' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('Display')} />
</ListItemLink>
<ListItemLink to='/dashboard/libraries/metadata' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('LabelMetadata')} />
</ListItemLink>
<ListItemLink to='/dashboard/libraries/nfo' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('TabNfoSettings')} />
</ListItemLink>
</List>
</Collapse>
<ListItem disablePadding>
<ListItemButton onClick={onPlaybackSectionClick}>
<ListItemIcon>
<PlayCircle />
</ListItemIcon>
<ListItemText primary={globalize.translate('TitlePlayback')} />
{isPlaybackSectionOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
</ListItem>
<Collapse in={isPlaybackSectionOpen} timeout='auto' unmountOnExit>
<List component='div' disablePadding>
<ListItemLink to='/dashboard/playback/transcoding' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('Transcoding')} />
</ListItemLink>
<ListItemLink to='/dashboard/playback/resume' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('ButtonResume')} />
</ListItemLink>
<ListItemLink to='/dashboard/playback/streaming' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('TabStreaming')} />
</ListItemLink>
<ListItemLink to='/dashboard/playback/trickplay' sx={{ pl: 4 }}>
<ListItemText inset primary={globalize.translate('Trickplay')} />
</ListItemLink>
</List>
</Collapse>
</List>
);
};
export default ServerDrawerSection;

View File

@@ -1,17 +0,0 @@
import format from 'date-fns/format';
import type { MRT_Cell, MRT_RowData } from 'material-react-table';
import { FC } from 'react';
import { useLocale } from 'hooks/useLocale';
interface CellProps {
cell: MRT_Cell<MRT_RowData>
}
const DateTimeCell: FC<CellProps> = ({ cell }) => {
const { dateFnsLocale } = useLocale();
return format(cell.getValue<Date>(), 'Pp', { locale: dateFnsLocale });
};
export default DateTimeCell;

View File

@@ -1,72 +0,0 @@
import Box from '@mui/material/Box/Box';
import Stack from '@mui/material/Stack/Stack';
import Typography from '@mui/material/Typography/Typography';
import { type MRT_RowData, type MRT_TableInstance, MaterialReactTable } from 'material-react-table';
import React from 'react';
import Page, { type PageProps } from 'components/Page';
interface TablePageProps<T extends MRT_RowData> extends PageProps {
title: string
subtitle?: string
table: MRT_TableInstance<T>
}
export const DEFAULT_TABLE_OPTIONS = {
// Enable custom features
enableColumnPinning: true,
enableColumnResizing: true,
// Sticky header/footer
enableStickyFooter: true,
enableStickyHeader: true,
muiTableContainerProps: {
sx: {
maxHeight: 'calc(100% - 7rem)' // 2 x 3.5rem for header and footer
}
}
};
const TablePage = <T extends MRT_RowData>({
title,
subtitle,
table,
children,
...pageProps
}: TablePageProps<T>) => {
return (
<Page
title={title}
{...pageProps}
>
<Box
className='content-primary'
sx={{
display: 'flex',
flexDirection: 'column',
height: '100%'
}}
>
<Stack
spacing={2}
sx={{
marginBottom: 1
}}
>
<Typography variant='h1'>
{title}
</Typography>
{subtitle && (
<Typography>
{subtitle}
</Typography>
)}
</Stack>
<MaterialReactTable table={table} />
</Box>
{children}
</Page>
);
};
export default TablePage;

View File

@@ -1,36 +0,0 @@
import HelpOutline from '@mui/icons-material/HelpOutline';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip/Tooltip';
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import { HelpLinks } from 'apps/dashboard/constants/helpLinks';
import globalize from 'lib/globalize';
const HelpButton = () => (
<Routes>
{
HelpLinks.map(({ paths, url }) => paths.map(path => (
<Route
key={[url, path].join('-')}
path={path}
element={
<Tooltip title={globalize.translate('Help')}>
<IconButton
href={url}
rel='noopener noreferrer'
target='_blank'
size='large'
color='inherit'
>
<HelpOutline />
</IconButton>
</Tooltip>
}
/>
))).flat()
}
</Routes>
);
export default HelpButton;

View File

@@ -1,50 +0,0 @@
import React, { useMemo } from 'react';
import globalize from 'lib/globalize';
import Widget from './Widget';
import List from '@mui/material/List';
import ActivityListItem from 'apps/dashboard/features/activity/components/ActivityListItem';
import { useLogEntries } from 'apps/dashboard/features/activity/api/useLogEntries';
import subSeconds from 'date-fns/subSeconds';
import Skeleton from '@mui/material/Skeleton';
import Stack from '@mui/material/Stack';
const ActivityLogWidget = () => {
const dayBefore = useMemo(() => (
subSeconds(new Date(), 24 * 60 * 60).toISOString()
), []);
const { data: logs, isPending } = useLogEntries({
startIndex: 0,
limit: 7,
minDate: dayBefore,
hasUserId: true
});
return (
<Widget
title={globalize.translate('HeaderActivity')}
href='/dashboard/activity?useractivity=true'
>
{isPending ? (
<Stack spacing={2}>
<Skeleton variant='rounded' height={60} />
<Skeleton variant='rounded' height={60} />
<Skeleton variant='rounded' height={60} />
<Skeleton variant='rounded' height={60} />
</Stack>
) : (
<List sx={{ bgcolor: 'background.paper' }}>
{logs?.Items?.map(entry => (
<ActivityListItem
key={entry.Id}
item={entry}
displayShortOverview={true}
/>
))}
</List>
)}
</Widget>
);
};
export default ActivityLogWidget;

View File

@@ -1,41 +0,0 @@
import React, { useMemo } from 'react';
import globalize from 'lib/globalize';
import Widget from './Widget';
import List from '@mui/material/List';
import ActivityListItem from 'apps/dashboard/features/activity/components/ActivityListItem';
import subSeconds from 'date-fns/subSeconds';
import { useLogEntries } from 'apps/dashboard/features/activity/api/useLogEntries';
const AlertsLogWidget = () => {
const weekBefore = useMemo(() => (
subSeconds(new Date(), 7 * 24 * 60 * 60).toISOString()
), []);
const { data: alerts, isPending } = useLogEntries({
startIndex: 0,
limit: 4,
minDate: weekBefore,
hasUserId: false
});
if (isPending || alerts?.Items?.length === 0) return null;
return (
<Widget
title={globalize.translate('Alerts')}
href='/dashboard/activity?useractivity=false'
>
<List sx={{ bgcolor: 'background.paper' }}>
{alerts?.Items?.map(entry => (
<ActivityListItem
key={entry.Id}
item={entry}
displayShortOverview={false}
/>
))}
</List>
</Widget>
);
};
export default AlertsLogWidget;

View File

@@ -1,28 +0,0 @@
import React from 'react';
import globalize from 'lib/globalize';
import Widget from './Widget';
import DeviceCard from 'apps/dashboard/features/devices/components/DeviceCard';
import Stack from '@mui/material/Stack';
import useLiveSessions from 'apps/dashboard/features/sessions/hooks/useLiveSessions';
const DevicesWidget = () => {
const { data: devices } = useLiveSessions();
return (
<Widget
title={globalize.translate('HeaderDevices')}
href='/dashboard/devices'
>
<Stack direction='row' flexWrap='wrap' gap={2}>
{devices?.map(device => (
<DeviceCard
key={device.Id}
device={device}
/>
))}
</Stack>
</Widget>
);
};
export default DevicesWidget;

View File

@@ -1,98 +0,0 @@
import type { ItemCounts } from '@jellyfin/sdk/lib/generated-client/models/item-counts';
import Book from '@mui/icons-material/Book';
import Movie from '@mui/icons-material/Movie';
import MusicNote from '@mui/icons-material/MusicNote';
import MusicVideo from '@mui/icons-material/MusicVideo';
import Tv from '@mui/icons-material/Tv';
import VideoLibrary from '@mui/icons-material/VideoLibrary';
import Grid from '@mui/material/Grid2';
import SvgIcon from '@mui/material/SvgIcon';
import React, { useMemo } from 'react';
import { useItemCounts } from 'apps/dashboard/features/metrics/api/useItemCounts';
import MetricCard, { type MetricCardProps } from 'apps/dashboard/features/metrics/components/MetricCard';
import globalize from 'lib/globalize';
interface MetricDefinition {
key: keyof ItemCounts
i18n: string
}
interface CardDefinition {
Icon: typeof SvgIcon
metrics: MetricDefinition[]
}
const CARD_DEFINITIONS: CardDefinition[] = [
{
Icon: Movie,
metrics: [{ key: 'MovieCount', i18n: 'Movies' }]
}, {
Icon: Tv,
metrics: [
{ key: 'SeriesCount', i18n: 'Series' },
{ key: 'EpisodeCount', i18n: 'Episodes' }
]
}, {
Icon: MusicNote,
metrics: [
{ key: 'AlbumCount', i18n: 'Albums' },
{ key: 'SongCount', i18n: 'Songs' }
]
}, {
Icon: MusicVideo,
metrics: [{ key: 'MusicVideoCount', i18n: 'MusicVideos' }]
}, {
Icon: Book,
metrics: [{ key: 'BookCount', i18n: 'Books' }]
}, {
Icon: VideoLibrary,
metrics: [{ key: 'BoxSetCount', i18n: 'Collections' }]
}
];
const ItemCountsWidget = () => {
const {
data: counts,
isPending
} = useItemCounts();
const cards: MetricCardProps[] = useMemo(() => {
return CARD_DEFINITIONS
.filter(def => (
// Include all cards while the request is pending
isPending
// Check if the metrics are present in counts
|| def.metrics.some(({ key }) => counts?.[key])
))
.map(({ Icon, metrics }) => ({
Icon,
metrics: metrics.map(({ i18n, key }) => ({
label: globalize.translate(i18n),
value: counts?.[key]
}))
}));
}, [ counts, isPending ]);
return (
<Grid
container
spacing={2}
sx={{
alignItems: 'stretch',
marginTop: 2
}}
>
{cards.map(card => (
<Grid
key={card.metrics.map(metric => metric.label).join('-')}
size={{ xs: 12, sm: 6, lg: 4 }}
>
<MetricCard {...card} />
</Grid>
))}
</Grid>
);
};
export default ItemCountsWidget;

View File

@@ -1,42 +0,0 @@
import React, { useMemo } from 'react';
import globalize from 'lib/globalize';
import Widget from './Widget';
import type { TaskInfo } from '@jellyfin/sdk/lib/generated-client/models/task-info';
import Paper from '@mui/material/Paper';
import { TaskState } from '@jellyfin/sdk/lib/generated-client/models/task-state';
import Typography from '@mui/material/Typography';
import TaskProgress from 'apps/dashboard/features/tasks/components/TaskProgress';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
type RunningTasksWidgetProps = {
tasks?: TaskInfo[];
};
const RunningTasksWidget = ({ tasks }: RunningTasksWidgetProps) => {
const runningTasks = useMemo(() => {
return tasks?.filter(v => v.State == TaskState.Running) || [];
}, [ tasks ]);
if (runningTasks.length == 0) return null;
return (
<Widget
title={globalize.translate('HeaderRunningTasks')}
href='/dashboard/tasks'
>
<Paper sx={{ padding: 2 }}>
<Stack spacing={2} maxWidth={'330px'}>
{runningTasks.map((task => (
<Box key={task.Id}>
<Typography>{task.Name}</Typography>
<TaskProgress task={task} />
</Box>
)))}
</Stack>
</Paper>
</Widget>
);
};
export default RunningTasksWidget;

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