Compare commits

..

291 Commits

Author SHA1 Message Date
jeffvli 68b1134da5 Fix formatting and typos 2022-11-21 14:45:59 -08:00
jeffvli 46187acadc Fix typos 2022-11-21 14:42:00 -08:00
jeffvli 69c79f0620 Update README instructions 2022-11-21 14:40:30 -08:00
jeffvli 234c9546c4 Add server release scripts 2022-11-21 13:12:29 -08:00
jeffvli edc80bc3b4 Fix subsonic scanner 2022-11-21 11:27:39 -08:00
jeffvli 1d05966127 Add wait script to prod build 2022-11-21 11:23:47 -08:00
jeffvli 2013c46991 Schema fixes 2022-11-21 09:58:00 -08:00
jeffvli eb7e259c86 Prevent replacement of identical username fields 2022-11-21 09:57:32 -08:00
jeffvli 20cd34a38f Fix publish errors 2022-11-20 19:49:36 -08:00
jeffvli 154de4147c Bump retry version and remove shell 2022-11-20 19:39:41 -08:00
jeffvli 4215e0e1f1 Set workflow dispatch on publish 2022-11-20 19:36:20 -08:00
jeffvli b89fd340e7 Fix publish workflow 2022-11-20 19:33:19 -08:00
jeffvli 19792bc8d8 Update appid 2022-11-20 19:28:50 -08:00
jeffvli 118f416066 Split prod and public builds 2022-11-20 19:28:15 -08:00
jeffvli 693286a6a1 Enable publish 2022-11-20 19:21:13 -08:00
jeffvli c273dd9753 Set default repo settings for i18n and typescript 2022-11-20 17:11:26 -08:00
jeffvli 9c98f8aa01 Remove unused 2022-11-20 17:10:47 -08:00
jeffvli ad39919f3f Change default port 2022-11-20 17:10:25 -08:00
jeffvli 0a89e35a3e Fix credential required 2022-11-20 17:09:13 -08:00
jeffvli de1e6c16c0 Add logout to error fallback 2022-11-20 17:01:52 -08:00
jeffvli b0f031bedc Reject when connection failed 2022-11-20 17:01:18 -08:00
jeffvli 079a400e74 Optimize docker build 2022-11-20 16:07:54 -08:00
jeffvli 2cb54a068a Hide electron-only settings 2022-11-20 05:20:19 -08:00
jeffvli 44ffe1984b Increase default size of sidebar queue 2022-11-20 05:16:37 -08:00
jeffvli d5d0c29280 Move fonts to settings 2022-11-20 05:16:13 -08:00
jeffvli 96dc79a527 Remove unused mpv state set 2022-11-20 04:56:03 -08:00
jeffvli 9b2f9f6326 Align setting title to start 2022-11-20 04:46:47 -08:00
jeffvli 1c130e6c58 Add customizzable mpv parameters 2022-11-20 04:46:25 -08:00
jeffvli 01d327f9f1 Remove tab border 2022-11-20 04:45:25 -08:00
jeffvli 99f9e67500 Add Textarea and JsonInput components 2022-11-20 04:45:10 -08:00
jeffvli 892fa0e7b8 Reset default state on server change 2022-11-20 03:42:26 -08:00
jeffvli 9d7b595e41 Add shortcut for devtools in production build 2022-11-20 03:37:09 -08:00
jeffvli b1d06581ab Move login mutation 2022-11-20 02:03:21 -08:00
jeffvli 0cb2e40f2b Disable artist filters for subsonic 2022-11-20 02:02:05 -08:00
jeffvli 6f0c523559 Persist album list config 2022-11-20 01:53:20 -08:00
jeffvli b51a79c3cd Add scroll events to grid 2022-11-20 01:52:34 -08:00
jeffvli cf9187f548 Add sockets path 2022-11-19 20:51:19 -08:00
jeffvli 25f6c940bb Adjust default themes 2022-11-19 20:51:11 -08:00
jeffvli 449e11c2d4 Adjust object fit 2022-11-19 20:50:35 -08:00
jeffvli 9f4a58352d Use song type instead of server 2022-11-19 20:50:25 -08:00
jeffvli 4f3bd8f44a Scroll to row on grid ready 2022-11-19 20:50:12 -08:00
jeffvli 5811069a0a Add prop type 2022-11-19 20:49:47 -08:00
jeffvli 24b0d37bd1 Adjust grid card rows 2022-11-19 20:49:28 -08:00
jeffvli d66c756af5 Add playqueue table 2022-11-19 20:30:57 -08:00
jeffvli 4cb78bb656 Add multiselect component 2022-11-19 20:26:24 -08:00
jeffvli fe678b546e Update login styles 2022-11-17 02:51:58 -08:00
jeffvli 2a6167441a Add queue drawer 2022-11-17 01:42:53 -08:00
jeffvli b4825c35b5 Various fixes 2022-11-16 20:01:52 -08:00
jeffvli c89808fd14 Update socket api 2022-11-16 19:52:55 -08:00
jeffvli 04b45e26cd Bump packages 2022-11-16 19:52:42 -08:00
jeffvli 3827953b59 Update react-query implementation 2022-11-16 19:52:25 -08:00
jeffvli 82d4ad5502 Add empty placeholder 2022-11-16 10:42:29 -08:00
jeffvli b411ab13ac Prevent null value for skip 2022-11-15 15:54:18 -08:00
jeffvli 67e3a00b26 Base component fixes 2022-11-15 15:54:03 -08:00
jeffvli f40c3838a1 Add border for sidebar items 2022-11-15 15:53:42 -08:00
jeffvli da2ad0ac2e Animate activity song change 2022-11-15 15:53:27 -08:00
jeffvli bf5f464712 Adjust play status 2022-11-15 15:53:05 -08:00
jeffvli f83368874f Adjust skeleton height 2022-11-15 13:37:25 -08:00
jeffvli 2be651ed56 Set popover defaults 2022-11-15 13:36:56 -08:00
jeffvli d120057c8d Fix loading transient prop 2022-11-15 13:36:50 -08:00
jeffvli 99398d5675 Set player to web automatically if not electron 2022-11-15 13:36:35 -08:00
jeffvli f62aacf9d8 Remove unused util 2022-11-15 13:36:14 -08:00
jeffvli a8f0a20602 Export server map function 2022-11-15 13:35:42 -08:00
jeffvli f9c5e6f9fb Add user activity 2022-11-15 13:35:26 -08:00
jeffvli e76267be06 Fix spinner color on queue 2022-11-15 13:33:20 -08:00
jeffvli 2a29f33352 Remove required generic for query options 2022-11-15 13:33:04 -08:00
jeffvli 758e9d4d2c Add server map route 2022-11-15 13:31:11 -08:00
jeffvli 8d5a05c329 Update look and feel 2022-11-14 18:12:24 -08:00
jeffvli 8dad9a8109 Require link prop for styles 2022-11-14 14:25:50 -08:00
jeffvli 7061e16fb4 Add custom spinner to button 2022-11-14 14:25:38 -08:00
jeffvli 4029b149ee Update tab styles 2022-11-14 14:25:24 -08:00
jeffvli 5ee5a089c5 Add spinner component 2022-11-14 14:25:15 -08:00
jeffvli 9e6e8d7fd6 Re-enable navidrome scanner items 2022-11-14 14:24:54 -08:00
jeffvli 28cc1f9c30 Set max width for container 2022-11-14 03:03:58 -08:00
jeffvli 38993f3352 Reduce fade in duration 2022-11-14 03:03:49 -08:00
jeffvli 1bd538b6b4 Update text for link styles 2022-11-14 03:00:05 -08:00
jeffvli 295eb82f20 Adjust default themes 2022-11-14 02:59:51 -08:00
jeffvli 1cc40348cb Remove default staleTime for user detail 2022-11-14 01:15:49 -08:00
jeffvli 85e80be08b Fix overflow 2022-11-14 01:15:02 -08:00
jeffvli 420d4835be Update base components 2022-11-14 01:14:39 -08:00
jeffvli c54eea4382 Add server permission management 2022-11-14 01:13:54 -08:00
jeffvli 1babcc40ee Add user profile image 2022-11-13 20:18:23 -08:00
jeffvli 14c22c63a0 Add server permission routes 2022-11-13 12:35:26 -08:00
jeffvli 1a6c4af5df Fix album permissions by role 2022-11-13 04:56:36 -08:00
jeffvli 4e2325f05d Fix server folder permissions by role 2022-11-13 03:06:24 -08:00
jeffvli 135a8d7a45 Retrieve serverfolders by permission 2022-11-13 02:41:42 -08:00
jeffvli 74170185a2 Add max width to tooltip 2022-11-13 02:41:08 -08:00
jeffvli 3a8665fded Add info tooltips to server options 2022-11-13 02:34:55 -08:00
jeffvli d028af5ff0 Add mpv path option 2022-11-13 02:08:12 -08:00
jeffvli ab7787484c Move localSettings utility to different file 2022-11-13 02:08:02 -08:00
jeffvli c469e2e5cc Adjust paper styles 2022-11-13 02:07:43 -08:00
jeffvli 5de5b34ecf Add accordion component 2022-11-13 02:07:14 -08:00
jeffvli ec59d27c1d Fix password input styles 2022-11-13 01:58:56 -08:00
jeffvli 9a079ef263 Adjust placeholder input color 2022-11-13 01:54:18 -08:00
jeffvli 372fc7c1f6 Add edit profile form 2022-11-13 01:54:02 -08:00
jeffvli 1686005792 Change dropdown position 2022-11-13 01:53:43 -08:00
jeffvli b9c0b62cf8 Add link to mpv site 2022-11-13 01:53:16 -08:00
jeffvli 797e3eab3b Set maxWidth for required container 2022-11-13 01:53:08 -08:00
jeffvli b100f4f790 Add shadow to tooltip 2022-11-13 01:52:52 -08:00
jeffvli 6c5a70c03e Add styles to fileinput 2022-11-13 01:52:47 -08:00
jeffvli 353168ad4e Add light/dark type to theme 2022-11-13 01:52:39 -08:00
jeffvli a03f41b76d Bump mantine to v5.7.2 2022-11-12 18:45:49 -08:00
jeffvli 81fafd20d3 Web player fixes 2022-11-12 18:45:25 -08:00
jeffvli 8353640d05 Update various components 2022-11-12 18:44:51 -08:00
jeffvli 79fae63aaf Add theme selector / update defaults 2022-11-12 18:43:47 -08:00
jeffvli 544fd25f6b Add error boundary package 2022-11-12 12:39:23 -08:00
jeffvli 0e9953be13 Remove unused 2022-11-12 01:22:44 -08:00
jeffvli 258e5bb8f6 Init simple-img in main app 2022-11-12 01:22:29 -08:00
jeffvli 5c6cd2410e Set user profile in server state 2022-11-12 01:22:12 -08:00
jeffvli b08c7dfaf1 Set web volume to be logarithmic 2022-11-12 01:20:23 -08:00
jeffvli 46145fd3aa Adjust playerbar 2022-11-12 01:20:09 -08:00
jeffvli 0b5cea16e9 Handle shuffle/repeat playback for web 2022-11-12 01:19:17 -08:00
jeffvli 34f4458733 Add crossfade style setting 2022-11-12 01:08:20 -08:00
jeffvli d3d6db03d7 Add error boundary 2022-11-11 12:51:08 -08:00
jeffvli f7839d6ed6 Handle shuffle/repeat playback for mpv 2022-11-11 04:40:20 -08:00
jeffvli 581ef32845 Add per-server permissions 2022-11-09 01:52:08 -08:00
jeffvli 73e6002cc7 Fix toast types 2022-11-08 19:01:05 -08:00
jeffvli 048b5898b9 Set center loader 2022-11-08 19:01:00 -08:00
jeffvli ddc0355b1e Update forms 2022-11-08 19:00:48 -08:00
jeffvli 30a7bac59a Add prop to permissions 2022-11-08 19:00:22 -08:00
jeffvli 445e4b56b7 Add initial users manager 2022-11-08 18:46:56 -08:00
jeffvli df8e38cedd Add isSuperAdmin account 2022-11-08 18:40:44 -08:00
jeffvli 3bc0ea16bc Wrap queue handler in callback 2022-11-08 15:16:22 -08:00
jeffvli 84b031f126 Move add server form inside of server list 2022-11-08 15:16:05 -08:00
jeffvli 8357941bfa Add context modal 2022-11-08 15:13:59 -08:00
jeffvli 187ccad15b Add user routes 2022-11-08 15:13:47 -08:00
jeffvli a326355576 Add song skip/displayname to schema 2022-11-08 14:32:01 -08:00
jeffvli e6bf71dcfe Add state merge 2022-11-08 12:17:18 -08:00
jeffvli a964662ad4 Add albumartists to related album 2022-11-08 12:04:10 -08:00
jeffvli 2652ab927f Add playButtonBehavior type 2022-11-08 12:03:36 -08:00
jeffvli 30e9543544 Remove select on sidebar 2022-11-08 04:00:36 -08:00
jeffvli d6fe3038e0 Update server forms 2022-11-08 04:00:22 -08:00
jeffvli d3e4c7d975 Display album artist for subsonic servers 2022-11-08 03:58:52 -08:00
jeffvli 1df07dd12a Adjust component styles 2022-11-08 03:58:23 -08:00
jeffvli 49eb78b03c Add configurable web audio device 2022-11-08 03:58:13 -08:00
jeffvli 7b1940e1f5 Add configurable play button behavior 2022-11-08 03:57:56 -08:00
jeffvli b2cc85d368 Settings fixes 2022-11-08 03:56:59 -08:00
jeffvli dbba5f9f95 Fix skip forward 2022-11-08 03:55:37 -08:00
jeffvli 03278d2624 Fix action required for mpv 2022-11-08 01:36:57 -08:00
jeffvli 9022f05463 Move handlers to separate functions 2022-11-08 01:36:26 -08:00
jeffvli 3cdd08fe89 Add slider component 2022-11-08 01:36:08 -08:00
jeffvli f7ea6c45f5 Add lazy image component 2022-11-08 01:35:21 -08:00
jeffvli ecb090d324 Add modal transitions 2022-11-08 00:23:16 -08:00
jeffvli 587fa2422a Handle web player 2022-11-08 00:20:39 -08:00
jeffvli e774cdf031 Add tabs component 2022-11-07 04:04:32 -08:00
jeffvli cd56783c96 Add media keys 2022-11-07 03:46:55 -08:00
jeffvli 6ac949bf88 Replace @emotion/styled with styled-components 2022-11-06 01:53:31 -08:00
jeffvli de91f75203 Fix card keys 2022-11-06 01:25:53 -08:00
jeffvli d69221f8a4 Fix styles 2022-11-06 00:52:03 -07:00
jeffvli 39d98c5066 Remove checkbox folder 2022-11-06 00:51:48 -07:00
jeffvli 4304a2ae84 Various updates 2022-11-06 00:46:51 -07:00
jeffvli fc1ab03118 Fix props 2022-11-06 00:12:04 -07:00
jeffvli f76cc2f230 Adjust skeleton row width 2022-11-06 00:11:48 -07:00
jeffvli 70ce493f5e Add menu styles 2022-11-06 00:11:37 -07:00
jeffvli 633c6416df Album list updates 2022-11-06 00:08:22 -07:00
jeffvli 07123615ca Move fadein directly to component 2022-11-05 12:00:46 -07:00
jeffvli 385ec5f856 Remove disabled cursor styles 2022-11-05 03:25:59 -07:00
jeffvli 608518ac1c Add link text styles 2022-11-05 03:25:42 -07:00
jeffvli cee6ff4df5 Misc fixes 2022-11-05 03:25:30 -07:00
jeffvli c8da4f8146 Fix action item check if not authenticated 2022-11-05 03:24:38 -07:00
jeffvli 63dba7b379 Increase dropdown size 2022-11-05 03:12:04 -07:00
jeffvli e014ac0a4b Update grid card styles and props 2022-11-05 03:11:51 -07:00
jeffvli ae53b17214 Fix null imageUrl on credential 2022-11-04 19:18:35 -07:00
jeffvli fd53f90db2 Order genres by name 2022-11-04 18:38:00 -07:00
jeffvli d88e99e38c Adjust placeholder defaults 2022-11-04 18:37:42 -07:00
jeffvli 096a7713da Add placeholder images 2022-11-04 18:21:22 -07:00
jeffvli 7aa89e8ad2 Add genres api 2022-11-03 14:45:43 -07:00
jeffvli f284b29052 Add default ops, handle releaseDate 2022-11-03 14:45:00 -07:00
jeffvli 5908554f38 Set all popovers withinPortal by default 2022-11-03 14:41:34 -07:00
jeffvli 53a7d728b3 Progress on advanced filters 2022-11-03 03:25:10 -07:00
jeffvli be05c1df79 Update base components 2022-11-03 03:19:15 -07:00
jeffvli e9142ffaa5 Add paper component 2022-11-03 03:17:27 -07:00
jeffvli aeefbf8f7f Add scroll area component 2022-11-03 02:26:01 -07:00
jeffvli 061e61b7d3 Add packages 2022-11-02 22:05:40 -07:00
jeffvli a888007bfa Update base components 2022-11-02 22:05:16 -07:00
jeffvli 94b40178aa Progress on advanced filters 2022-11-02 22:04:46 -07:00
jeffvli 73fff64a75 Add genres route 2022-11-01 11:30:40 -07:00
jeffvli 97486b23ee Use img tag instead of background-image 2022-11-01 03:47:28 -07:00
jeffvli 005a30e0f4 Add advanced filters base component 2022-11-01 02:56:35 -07:00
jeffvli 1cdcde010c Add shell layout 2022-10-30 20:21:55 -07:00
jeffvli 895356701f Update server list 2022-10-30 20:21:31 -07:00
jeffvli 689560a7a5 Adjust playerbar 2022-10-30 20:20:04 -07:00
jeffvli bc9e6a9a73 Add animated page 2022-10-30 20:19:11 -07:00
jeffvli eb73c87933 Add specific toast types in export 2022-10-30 20:18:50 -07:00
jeffvli 7725d3dfbb Add fileinput, popover 2022-10-30 20:18:38 -07:00
jeffvli 659a9b949b Set as polymorphic 2022-10-30 20:18:16 -07:00
jeffvli 04afa13eae Fix year data 2022-10-30 20:17:06 -07:00
jeffvli df1844b74c Adjust styling and add links 2022-10-30 20:16:53 -07:00
jeffvli b2fc76203d Add 404 route 2022-10-30 15:00:54 -07:00
jeffvli a60a053b6b Add action-required route 2022-10-30 01:43:32 -07:00
jeffvli 699ed268e6 Set MPV path from local settings file 2022-10-30 01:37:05 -07:00
jeffvli e4f797debc Fix type 2022-10-30 01:34:09 -07:00
jeffvli 852a4297a3 Fix names 2022-10-29 19:14:40 -07:00
jeffvli 2489622d90 Rename 2022-10-29 19:14:13 -07:00
jeffvli 19090a0ed8 Update scanner (frontend) 2022-10-29 19:13:40 -07:00
jeffvli 0200b92860 Update scanner (server) 2022-10-29 19:12:02 -07:00
jeffvli ff6882a6cd Handle logout on invalid token 2022-10-29 18:57:23 -07:00
jeffvli 97b383ff0b Fix scripts based on new server path 2022-10-28 16:01:37 -07:00
jeffvli 4fb963d689 Add initial queue/album list routes 2022-10-28 13:11:29 -07:00
jeffvli c6d80831f8 Add ND type 2022-10-28 13:10:07 -07:00
jeffvli 32fe11d3de Handle right sidebar 2022-10-28 13:09:42 -07:00
jeffvli 5bcb0a3824 Add hook to fetch current server cred 2022-10-28 13:07:56 -07:00
jeffvli 91536f1bc9 Add isAdmin to permission set 2022-10-28 13:04:36 -07:00
jeffvli 4fb8d4ebd9 Handle album add with local creds 2022-10-28 13:04:09 -07:00
jeffvli b3f4cfee5d Add switch component 2022-10-28 13:03:10 -07:00
jeffvli 00710125d3 Improve grid 2022-10-28 13:02:55 -07:00
jeffvli 2352c136a1 Add fadeIn style 2022-10-28 13:02:00 -07:00
jeffvli ee8cc14e7e Fix missing song album details 2022-10-28 13:00:31 -07:00
jeffvli ca664d9430 Add server-side credential requirement 2022-10-27 20:33:42 -07:00
jeffvli cbbf3087ff Update serverfolder enable/disable 2022-10-27 00:58:59 -07:00
jeffvli 88e716b970 Add sidebar image option 2022-10-26 21:44:25 -07:00
jeffvli 17258e950e Update frontend linter 2022-10-26 16:28:07 -07:00
jeffvli 5b349083c9 Remove old file 2022-10-26 16:26:24 -07:00
jeffvli 1130006f0f Set min sidebar width to 200 2022-10-26 16:26:01 -07:00
jeffvli e31d8c1cc4 Fix types reference 2022-10-26 16:25:48 -07:00
jeffvli 834e311253 Remove sidebar index 2022-10-26 16:25:37 -07:00
jeffvli 82a47c42b8 Add sidebar state to app store 2022-10-26 16:24:28 -07:00
jeffvli 46eb5e4e5d Fix import 2022-10-26 16:24:16 -07:00
jeffvli 9204cdbe6a Fix casing 2 2022-10-26 16:23:57 -07:00
jeffvli 8258bbe5b3 Fix casing 2022-10-26 16:22:29 -07:00
jeffvli 8f82d001f0 Move types up 2022-10-26 16:21:51 -07:00
jeffvli 6763f4439d Update package name references 2022-10-26 16:13:32 -07:00
jeffvli 1622c12dfa Update server management 2022-10-26 16:12:57 -07:00
jeffvli 02e3b96384 Update titlebar 2022-10-26 16:12:18 -07:00
jeffvli c4765ba2d1 Add permissions check hook 2022-10-26 16:06:22 -07:00
jeffvli 1f261a95ad Update default routing 2022-10-26 16:05:58 -07:00
jeffvli cf17ce6e9d Update login flow 2022-10-26 16:04:39 -07:00
jeffvli 146a03cb3c Update package name 2022-10-26 15:59:33 -07:00
jeffvli f8e1a7d79e Update base components 2022-10-26 15:59:18 -07:00
jeffvli dc891e1b79 Set project name to feishin 2022-10-25 16:54:23 -07:00
jeffvli 0438f2d5f2 Move server directory outside of frontend src 2022-10-25 16:52:45 -07:00
jeffvli 863dce88b7 Add local settings 2022-10-24 22:38:06 -07:00
jeffvli f09abdb4c6 Update styles 2022-10-24 22:34:13 -07:00
jeffvli 781adb7c4d Update libs 2022-10-24 22:30:49 -07:00
jeffvli dd3de66232 Update player/shared components 2022-10-24 22:30:16 -07:00
jeffvli 8973571147 Update store/routes 2022-10-24 22:20:35 -07:00
jeffvli f8e7d02daf Update frontend base 2022-10-24 22:19:52 -07:00
jeffvli 921c688c94 Update utils 2022-10-24 22:09:58 -07:00
jeffvli 663b951cd7 Set jsxImportSource 2022-10-24 22:01:34 -07:00
jeffvli d4e4bdb858 Add lint rules 2022-10-24 22:01:23 -07:00
jeffvli 33e4526caa Update packages 2022-10-24 22:01:02 -07:00
jeffvli df3418120a Update base components 2022-10-24 21:56:58 -07:00
jeffvli dfdf53f6ee Update login 2022-10-24 21:47:18 -07:00
jeffvli d5bbff5eb6 Update frontend API structure 2022-10-24 21:47:03 -07:00
jeffvli 76b6eed4bb Update scanner imports 2022-10-24 21:42:15 -07:00
jeffvli 4a3ce02805 Redo server functionality 2022-10-24 21:41:47 -07:00
jeffvli db8a7d6a63 Update passport/prisma libs 2022-10-24 21:28:22 -07:00
jeffvli c09e9b6583 Add tsconfig-paths 2022-10-24 21:27:00 -07:00
jeffvli 730b72e64f Update initial schema 2022-10-24 21:25:51 -07:00
jeffvli 3aab920c71 Cleanup packages and structure
- Remove unused packages
- Swap styled-components for emotion/styled
- Add tsconfig baseUrl and src path
- Upgrade and add mantine packages
2022-10-19 21:23:31 -07:00
jeffvli 9675fd1d8e Remove unused packages 2022-10-19 21:21:09 -07:00
jeffvli 3e80f71833 Remove unused utils 2022-10-18 18:52:40 -07:00
jeffvli 9424203960 Add serverCredentials to passport user output 2022-10-18 01:46:41 -07:00
jeffvli 27b4b36cbf Add validations, req.authUser 2022-10-18 01:45:46 -07:00
jeffvli 238c90478e Reset schema and migrations 2022-10-17 20:02:29 -07:00
jeffvli a0c634da2f Update validation middleware
- Move to separate directory
- Add TypedRequest utility fn
- Utilize as middleware instead of function
2022-10-16 02:29:28 -07:00
jeffvli 7b55ca2fa8 Remove unused 2022-10-14 20:34:23 -07:00
jeffvli e2808e0bd4 Update scanners 2022-10-14 20:26:53 -07:00
jeffvli 5f844ef975 Set many album artists 2022-10-14 19:57:08 -07:00
jeffvli 968e80a6d8 Update subsonic scanner for new schema 2022-10-12 20:47:23 -07:00
jeffvli 0772566637 Set size to int 2022-10-12 18:21:51 -07:00
jeffvli 99f30439e1 Add server scripts 2022-10-12 13:52:50 -07:00
jeffvli 6433ccd750 Set error message optional 2022-10-12 13:52:44 -07:00
jeffvli ea2d3ea8f1 Adjust route structure
- Items now accessed through servers/:serverId
- Auth middleware blanketly applied on routes
- Merge params on routes
2022-10-12 13:52:35 -07:00
jeffvli 21bf5ce523 Bump server packages 2022-10-12 13:52:29 -07:00
jeffvli 95421698da Remove hard-coded types
- Use auto-generated prisma types
2022-10-12 13:52:22 -07:00
jeffvli 02ef79dcb2 Optimize jellyfin scanner
- Include changes / unfinished subsonic scanner
2022-10-12 13:52:16 -07:00
jeffvli 8aedd94033 Update auth object and middleware 2022-10-12 13:52:08 -07:00
jeffvli b5c7abb566 Update schema and add initial migration 2022-10-12 13:52:00 -07:00
jeffvli 9d09b830f9 Add initial rest client files 2022-10-12 13:51:16 -07:00
jeffvli dd6b80795e Normalize types 2022-08-20 02:06:00 -07:00
jeffvli a087dbdea3 Add ag-grid package 2022-08-02 10:53:56 -07:00
jeffvli 3d677188b5 Fix initial volume 2022-08-02 10:47:12 -07:00
jeffvli d11051bbc1 Adjust album/song types 2022-08-02 10:45:27 -07:00
jeffvli e1bc6ecf30 Add queue handler
Initial support for play "now", "next", and "last"
2022-07-31 05:24:33 -07:00
jeffvli 9c9cf3a978 Add dnd-kit package 2022-07-31 02:14:54 -07:00
jeffvli e1977b291e Add basic development instructions 2022-07-30 19:31:32 -07:00
jeffvli c100bbb341 Linting, misc fixes 2022-07-30 19:31:14 -07:00
jeffvli bb4576390d Change TS import baseUrl 2022-07-30 17:44:57 -07:00
jeffvli df5f8c08f3 Remove boilerplate file 2022-07-30 16:57:30 -07:00
jeffvli 95ff874702 Swap tabler-icons for react-icons 2022-07-30 16:57:16 -07:00
jeffvli 1eed976747 Remove isScrolling 2022-07-30 16:27:47 -07:00
jeffvli fe422897ab Update dev docker deployment
Rename environment variables, add tokens
2022-07-30 15:47:06 -07:00
jeffvli bc86da7762 Update pagination request on client 2022-07-30 15:41:18 -07:00
jeffvli aa673ac854 Use skip/take cursors instead of page number 2022-07-30 15:28:30 -07:00
jeffvli b8cf1d8283 Disable no-shadow rule
Resolves a conflict with typescript enum values
2022-07-30 07:04:01 -07:00
jeffvli b9a171b096 Add filter functionality for infinite album list 2022-07-30 07:03:10 -07:00
jeffvli fa9cf2efda Migrate Mantine components to v5 2022-07-27 21:20:41 -07:00
jeffvli 34ee3222f4 Bump react to 18.2.0 2022-07-27 21:19:49 -07:00
jeffvli f13427022e add mantine dependency 2022-07-25 19:53:26 -07:00
jeffvli e8b612c974 add initial files 2022-07-25 19:40:16 -07:00
535 changed files with 59943 additions and 18101 deletions
+4
View File
@@ -0,0 +1,4 @@
node_modules
release/app/node_modules
release/app/dist
server/node_modules
+5 -11
View File
@@ -1,18 +1,12 @@
# EditorConfig is awesome: http://EditorConfig.org
# https://github.com/jokeyrhyme/standard-editorconfig
# top-most EditorConfig file
root = true root = true
# defaults
[*] [*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_size = 2
indent_style = space indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
-39
View File
@@ -1,39 +0,0 @@
/**
* TODO: Rewrite this config to ESM
* But currently electron-builder doesn't support ESM configs
* @see https://github.com/develar/read-config-file/issues/10
*/
/**
* @type {() => import('electron-builder').Configuration}
* @see https://www.electron.build/configuration/configuration
*/
module.exports = async function () {
const {getVersion} = await import('./version/getVersion.mjs');
return {
directories: {
output: 'dist',
buildResources: 'buildResources',
},
files: ['packages/**/dist/**'],
extraMetadata: {
version: getVersion(),
},
productName: 'Feishin',
appId: 'org.jeffvli.feishin',
artifactName: '${productName}-${version}-${os}-${arch}.${ext}',
win: {
target: ['nsis', 'zip'],
},
publish: {
provider: 'github',
owner: 'jeffvli',
repo: 'feishin',
},
// Specify linux target just for disabling snap compilation
linux: {
target: 'deb',
},
};
};
+7
View File
@@ -0,0 +1,7 @@
{
"rules": {
"no-console": "off",
"global-require": "off",
"import/no-dynamic-require": "off"
}
}
+59
View File
@@ -0,0 +1,59 @@
/**
* Base webpack config used across other specific configs
*/
import webpack from 'webpack';
import { dependencies as externals } from '../../release/app/package.json';
import webpackPaths from './webpack.paths';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
const configuration: webpack.Configuration = {
externals: [...Object.keys(externals || {})],
module: {
rules: [
{
exclude: /node_modules/,
test: /\.[jt]sx?$/,
use: {
loader: 'ts-loader',
options: {
// Remove this line to enable type checking in webpack builds
transpileOnly: true,
},
},
},
],
},
output: {
// https://github.com/webpack/webpack/issues/1114
library: {
type: 'commonjs2',
},
path: webpackPaths.srcPath,
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
fallback: {
child_process: false,
},
plugins: [new TsconfigPathsPlugin({ baseUrl: webpackPaths.srcPath })],
modules: [webpackPaths.srcPath, 'node_modules'],
},
stats: 'errors-only',
};
export default configuration;
+3
View File
@@ -0,0 +1,3 @@
/* eslint import/no-unresolved: off, import/no-self-import: off */
module.exports = require('./webpack.config.renderer.dev').default;
+84
View File
@@ -0,0 +1,84 @@
/**
* Webpack config for production electron main process
*/
import path from 'path';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: 'electron-main',
entry: {
main: path.join(webpackPaths.srcMainPath, 'main.ts'),
preload: path.join(webpackPaths.srcMainPath, 'preload.ts'),
},
output: {
path: webpackPaths.distMainPath,
filename: '[name].js',
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
START_MINIMIZED: false,
}),
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false,
},
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,70 @@
import path from 'path';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: 'electron-preload',
entry: path.join(webpackPaths.srcMainPath, 'preload.ts'),
output: {
path: webpackPaths.dllPath,
filename: 'preload.js',
},
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
],
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false,
},
watch: true,
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,79 @@
/**
* Builds the DLL for development electron renderer process
*/
import path from 'path';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import { dependencies } from '../../package.json';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('development');
const dist = webpackPaths.dllPath;
const configuration: webpack.Configuration = {
context: webpackPaths.rootPath,
devtool: 'eval',
mode: 'development',
target: 'electron-renderer',
externals: ['fsevents', 'crypto-browserify'],
/**
* Use `module` from `webpack.config.renderer.dev.js`
*/
module: require('./webpack.config.renderer.dev').default.module,
entry: {
renderer: Object.keys(dependencies || {}),
},
output: {
path: dist,
filename: '[name].dev.dll.js',
library: {
name: 'renderer',
type: 'var',
},
},
plugins: [
new webpack.DllPlugin({
path: path.join(dist, '[name].json'),
name: '[name]',
}),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
options: {
context: webpackPaths.srcPath,
output: {
path: webpackPaths.dllPath,
},
},
}),
],
};
export default merge(baseConfig, configuration);
+191
View File
@@ -0,0 +1,191 @@
import 'webpack-dev-server';
import { execSync, spawn } from 'child_process';
import fs from 'fs';
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import chalk from 'chalk';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const port = process.env.PORT || 4343;
const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const requiredByDLLConfig = module.parent!.filename.includes(
'webpack.config.renderer.dev.dll'
);
/**
* Warn if the DLL is not built
*/
if (
!requiredByDLLConfig &&
!(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest))
) {
console.log(
chalk.black.bgYellow.bold(
'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"'
)
);
execSync('npm run postinstall');
}
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: ['web', 'electron-renderer'],
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
...(requiredByDLLConfig
? []
: [
new webpack.DllReferencePlugin({
context: webpackPaths.dllPath,
manifest: require(manifest),
sourceType: 'var',
}),
]),
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
setupMiddlewares(middlewares) {
console.log('Starting preload.js builder...');
const preloadProcess = spawn('npm', ['run', 'start:preload'], {
shell: true,
stdio: 'inherit',
})
.on('close', (code: number) => process.exit(code!))
.on('error', (spawnError) => console.error(spawnError));
console.log('Starting Main Process...');
spawn('npm', ['run', 'start:main'], {
shell: true,
stdio: 'inherit',
})
.on('close', (code: number) => {
preloadProcess.kill();
process.exit(code!);
})
.on('error', (spawnError) => console.error(spawnError));
return middlewares;
},
},
};
export default merge(baseConfig, configuration);
@@ -0,0 +1,131 @@
/**
* Build config for electron renderer process
*/
import path from 'path';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import deleteSourceMaps from '../scripts/delete-source-maps';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
checkNodeEnv('production');
deleteSourceMaps();
const devtoolsConfig =
process.env.DEBUG_PROD === 'true'
? {
devtool: 'source-map',
}
: {};
const configuration: webpack.Configuration = {
...devtoolsConfig,
mode: 'production',
target: ['web', 'electron-renderer'],
entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')],
output: {
path: webpackPaths.distRendererPath,
publicPath: './',
filename: 'renderer.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?(a|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
}),
new CssMinimizerPlugin(),
],
},
plugins: [
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
DEBUG_PROD: false,
}),
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
isDevelopment: process.env.NODE_ENV !== 'production',
}),
],
};
export default merge(baseConfig, configuration);
+140
View File
@@ -0,0 +1,140 @@
import 'webpack-dev-server';
import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import checkNodeEnv from '../scripts/check-node-env';
import baseConfig from './webpack.config.base';
import webpackPaths from './webpack.paths';
// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
checkNodeEnv('development');
}
const port = process.env.PORT || 4343;
const configuration: webpack.Configuration = {
devtool: 'inline-source-map',
mode: 'development',
target: ['web', 'electron-renderer'],
entry: [
`webpack-dev-server/client?http://localhost:${port}/dist`,
'webpack/hot/only-dev-server',
path.join(webpackPaths.srcRendererPath, 'index.tsx'),
],
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
module: {
rules: [
{
test: /\.s?css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
sourceMap: true,
importLoaders: 1,
},
},
'sass-loader',
],
include: /\.module\.s?(c|a)ss$/,
},
{
test: /\.s?css$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
exclude: /\.module\.s?(c|a)ss$/,
},
// Fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
// Images
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
/**
* Create global constants which can be configured at compile time.
*
* Useful for allowing different behaviour between development builds and
* release builds
*
* NODE_ENV should be production so that modules do not perform certain
* development checks
*
* By default, use 'development' as NODE_ENV. This can be overriden with
* 'staging', for example, by changing the ENV variables in the npm scripts
*/
new webpack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
new ReactRefreshWebpackPlugin(),
new HtmlWebpackPlugin({
filename: path.join('index.html'),
template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
},
isBrowser: false,
env: process.env.NODE_ENV,
isDevelopment: process.env.NODE_ENV !== 'production',
nodeModules: webpackPaths.appNodeModulesPath,
}),
],
node: {
__dirname: false,
__filename: false,
},
devServer: {
port,
compress: true,
hot: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
setupMiddlewares(middlewares) {
return middlewares;
},
},
};
export default merge(baseConfig, configuration);
+38
View File
@@ -0,0 +1,38 @@
const path = require('path');
const rootPath = path.join(__dirname, '../..');
const dllPath = path.join(__dirname, '../dll');
const srcPath = path.join(rootPath, 'src');
const srcMainPath = path.join(srcPath, 'main');
const srcRendererPath = path.join(srcPath, 'renderer');
const releasePath = path.join(rootPath, 'release');
const appPath = path.join(releasePath, 'app');
const appPackagePath = path.join(appPath, 'package.json');
const appNodeModulesPath = path.join(appPath, 'node_modules');
const srcNodeModulesPath = path.join(srcPath, 'node_modules');
const distPath = path.join(appPath, 'dist');
const distMainPath = path.join(distPath, 'main');
const distRendererPath = path.join(distPath, 'renderer');
const buildPath = path.join(releasePath, 'build');
export default {
rootPath,
dllPath,
srcPath,
srcMainPath,
srcRendererPath,
releasePath,
appPath,
appPackagePath,
appNodeModulesPath,
srcNodeModulesPath,
distPath,
distMainPath,
distRendererPath,
buildPath,
};
+1
View File
@@ -0,0 +1 @@
export default 'test-file-stub';
+8
View File
@@ -0,0 +1,8 @@
{
"rules": {
"no-console": "off",
"global-require": "off",
"import/no-dynamic-require": "off",
"import/no-extraneous-dependencies": "off"
}
}
+24
View File
@@ -0,0 +1,24 @@
// Check if the renderer and main bundles are built
import path from 'path';
import chalk from 'chalk';
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';
const mainPath = path.join(webpackPaths.distMainPath, 'main.js');
const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js');
if (!fs.existsSync(mainPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The main process is not built yet. Build it by running "npm run build:main"'
)
);
}
if (!fs.existsSync(rendererPath)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
'The renderer process is not built yet. Build it by running "npm run build:renderer"'
)
);
}
+54
View File
@@ -0,0 +1,54 @@
import fs from 'fs';
import chalk from 'chalk';
import { execSync } from 'child_process';
import { dependencies } from '../../package.json';
if (dependencies) {
const dependenciesKeys = Object.keys(dependencies);
const nativeDeps = fs
.readdirSync('node_modules')
.filter((folder) => fs.existsSync(`node_modules/${folder}/binding.gyp`));
if (nativeDeps.length === 0) {
process.exit(0);
}
try {
// Find the reason for why the dependency is installed. If it is installed
// because of a devDependency then that is okay. Warn when it is installed
// because of a dependency
const { dependencies: dependenciesObject } = JSON.parse(
execSync(`npm ls ${nativeDeps.join(' ')} --json`).toString()
);
const rootDependencies = Object.keys(dependenciesObject);
const filteredRootDependencies = rootDependencies.filter((rootDependency) =>
dependenciesKeys.includes(rootDependency)
);
if (filteredRootDependencies.length > 0) {
const plural = filteredRootDependencies.length > 1;
console.log(`
${chalk.whiteBright.bgYellow.bold(
'Webpack does not work with native dependencies.'
)}
${chalk.bold(filteredRootDependencies.join(', '))} ${
plural ? 'are native dependencies' : 'is a native dependency'
} and should be installed inside of the "./release/app" folder.
First, uninstall the packages from "./package.json":
${chalk.whiteBright.bgGreen.bold('npm uninstall your-package')}
${chalk.bold(
'Then, instead of installing the package to the root "./package.json":'
)}
${chalk.whiteBright.bgRed.bold('npm install your-package')}
${chalk.bold('Install the package to "./release/app/package.json"')}
${chalk.whiteBright.bgGreen.bold(
'cd ./release/app && npm install your-package'
)}
Read more about native dependencies at:
${chalk.bold(
'https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure'
)}
`);
process.exit(1);
}
} catch (e) {
console.log('Native dependencies could not be checked');
}
}
+16
View File
@@ -0,0 +1,16 @@
import chalk from 'chalk';
export default function checkNodeEnv(expectedEnv) {
if (!expectedEnv) {
throw new Error('"expectedEnv" not set');
}
if (process.env.NODE_ENV !== expectedEnv) {
console.log(
chalk.whiteBright.bgRed.bold(
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
)
);
process.exit(2);
}
}
+16
View File
@@ -0,0 +1,16 @@
import chalk from 'chalk';
import detectPort from 'detect-port';
const port = process.env.PORT || '4343';
detectPort(port, (err, availablePort) => {
if (port !== String(availablePort)) {
throw new Error(
chalk.whiteBright.bgRed.bold(
`Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`
)
);
} else {
process.exit(0);
}
});
+17
View File
@@ -0,0 +1,17 @@
import rimraf from 'rimraf';
import process from 'process';
import webpackPaths from '../configs/webpack.paths';
const args = process.argv.slice(2);
const commandMap = {
dist: webpackPaths.distPath,
release: webpackPaths.releasePath,
dll: webpackPaths.dllPath,
};
args.forEach((x) => {
const pathToRemove = commandMap[x];
if (pathToRemove !== undefined) {
rimraf.sync(pathToRemove);
}
});
+8
View File
@@ -0,0 +1,8 @@
import path from 'path';
import rimraf from 'rimraf';
import webpackPaths from '../configs/webpack.paths';
export default function deleteSourceMaps() {
rimraf.sync(path.join(webpackPaths.distMainPath, '*.js.map'));
rimraf.sync(path.join(webpackPaths.distRendererPath, '*.js.map'));
}
+20
View File
@@ -0,0 +1,20 @@
import { execSync } from 'child_process';
import fs from 'fs';
import { dependencies } from '../../release/app/package.json';
import webpackPaths from '../configs/webpack.paths';
if (
Object.keys(dependencies || {}).length > 0 &&
fs.existsSync(webpackPaths.appNodeModulesPath)
) {
const electronRebuildCmd =
'../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .';
const cmd =
process.platform === 'win32'
? electronRebuildCmd.replace(/\//g, '\\')
: electronRebuildCmd;
execSync(cmd, {
cwd: webpackPaths.appPath,
stdio: 'inherit',
});
}
+9
View File
@@ -0,0 +1,9 @@
import fs from 'fs';
import webpackPaths from '../configs/webpack.paths';
const { srcNodeModulesPath } = webpackPaths;
const { appNodeModulesPath } = webpackPaths;
if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) {
fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction');
}
+30
View File
@@ -0,0 +1,30 @@
const { notarize } = require('electron-notarize');
const { build } = require('../../package.json');
exports.default = async function notarizeMacos(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
if (process.env.CI !== 'true') {
console.warn('Skipping notarizing step. Packaging is not running in CI');
return;
}
if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env)) {
console.warn(
'Skipping notarizing step. APPLE_ID and APPLE_ID_PASS env variables must be set'
);
return;
}
const appName = context.packager.appInfo.productFilename;
await notarize({
appBundleId: build.appId,
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASS,
});
};
+34
View File
@@ -0,0 +1,34 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Coverage directory used by tools like istanbul
coverage
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
# OSX
.DS_Store
src/i18n
release/app/dist
release/build
.erb/dll
.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts
# eslint ignores hidden directories by default:
# https://github.com/eslint/eslint/issues/8429
!.erb
+82
View File
@@ -0,0 +1,82 @@
module.exports = {
extends: ['erb', 'plugin:typescript-sort-keys/recommended'],
ignorePatterns: ['.erb/*', 'server'],
parser: '@typescript-eslint/parser',
parserOptions: {
createDefaultProgram: true,
ecmaVersion: 2020,
project: './tsconfig.json',
sourceType: 'module',
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint', 'import', 'sort-keys-fix'],
root: true,
rules: {
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-shadow': ['off'],
'import/extensions': 'off',
// A temporary hack related to IDE not resolving correct package.json
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'error',
'import/order': [
'error',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: ['builtin', 'external', 'internal', ['parent', 'sibling']],
'newlines-between': 'never',
pathGroups: [
{
group: 'external',
pattern: 'react',
position: 'before',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'import/prefer-default-export': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/interactive-supports-focus': 'off',
'jsx-a11y/media-has-caption': 'off',
'no-await-in-loop': 'off',
'no-console': 'off',
'no-nested-ternary': 'off',
'no-restricted-syntax': 'off',
'no-underscore-dangle': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
ignoreCase: false,
noSortAlphabetically: false,
reservedFirst: true,
shorthandFirst: true,
shorthandLast: false,
},
],
// Since React 17 and typescript 4.1 you can safely disable the rule
'react/react-in-jsx-scope': 'off',
'sort-keys-fix/sort-keys-fix': 'warn',
},
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
// See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
typescript: {},
webpack: {
config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
},
},
},
};
-51
View File
@@ -1,51 +0,0 @@
{
"root": true,
"env": {
"es2021": true,
"node": true,
"browser": false
},
"extends": [
"eslint:recommended",
/** @see https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#recommended-configs */
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"ignorePatterns": ["node_modules/**", "**/dist/**"],
"rules": {
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
],
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/consistent-type-imports": "error",
/**
* Having a semicolon helps the optimizer interpret your code correctly.
* This avoids rare errors in optimized code.
* @see https://twitter.com/alex_kozack/status/1364210394328408066
*/
"semi": ["error", "always"],
/**
* This will make the history of changes in the hit a little cleaner
*/
"comma-dangle": ["warn", "always-multiline"],
/**
* Just for beauty
*/
"quotes": [
"warn",
"single",
{
"avoidEscape": true
}
]
}
}
+12 -4
View File
@@ -1,4 +1,12 @@
.github/actions/**/*.js linguist-detectable=false * text eol=lf
scripts/*.js linguist-detectable=false *.exe binary
*.config.js linguist-detectable=false *.png binary
* text=auto eol=lf *.jpg binary
*.jpeg binary
*.ico binary
*.icns binary
*.eot binary
*.otf binary
*.ttf binary
*.woff binary
*.woff2 binary
-10
View File
@@ -1,11 +1 @@
# These are supported funding model platforms # These are supported funding model platforms
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: jeffvli
liberapay: #
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+67
View File
@@ -0,0 +1,67 @@
---
name: Bug report
about: You're having technical issues. 🐞
labels: 'bug'
---
<!-- Please use the following issue template or your issue will be closed -->
## Prerequisites
<!-- If the following boxes are not ALL checked, your issue is likely to be closed -->
- [ ] Using npm
- [ ] Using an up-to-date [`main` branch](https://github.com/electron-react-boilerplate/electron-react-boilerplate/tree/main)
- [ ] Using latest version of devtools. [Check the docs for how to update](https://electron-react-boilerplate.js.org/docs/dev-tools/)
- [ ] Tried solutions mentioned in [#400](https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues/400)
- [ ] For issue in production release, add devtools output of `DEBUG_PROD=true npm run build && npm start`
## Expected Behavior
<!--- What should have happened? -->
## Current Behavior
<!--- What went wrong? -->
## Steps to Reproduce
<!-- Add relevant code and/or a live example -->
<!-- Add stack traces -->
1.
2.
3.
4.
## Possible Solution (Not obligatory)
<!--- Suggest a reason for the bug or how to fix it. -->
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Did you make any changes to the boilerplate after cloning it? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
- Node version :
- electron-react-boilerplate version or branch :
- Operating System and version :
- Link to your project :
<!---
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
Donations will ensure the following:
🔨 Long term maintenance of the project
🛣 Progress on the roadmap
🐛 Quick responses to bug reports and help requests
-->
+19
View File
@@ -0,0 +1,19 @@
---
name: Question
about: Ask a question.❓
labels: 'question'
---
## Summary
<!-- What do you need help with? -->
<!---
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
Donations will ensure the following:
🔨 Long term maintenance of the project
🛣 Progress on the roadmap
🐛 Quick responses to bug reports and help requests
-->
@@ -0,0 +1,15 @@
---
name: Feature request
about: You want something added to the boilerplate. 🎉
labels: 'enhancement'
---
<!---
❗️❗️ Also, please consider donating (https://opencollective.com/electron-react-boilerplate-594) ❗️❗️
Donations will ensure the following:
🔨 Long term maintenance of the project
🛣 Progress on the roadmap
🐛 Quick responses to bug reports and help requests
-->
-28
View File
@@ -1,28 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: cawa-93
---
**Describe the bug**
A clear and concise description of what the bug is.
**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.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Questions & Discussions
url: https://github.com/cawa-93/vite-electron-builder/discussions/categories/q-a
about: Use GitHub discussions for message-board style questions and discussions.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: cawa-93
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+6
View File
@@ -0,0 +1,6 @@
requiredHeaders:
- Prerequisites
- Expected Behavior
- Current Behavior
- Possible Solution
- Your Environment
-18
View File
@@ -1,18 +0,0 @@
{
"extends": [
"config:base",
":semanticCommits",
":semanticCommitTypeAll(deps)",
":semanticCommitScopeDisabled",
":automergeAll",
":automergeBranch",
":disableDependencyDashboard",
":pinVersions",
":onlyNpm",
":label(dependencies)"
],
"gitNoVerify": [
"commit",
"push"
]
}
+17
View File
@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- discussion
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
-47
View File
@@ -1,47 +0,0 @@
# This workflow is the entry point for all CI processes.
# It is from here that all other workflows are launched.
on:
workflow_dispatch:
push:
branches:
- main
- 'renovate/**'
paths-ignore:
- '.github/**'
- '!.github/workflows/ci.yml'
- '!.github/workflows/typechecking.yml'
- '!.github/workflows/tests.yml'
- '!.github/workflows/release.yml'
- '**.md'
- .editorconfig
- .gitignore
- '.idea/**'
- '.vscode/**'
pull_request:
paths-ignore:
- '.github/**'
- '!.github/workflows/ci.yml'
- '!.github/workflows/typechecking.yml'
- '!.github/workflows/tests.yml'
- '!.github/workflows/release.yml'
- '**.md'
- .editorconfig
- .gitignore
- '.idea/**'
- '.vscode/**'
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
typechecking:
uses: ./.github/workflows/typechecking.yml
tests:
uses: ./.github/workflows/tests.yml
draft_release:
with:
dry-run: ${{ github.event_name != 'push' || github.ref_name != 'main' }}
needs: [ typechecking, tests ]
uses: ./.github/workflows/release.yml
-65
View File
@@ -1,65 +0,0 @@
on:
workflow_dispatch:
push:
paths:
- '**.js'
- '**.mjs'
- '**.cjs'
- '**.jsx'
- '**.ts'
- '**.mts'
- '**.cts'
- '**.tsx'
- '**.vue'
- '**.json'
pull_request:
paths:
- '**.js'
- '**.mjs'
- '**.cjs'
- '**.jsx'
- '**.ts'
- '**.mts'
- '**.cts'
- '**.tsx'
- '**.vue'
- '**.json'
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16 # Need for npm >=7.7
cache: 'npm'
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run lint --if-present
# This job just check code style for in-template contributions.
code-style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16 # Need for npm >=7.7
cache: 'npm'
- run: npm i prettier
- run: npx prettier --check "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,json}"
+39
View File
@@ -0,0 +1,39 @@
name: Publish (Manual)
on: workflow_dispatch
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
steps:
- name: Checkout git repo
uses: actions/checkout@v1
- name: Install Node and NPM
uses: actions/setup-node@v1
with:
node-version: 16
cache: npm
- name: Install dependencies
run: |
npm install --legacy-peer-deps
- name: Publish releases
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: nick-invision/retry@v2.8.2
with:
timeout_minutes: 30
max_attempts: 3
retry_on: error
command: |
npm run postinstall
npm run build
npm exec electron-builder -- --publish always --win --mac --linux
on_retry_command: npm cache clean --force
-60
View File
@@ -1,60 +0,0 @@
name: Release
on:
workflow_call:
inputs:
dry-run:
description: 'Compiles the app but not upload artifacts to distribution server'
default: false
required: false
type: boolean
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
draft_release:
strategy:
fail-fast: true
matrix:
os: [ macos-latest, ubuntu-latest, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16 # Need for npm >=7.7
cache: 'npm'
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- name: Compile artifacts ${{ inputs.dry-run && '' || 'and upload them to github release' }}
# I use this action because it is capable of retrying multiple times if there are any issues with the distribution server
uses: nick-fields/retry@v2
with:
timeout_minutes: 15
max_attempts: 3
retry_on: error
shell: 'bash'
command: npx --no-install electron-builder --config .electron-builder.config.js --publish ${{ inputs.dry-run && 'never' || 'always' }}
env:
# Code Signing params
# See https://www.electron.build/code-signing
# CSC_LINK: ''
# CSC_KEY_PASSWORD: ''
# Publishing artifacts
GH_TOKEN: ${{ secrets.github_token }} # GitHub token, automatically provided (No need to define this secret in the repo settings)
+34
View File
@@ -0,0 +1,34 @@
name: Test
on: [push, pull_request]
jobs:
release:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
steps:
- name: Check out Git repository
uses: actions/checkout@v1
- name: Install Node.js and NPM
uses: actions/setup-node@v2
with:
node-version: 16
cache: npm
- name: npm install
run: |
npm install --legacy-peer-deps
- name: npm test
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npm run package
npm run lint
npm exec tsc
npm test
-53
View File
@@ -1,53 +0,0 @@
name: Tests
on: [ workflow_call ]
concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
unit:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, ubuntu-latest, macos-latest ]
package: [ main, preload, renderer ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run test:${{ matrix.package }} --if-present
e2e:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
# I ran into problems trying to run an electron window in ubuntu due to a missing graphics server.
# That's why this special command for Ubuntu is here
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e --if-present
if: matrix.os == 'ubuntu-latest'
- run: npm run test:e2e --if-present
if: matrix.os != 'ubuntu-latest'
-27
View File
@@ -1,27 +0,0 @@
name: Typechecking
on: [ workflow_call ]
concurrency:
group: typechecking-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: 'bash'
jobs:
typescript:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16 # Need for npm >=7.7
cache: 'npm'
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run typecheck --if-present
+24 -51
View File
@@ -1,58 +1,31 @@
node_modules # Logs
.DS_Store logs
dist *.log
*.local
thumbs.db
# Runtime data
pids
*.pid
*.seed
# Coverage directory used by tools like istanbul
coverage
.eslintcache .eslintcache
.browserslistrc
.electron-vendors.cache.json
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Dependency directory
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
# User-specific stuff # OSX
.idea/**/workspace.xml .DS_Store
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files release/app/dist
.idea/**/contentModel.xml release/build
.erb/dll
# Sensitive or high-churn files .idea
.idea/**/dataSources/ npm-debug.log.*
.idea/**/dataSources.ids *.css.d.ts
.idea/**/dataSources.local.xml *.sass.d.ts
.idea/**/sqlDataSources.xml *.scss.d.ts
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle .env*
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# Editor-based Rest Client
.idea/httpRequests
/.idea/csv-plugin.xml
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
-65
View File
@@ -1,65 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<HTMLCodeStyleSettings>
<option name="HTML_ATTRIBUTE_WRAP" value="4" />
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
<option name="HTML_ENFORCE_QUOTES" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_TYPE_BRACES" value="false" />
</JSCodeStyleSettings>
<JSON>
<option name="OBJECT_WRAPPING" value="5" />
<option name="ARRAY_WRAPPING" value="5" />
</JSON>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_TYPE_BRACES" value="false" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="UNIFORM_INDENT" value="false" />
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="120" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
-5
View File
@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
<serverData>
<paths name="ihappymama-aliexpress">
<serverdata>
<mappings>
<mapping local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
<paths name="iosico.com">
<serverdata>
<mappings>
<mapping local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
<paths name="somespeed.com">
<serverdata>
<mappings>
<mapping local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
</serverData>
</component>
</project>
-6
View File
@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<work-dir-patterns value="src/**/*.{ts,vue} {bin,config}/**/*.js" />
<option name="fix-on-save" value="true" />
</component>
</project>
-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JsonSchemaMappingsProjectConfiguration">
<state>
<map>
<entry key="GitHub Workflow">
<value>
<SchemaInfo>
<option name="name" value="GitHub Workflow" />
<option name="relativePathToSchema" value="https://json.schemastore.org/github-workflow.json" />
<option name="applicationDefined" value="true" />
<option name="patterns">
<list>
<Item>
<option name="path" value=".github/workflows/release.yml" />
</Item>
<Item>
<option name="path" value=".github/workflows/ci.yml" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
</map>
</state>
</component>
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/vite-electron-builder.iml" filepath="$PROJECT_DIR$/.idea/vite-electron-builder.iml" />
</modules>
</component>
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myRunOnSave" value="true" />
<option name="myRunOnReformat" value="true" />
<option name="myFilesPattern" value="{**/*,*}.{js,mjs,cjs,ts,mts,cts,vue,json}" />
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/packages/main/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/packages/preload/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/packages/renderer/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/packages/renderer/dist" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/main/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/preload/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/preload/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/packages/main/node_modules" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebResourcesPaths">
<contentEntries>
<entry url="file://$PROJECT_DIR$">
<entryData>
<resourceRoots>
<path value="file://$PROJECT_DIR$/packages/renderer/assets" />
</resourceRoots>
</entryData>
</entry>
</contentEntries>
</component>
</project>
-24
View File
@@ -1,24 +0,0 @@
import {resolve, sep} from 'path';
export default {
'*.{js,mjs,cjs,ts,mts,cts,vue}': 'eslint --cache --fix',
/**
* Run typechecking if any type-sensitive files or project dependencies was changed
* @param {string[]} filenames
* @return {string[]}
*/
'{package-lock.json,packages/**/{*.ts,*.vue,tsconfig.json}}': ({filenames}) => {
// if dependencies was changed run type checking for all packages
if (filenames.some(f => f.endsWith('package-lock.json'))) {
return ['npm run typecheck --if-present'];
}
// else run type checking for staged packages
const fileNameToPackageName = filename =>
filename.replace(resolve(process.cwd(), 'packages') + sep, '').split(sep)[0];
return [...new Set(filenames.map(fileNameToPackageName))].map(
p => `npm run typecheck:${p} --if-present`,
);
},
};
-10
View File
@@ -1,10 +0,0 @@
**/node_modules
**/dist
**/*.svg
package.json
package-lock.json
.electron-vendors.cache.json
.github
.idea
+4 -16
View File
@@ -1,20 +1,8 @@
{ {
"printWidth": 100, "trailingComma": "es5",
"tabWidth": 2,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"overrides": [ "printWidth": 100,
{ "arrowParens": "always"
"files": ["**/*.css", "**/*.scss", "**/*.html"],
"options": {
"singleQuote": false
}
}
],
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "lf",
"singleAttributePerLine": true
} }
-3
View File
@@ -1,3 +0,0 @@
{
"pre-commit": "npx nano-staged"
}
+4 -1
View File
@@ -21,7 +21,10 @@
"declaration-block-no-redundant-longhand-properties": null, "declaration-block-no-redundant-longhand-properties": null,
"selector-class-pattern": null, "selector-class-pattern": null,
"selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }], "selector-type-case": ["lower", { "ignoreTypes": ["/^\\$\\w+/"] }],
"selector-type-no-unknown": [true, { "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }], "selector-type-no-unknown": [
true,
{ "ignoreTypes": ["/-styled-mixin/", "/^\\$\\w+/"] }
],
"value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }], "value-keyword-case": ["lower", { "ignoreKeywords": ["dummyValue"] }],
"declaration-colon-newline-after": null "declaration-colon-newline-after": null
} }
+2 -2
View File
@@ -1,8 +1,8 @@
{ {
"recommendations": [ "recommendations": [
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode"
"ms-vscode.vscode-typescript-next"
] ]
} }
+21 -4
View File
@@ -2,12 +2,29 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Electron: Main",
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Debug Main Process", "protocol": "inspector",
"skipFiles": ["<node_internals>/**"], "runtimeExecutable": "npm",
"program": "${workspaceFolder}\\scripts\\watch.mjs", "runtimeArgs": [
"autoAttachChildProcesses": true "run start:main --inspect=5858 --remote-debugging-port=9223"
],
"preLaunchTask": "Start Webpack Dev"
},
{
"name": "Electron: Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"webRoot": "${workspaceFolder}",
"timeout": 15000
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": ["Electron: Main", "Electron: Renderer"]
} }
] ]
} }
+52 -6
View File
@@ -1,8 +1,54 @@
{ {
"typescript.preferences.importModuleSpecifier": "non-relative", "files.associations": {
"stylelint.validate": ["css", "less", "postcss", "typescript", "typescriptreact", "scss"], ".eslintrc": "jsonc",
"typescript.updateImportsOnFileMove.enabled": "always", ".prettierrc": "jsonc",
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, ".eslintignore": "ignore"
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, },
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true "eslint.validate": ["typescript"],
"eslint.workingDirectories": [
{ "directory": "./", "changeProcessCWD": true },
{ "directory": "./server", "changeProcessCWD": true }
],
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": false
},
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"javascript.validate.enable": false,
"javascript.format.enable": false,
"typescript.format.enable": false,
"search.exclude": {
".git": true,
".eslintcache": true,
".erb/dll": true,
"release/{build,app/dist}": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"package-lock.json": true,
"*.{css,sass,scss}.d.ts": true
},
"rest-client.environmentVariables": {
"$shared": {
"host": "http://localhost:9321"
},
"dev-user": {
"token": "",
"refreshToken": "",
"authUsername": "user",
"authPassword": "user"
},
"dev-admin": {
"token": "",
"refreshToken": "",
"authUsername": "admin",
"authPassword": "admin"
}
},
"i18n-ally.localesPaths": ["src/i18n", "src/i18n/locales"],
"typescript.tsdk": "node_modules\\typescript\\lib"
} }
+25
View File
@@ -0,0 +1,25 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"label": "Start Webpack Dev",
"script": "start:renderer",
"options": {
"cwd": "${workspaceFolder}"
},
"isBackground": true,
"problemMatcher": {
"owner": "custom",
"pattern": {
"regexp": "____________"
},
"background": {
"activeOnStart": true,
"beginsPattern": "Compiling\\.\\.\\.$",
"endsPattern": "(Compiled successfully|Failed to compile)\\.$"
}
}
}
]
}
+586
View File
@@ -0,0 +1,586 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
[0.15.0] - 2022-04-13
### Added
- Added setting to save and resume the current queue between sessions (#130) (Thanks @kgarner7)
- Added a simple "play random" button to the player bar (#276)
- Added new seek/volume sliders (#272)
- Seeking/dragging is now more responsive
- Added improved discord rich presence (#286)
- Added download button on the playlist view (#266)
- (Jellyfin) Added "genre" column to the artist list
### Changed
- Swapped the order of "Seek Forward/Backward" and "Next/Prev Track" buttons on the player bar
- Global volume is now calculated logarithmically (#275) (Thanks @gelaechter)
- "Auto playlist" is now named "Play Random" (#276)
- "Now playing" option is now available on the "Start page" setting
### Fixed
- Playing songs by double clicking on a list should now play in the proper order (#279)
- (Linux) Fixed MPRIS metadata not updating when player automatically increments (#263)
- Application fonts now loaded locally instead of from Google CDN (#284)
- Enabling "Default to Album List on Artist Page" no longer performs a double redirect when entering the artist page (#271)
- Stop button is no longer disabled when playback is stopped (#273)
- Various package updates (#288) (Thanks @kgarner7)
- Top control bar show no longer be accessible when not logged in (#267)
[0.14.0] - 2022-03-12
### Added
- Added zoom options via hotkeys (#252)
- Zoom in: CTRL + SHIFT + =
- Zoom out: CTRL + SHIFT + -
- Added PLAY context menu options to the Genre view (#239)
- Added STOP button to the main player controls (#252)
- Added "System Notifications" option to display native notifications when the song automatically changes (#245)
- Added arm64 build (#238)
- New languages
- Spanish (Thanks @ami-sc) (#250)
- Sinhala (Thanks @hirusha-adi) (#254)
### Fixed
- (Jellyfin) Fixed the order of returned songs when playing from the Folder view using the context menu (#240)
- (Linux) Reset MPRIS position to 0 when using "previous track" resets the song 0 (#249)
- Fixed JavaScript error when removing all songs from the queue using the context menu (#248)
- Fixed Ampache server support by adding .view to all Subsonic API endpoints (#253)
### Removed
- (Windows) Removed the cover art display when hovering Sonixd on the taskbar (due to new sidebar position) (#242)
[0.13.1] - 2022-02-16
### Fixed
- Fixed startup crash on all OS if the default settings file is not present (#237)
[0.13.0] - 2022-02-16
### Added
- Added new searchbar and search UI (#227, #228)
- Added playback controls to the Sonixd tray menu (#225)
- Added playlist selections to the `Start Page` config option
### Changed
- Sidebar changes (#206)
- Allow resizing of the sidebar when expanded
- Allow a toggle of the playerbar's cover art to the sidebar when expanded
- Display playlist list on the sidebar under the navigation
- Allow configuration of the display of sidebar elements
- Changed the `Artist` row on the playerbar to use a comma delimited list of the song's artists rather than the album artist (#218)
### Fixed
- Fixed the player volume not resetting to its default value when resetting a song while crossfading (#228)
- (Jellyfin) Fixed artist list not displaying user favorites
- (Jellyfin) Fixed `bitrate` column not properly by its numeric value (#220)
- Fixed javascript exception when incrementing/decrementing the queue (#230)
- Fixed popups/tooltips not using the configured font
[0.12.1] - 2022-02-02
### Fixed
- Fixed translation syntax error causing application to crash when deleting playlists from the context menu (#216)
- Fixed Player behavior (#217)
- No longer scrobbles an additional time after the last song ends when repeat is off
- (Jellyfin) Properly handles scrobbling the player's pause/resume and time position
[0.12.0] - 2022-01-31
### Added
- Added support for language/translations (#146) (Thanks @gelaechter)
- German translation added (Thanks @gelaechter)
- Simplified Chinese translation added (Thanks @fangxx3863)
- (Windows) Added media keys with desktop overlay (#79) (Thanks @GermanDarknes)
- (Subsonic) Added support for `/getLyrics` to display the current song's lyrics in a popup (#151)
- (Jellyfin) Added song list page
- Added config to choose the default Album/Song list sort on startup (#169)
- Added config to choose the application start page (#176) (Thanks @GermanDarknes)
- Added config for pagination for Album/Song list pages
- (Windows) Added option to set custom directory on installation (#184)
- Added config to set the default artist page to the album list (#199)
- Added info mode for the Now Playing page (#160)
- Added release notes popup
### Changed
- Player behavior
- `Media Stop` now stops the track and resets it instead of clearing the queue (#200)
- `Media Prev` now resets to the start of the song if pressed after 5 seconds (#207)
- `Media Prev` now resets to the start of the song if repeat is off and is the first song of the queue (#207)
- `Media Next` now does nothing if repeat is off and is the last song of the queue (#207)
- Playing a single track in the queue without repeat no longer plays the track twice (#205)
- Scrobbling
- (Jellyfin) Scrobbling has been reverted to use the `/sessions/playing` endpoint to support the Playback Reporting plugin (#187)
- Scrobbling occurs after 5 seconds has elapsed for the current track as to not instantly mark the song as played
- Pressing `CTRL + F` or the search button now focuses the text in the searchbar (#203) (Thanks @WeekendWarrior1)
- Changed loading indicators for all pages
- OBS scrobble now outputs an image.txt file instead of the downloading the cover image (#136)
- Player Bar
- Album name now appears under the artist
- (Subsonic) 5-star rating is available
- Clicking on the cover art now displays a full-size image
- Clicking on the song name now redirects to the Now Playing queue
- (Jellyfin) Removed track limit for "Auto Playlist"
### Fixed
- (macOS) Fixed macOS exit behavior (#198) (Thanks @zackslash)
- (Linux) Fixed MPRIS `position` result (#162)
- (Subsonic) Fixed artist page crashing the application if server does not support `/getArtistInfo2` (#170)
- (Jellyfin) Fixed `View all songs` returning songs out of their album track order
- (Jellyfin) Fixed the "Latest Albums" on the album artist page displaying no albums
- Fixed card overlay button color on click
- Fixed buttons on the Album page to work better with light mode
- Fixed unfavorite button on Album page
[0.11.0] - 2022-01-01
### Added
- Added external integrations
- Added Discord rich presence to display the currently playing song (#155)
- Added OBS (Open Broadcaster Software) scrobbling to send current track metadata to desktop or the Tuna plugin (#136)
- Added a `Native` option for Titlebar Style (#148) (Thanks @gelaechter)
- (Jellyfin) Added toggle to allow transcoding for non-directplay compatible filetypes (#158)
- Additional MPRIS support
- Added metadata:
- `albumArtist`, `discNumber`, `trackNumber`, `useCount`, `genre`
- Added events:
- `seek`, `position`, `volume`, `repeat`, `shuffle`
### Changed
- Overhauled the Artist page
- (Jellyfin) Split albums by album artist OR compilation
- (Jellyfin) Added artist genres
- (Subsonic) Added Top Songs section
- Moved related artists to the main page scrolling menu
- Added `View All Songs` button to view all songs by the artist
- Added artist radio (mix) button
- Horizontal scrolling menu no longer displays scrollbar
- Changed button styling on Playlist/Album/Artist pages
- Changed page image styling to use the card on Playlist/Album/Artist pages
### Fixed
- Fixed various MPRIS features
- Synchronized the play/pause state between the player and MPRIS client when pausing from Sonixd (#152)
- Fixed the identity of Sonixd to use the app name instead of description (#163)
- Fixed various submenus opening in the right-click context menu when the option is disabled (#164)
- Fixed compatibility with older Subsonic API servers (now targets Subsonic v1.13.0) (#144)
- Fixed playback causing heavily increased CPU/Power usage #145)
[0.10.0] - 2021-12-15
### Added
- Added 2 new default themes
- City Lights
- One Dark
- Added additional album filters (#66)
- Genres (AND/OR)
- Artists (AND/OR)
- Years (FROM/TO)
- Added external column sort filters for multiple pages (#66)
- Added item counter to page titles
- `Play Count` column has been added to albums (only works for Navidrome)
### Changed
- Config page has been fully refreshed to a new look
- Config popover on the action bar now includes all config tabs
- Tooltips
- Increased default tooltip delay from 250ms -> 500ms
- Increased tooltip delay on card overlay buttons to 1000ms
- Grid view
- Placeholder images for playlists, albums, and artists have been updated (inspired from Jellyfin Web UI)
- Card title/subtitle width decreased from 100% to default length
- Separate card info section from image/overlay buttons on hover
- Popovers (config, auto playlist, etc)
- Now have decreased opacity
- Enabling/disabling global media keys no longer requires app restart
### Fixed
- (Jellyfin) Fixed `Recently Played` and `Most Played` filters on the Dashboard page (#114)
- (Jellyfin) Fixed server scrobble (#126)
- No longer sends the `/playing` request on song start (prevents song being marked as played when it starts)
- Fixed song play count increasing multiple times per play
- (Jellyfin) Fixed tracks without embedded art displaying placeholder (#128)
- (Jellyfin) Fixed song `Path` property not displaying data
- (Subsonic) Fixed login check for Funkwhale servers (#135)
- Fixed persistent grid-view scroll position
- Fixed list-view columns
- `Visibility` column now properly displays data
- Selected media folder is now cleared from settings on disconnect (prevents errors when signing into a new server)
- Fixed adding/removing artist as favorite on the Artist page not updating
- Fixed search bar not properly handling Asian keyboard inputs
## [0.9.1] - 2021-12-07
### Changed
- List-view scroll position is now persistent for the following:
- Now Playing
- Playlist list
- Favorites (all)
- Album list
- Artist list
- Genre list
- Grid-view scroll position is now persistent for the following:
- Playlist list
- Favorites (album/artist)
- Album list
- Artist list
- (Jellyfin) Changed audio stream URL to force transcoding off (#108)
### Fixed
- (Jellyfin) Fixed the player not sending the "finish" condition when the song meets the scrobble condition (unresolved from 0.9.0) (#111)
## [0.9.0] - 2021-12-06
### Added
- Added 2 new default themes
- Plex-like
- Spotify-like
- Added volume control improvements
- Volume value tooltip while hovering the slider
- Mouse scroll wheel controls volume while hovering the slider
- Clicking the volume icon will mute/unmute
### Changed
- Overhauled all default themes
- Rounded buttons, inputs, etc.
- Changed grid card hover effects
- Removed hover scale
- Removed default background on overlay buttons
- Moved border to only the image instead of full card
- Album page
- Genre(s) are now listed on a line separate from the artists
- Album artist is now distinct from track artists
- Increased length of the genre/artist line from 70% -> 80%
- The genre/artist line is now scrollable using the mouse wheel
- (Jellyfin) List view
- `Artist` column now uses the album artist property
- `Title (Combined)` column now displays all track artists, comma-delimited instead of the album artist
- `Genre` column now displays all genres, comma-delimited, left-aligned
### Fixed
- (Jellyfin) Fixed the player not sending the "finish" condition when the song meets the scrobble condition
- (Jellyfin) Fixed album lists not sorting by the `genre` column
- (Jellyfin)(API) Fixed the A-Z(Artist) not sorting by Album Artist on the album list
- (Jellyfin)(API) Fixed auto playlist not respecting the selected music folder
- (Jellyfin)(API) Fixed the artist page not respecting the selected music folder
## [0.8.5] - 2021-11-25
### Fixed
- Fixed default (OOBE) title column not display data (#104)
## [0.8.4] - 2021-11-25
### Fixed
- (Jellyfin)(Linux) Fixed JS MPRIS error when switching tracks due to unrounded song duration
- (Linux) Fixed MPRIS artist, genre, and coverart not updating on track change
## [0.8.3] - 2021-11-25
### Fixed
- (Subsonic) Fixed playing a folder from the folder view
- Fixed rating context menu option available from the Genre page
## [0.8.2] - 2021-11-25
### Added
- Added option to disable auto updates
### Fixed
- Fixed gapless playback on certain \*sonic servers (#100)
- Fixed playerbar coverart not redirecting to `Now Playing` page
## [0.8.1] - 2021-11-24
### Fixed
- (Subsonic) Fixed errors blocking playlists from being deleted
## [0.8.0] - 2021-11-24
### Added
- Added Jellyfin server support (#87)
- Supports full Sonixd feature-set (except ratings)
- Added a mini config popover to change list/grid view options on the top action bar
- Added system audio device selector (#96)
- Added context menu option `Set rating` to bulk set ratings for songs (and albums/artists on Navidrome) (#95)
### Changed
- Reduced cached image from 500px -> 350px (to match max grid size)
- Grid/header images now respect image aspect ratio returned by the server
- Playback filter input now uses a regex validation before allowing you to add
- Renamed all `Name` columns to `Title`
- Search bar now clears after pressing enter to globally search
- Added borders to popovers
### Fixed
- Fixed application performance issues when player is crossfading to the next track
- Fixed null entries showing at the beginning of descending sort on playlist/now playing lists
- Tooltips no longer pop up on the artist/playlist description when null
## [0.7.0] - 2021-11-15
### Added
- Added download buttons on the Album and Artist pages (#29)
- Allows you to download (via browser) or copy download links to your clipboard (to use with a download manager)
### Changed
- Changed default tooltip delay from `500ms` -> `250ms`
- Moved search bar from page header to the main layout action bar
- Added notice for macOS media keys to require trusted accessibility in the client
### Fixed
- Fixed auto playlist and album fetch in Gonic servers
- Fixed the macOS titlebar styling to better match the original (#83)
- Fixed thumbnailclip error when resizing the application in macOS (#84)
- Fixed playlist page not using cached image
## [0.6.0] - 2021-11-09
### Added
- Added additional grid-view customization options (#74)
- Gap size (spaces between cards)
- Alignment (left-align, center-align)
### Changed
- Changed default album/artist uncached image sizes from `150px` -> `350px`
### Fixed
- (Windows) Fixed default taskbar thumbnail on Windows10 when minimized to use window instead of album cover (#73)
- Fixed playback settings unable to change via the UI
- Crossfade duration
- Polling interval
- Volume fade
- Fixed header styling on the Config page breaking at smaller window widths (#72)
- Fixed the position of the description tooltip on the Artist page
- Fixed the `Add to playlist` popover showing underneath the modal in modal-view
### Removed
- Removed unused `fonts.size.pageTitle` theme property
## [0.5.0] - 2021-11-05
### Added
- Added extensible theming (#60)
- Added playback presets (gapless, fade, normal) to the config
- Added persistence for column sort for all list-views (except playlist and search) (#47)
- Added playback filters to the config to filter out songs based on regex (#53)
- Added music folder selector in auto playlist (this may or may not work depending on your server)
- Added improved playlist, artist, and album pages
- Added dynamic images on the Playlist page for servers that don't support playlist images (e.g. Navidrome)
- Added link to open the local `settings.json` file
- Added setting to use legacy authentication (#63)
### Changed
- Improved overall application keyboard accessibility
- Playback no longer automatically starts if adding songs to the queue using `Add to queue`
- Prevent accidental page navigation when using [Ctrl/Shift + Click] when multi-selecting rows in list-view
- Standardized buttons between the Now Playing page and the mini player
- "Add random" renamed to "Auto playlist"
- Increased 'info' notification timeout from 1500ms -> 2000ms
- Changed default mini player columns to better fit
- Updated default themes to more modern standards (Default Dark, Default Light)
### Fixed
- Fixed title sort on the `Title (Combined)` column on the album list
- Fixed 2nd song in queue being skipped when using the "Play" button multiple pages (album, artist, auto playlist)
- Fixed `Title` column not showing the title on the Folder page (#69)
- Fixed context menu windows showing underneath the mini player
- Fixed `Add to queue (next)` adding songs to the wrong unshuffled index when shuffle is enabled
- Fixed local search on the root Folder page
- Fixed input picker dropdowns following the page on scroll
- Fixed the current playing song not highlighted when using `Add to queue` on an empty play queue
- Fixed artist list not using the `artistImageUrl` returned by Navidrome
## [0.4.1] - 2021-10-27
### Added
- Added links to the genre column on the list-view
- Added page forward/back buttons to main layout
### Changed
- Increase delay when completing mouse drag select in list view from `100ms` -> `200ms`
- Change casing for main application name `sonixd` -> `Sonixd`
### Fixed
- Fixed Linux media hotkey support (MPRIS)
- Added commands for additional events `play` and `pause` (used by KDE's media player overlay)
- Set status to `Playing` when initially starting a song
- Set current song metadata when track automatically changes instead of only when it manually changes
- Fixed filtered link to Album List on the Album page
- Fixed filtered link to Album List on the Dashboard page
- Fixed font color for lists/tables in panels
- Affects the search view song list and column selector list
## [0.4.0] - 2021-10-26
### Added
- Added music folder selector (#52)
- Added media hotkeys / MPRIS support for Linux (#50)
- This is due to dbus overriding the global shortcuts that electron sends
- Added advanced column selector component
- Drag-n-drop list
- Individual resizable columns
- (Windows) Added tray (Thanks @ncarmic4) (#45)
- Settings to minimize/exit to tray
### Changed
- Page selections are now persistent
- Active tab on config page
- Active tab on favorites page
- Filter selector on album list page
- Playlists can now be saved after being sorted using column filters
- Folder view
- Now shows all root folders in the list instead of in the input picker
- Now shows music folders in the input picker
- Now uses loader when switching pages
- Changed styling for various views/components
- Look & Feel setting page now split up into multiple panels
- Renamed context menu button `Remove from current` -> `Remove selected`
- Page header titles width increased from `45%` -> `80%`
- Renamed `Scan library` -> `Scan`
- All pages no longer refetch data when clicking back into the application
### Fixed
- Fixed shift-click multi select on a column-sorted list-view
- Fixed right-click context menu showing up behind all modals (#55)
- Fixed mini player showing up behind tag picker elements
- Fixed duration showing up as `NaN:NaN` when duration is null or invalid
- Fixed albums showing as a folder in Navidrome instances
## [0.3.0] - 2021-10-16
### Added
- Added folder browser (#1)
- Added context menu button `View in folder`
- Requires that your server has support for the original `/getIndexes` and `/getMusicDirectory` endpoints
- Added configurable row-hover highlight for list-view
- (Windows) Added playback controls in thumbnail toolbar (#32)
- (Windows/macOS) Added window size/position remembering on application close (#31)
### Changed
- Changed styling for various views/components
- Tooltips added on grid-view card hover buttons
- Mini-player removed rounded borders and increased opacity
- Mini-player removed animation on open/close
- Search bar now activated from button -> input on click / CTRL+F
- Page header toolbar buttons styling consistency
- Album list filter moved from right -> left
- Reordered context menu button `Move selected to [...]`
- Decreased horizontal width of expanded sidebar from 193px -> 165px
### Fixed
- Fixed duplicate scrobble requests when pause/resuming a song after the scrobble threshold (#30)
- Fixed genre column not applying in the song list-view
- Fixed default titlebar set on first run
## [0.2.1] - 2021-10-11
### Fixed
- Fixed using play buttons on the artist view not starting playback
- Fixed favoriting on horizontal scroll menu on dashboard/search views
- Fixed typo on default artist list viewtype
- Fixed artist image selection on artist view
## [0.2.0] - 2021-10-11
### Added
- Added setting to enable scrobbling playing/played tracks to your server (#17)
- Added setting to change between macOS and Windows styled titlebar (#23)
- Added app/build versions and update checker on the config page (#18)
- Added 'view in modal' button on the list-view context menu (#8)
- Added a persistent indicator on grid-view cards for favorited albums/artists (#7)
- Added buttons for 'Add to queue (next)' and 'Add to queue (later)' (#6)
- Added left/right scroll buttons to the horizontal scrolling menu (dashboard/search)
- Added last.fm link to artist page
- Added link to cache location to open in local file explorer
- Added reset to default for cache location
- Added additional tooltips
- Grid-view card title and subtitle buttons
- Cover art on the player bar
- Header titles on album/artist pages
### Changed
- Changed starring logic on grid-view card to update local cache instead of refetch
- Changed styling for various views/components
- Use dynamically sized hover buttons on grid-view cards depending on the card size
- Decreased size of buttons on album/playlist/artist pages
- Input picker text color changed from primary theme color to primary text color
- Crossfade type config changed from radio buttons to input picker
- Disconnect button color from red to default
- Tooltip styling updated to better match default theme
- Changed tag links to text links on album page
- Changed page header images to use cache (album/artist)
- Artist image now falls back to last.fm if no local image
### Fixed
- Fixed song & image caching (#16)
- Fixed set default artist list view type on first startup
## [0.1.0] - 2021-10-06
### Added
- Initial release
+51
View File
@@ -0,0 +1,51 @@
# Stage 1 - Build frontend
FROM node:16.5-alpine as ui-builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build:renderer
RUN npm prune --production
RUN npm cache clean --force
RUN rm -rf /root/.cache
# Stage 2 - Build server
FROM node:16.5-alpine as server-builder
WORKDIR /app
COPY server .
RUN npm install && npx prisma generate
RUN npm run build
RUN npm prune --production
RUN npm cache clean --force
RUN rm -rf /root/.cache
# Stage 3 - Deploy
FROM node:16.5-alpine
WORKDIR /root
RUN mkdir appdata
RUN mkdir feishin-server
RUN mkdir feishin-client
RUN npm cache clean --force
RUN npm prune --production
# Add server build files
COPY --from=server-builder /app/dist ./feishin-server
COPY --from=server-builder /app/node_modules ./feishin-server/node_modules
COPY --from=server-builder /app/prisma ./feishin-server/prisma
# Add client build files
COPY --from=ui-builder /app/release/app/dist/renderer ./feishin-client
COPY docker-entrypoint.sh ./feishin-server/docker-entrypoint.sh
RUN chmod +x ./feishin-server/docker-entrypoint.sh
COPY ./server/wait-for-it.sh ./feishin-server/wait-for-it.sh
RUN chmod +x ./feishin-server/wait-for-it.sh
RUN npm install pm2 -g
WORKDIR /root/feishin-server
EXPOSE 9321
CMD ["sh", "docker-entrypoint.sh"]
+55 -18
View File
@@ -1,19 +1,3 @@
Copyright (C) 2022 jeffvli
This program 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 3 of the License, or
(at your option) any later version.
This program 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/>.
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 29 June 2007
@@ -86,7 +70,7 @@ modification follow.
TERMS AND CONDITIONS TERMS AND CONDITIONS
1. Definitions. 0. Definitions.
"This License" refers to version 3 of the GNU General Public License. "This License" refers to version 3 of the GNU General Public License.
@@ -634,4 +618,57 @@ an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee. copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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 3 of the License, or
(at your option) any later version.
This program 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/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+162 -282
View File
@@ -1,305 +1,185 @@
# Vite Electron Builder Boilerplate # Feishin
<p align="center">
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua) <a href="https://github.com/jeffvli/feishin/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/jeffvli/feishin?style=flat-square&color=brightgreen"
[![GitHub issues by-label](https://img.shields.io/github/issues/cawa-93/vite-electron-builder/help%20wanted?label=issues%20need%20help&logo=github)](https://github.com/cawa-93/vite-electron-builder/issues?q=label%3A%22help+wanted%22+is%3Aopen+is%3Aissue) alt="License">
[![Required Node.JS >= v16.13](https://img.shields.io/static/v1?label=node&message=%3E=16.13&logo=node.js&color)](https://nodejs.org/about/releases/) </a>
[![Required npm >= v8.1](https://img.shields.io/static/v1?label=npm&message=%3E=8.1&logo=npm&color)](https://github.com/npm/cli/releases) <a href="https://github.com/jeffvli/feishin/releases">
<img src="https://img.shields.io/github/v/release/jeffvli/feishin?style=flat-square&color=blue"
> Vite+Electron = 🔥 alt="Release">
</a>
This is a template for secure electron applications. Written following the latest safety requirements, recommendations <a href="https://github.com/jeffvli/feishin/releases">
and best practices. <img src="https://img.shields.io/github/downloads/jeffvli/feishin/total?style=flat-square&color=orange"
alt="Downloads">
Under the hood is [Vite] — A next-generation blazing fast bundler, and [electron-builder] for packaging. </a>
<a href="https://hub.docker.com/r/jeffvictorli/feishin">
## Get started <img src="https://img.shields.io/docker/v/jeffvictorli/feishin?style=flat-square&color=orange"
alt="Docker">
Follow these steps to get started with the template: </a>
</a>
1. Click the **[Use this template](https://github.com/cawa-93/vite-electron-builder/generate)** button (you must be <a href="https://hub.docker.com/r/jeffvictorli/feishin">
logged in) or just clone this repo. <img src="https://img.shields.io/docker/pulls/jeffvictorli/feishin?style=flat-square&color=orange"
2. If you want to use another package manager don't forget to edit [`.github/workflows`](/.github/workflows) -- it alt="Docker pulls">
uses `npm` by default. </a>
</p>
That's all you need. 😉 <p align="center">
<a href="https://discord.gg/FVKpcMDy5f">
**Note**: This template uses npm v7 feature — [**Installing Peer Dependencies <img src="https://img.shields.io/discord/922656312888811530?color=black&label=discord&logo=discord&logoColor=white"
Automatically**](https://github.com/npm/rfcs/blob/latest/implemented/0025-install-peer-deps.md). If you are using a alt="Discord">
different package manager, you may need to install some peerDependencies manually. </a>
<a href="https://matrix.to/#/#sonixd:matrix.org">
**Note**: Find more useful forks [here](https://github.com/cawa-93/vite-electron-builder/discussions/categories/forks). <img src="https://img.shields.io/matrix/sonixd:matrix.org?color=black&label=matrix&logo=matrix&logoColor=white"
alt="Matrix">
## Features </a>
</p>
### Electron [![Electron version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/electron?label=%20)][electron]
Repository for the rewrite of [Sonixd](https://github.com/jeffvli/sonixd).
- This template uses the latest electron version with all the latest security patches.
- The architecture of the application is built according to the ## Getting Started
security [guides](https://www.electronjs.org/docs/tutorial/security) and best practices.
- The latest version of the [electron-builder] is used to package the application. The default credentials to login will be `admin/admin`.
### Vite [![Vite version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/vite?label=%20)][vite] Download the [latest desktop client](https://github.com/jeffvli/feishin/releases).
- [Vite] is used to bundle all source codes. It's an extremely fast bundler, that has a vast array of amazing features. ### Docker Compose
You can learn more about how it is arranged in [this](https://www.youtube.com/watch?v=xXrhg26VCSc) video.
- Vite [supports](https://vitejs.dev/guide/env-and-mode.html) reading `.env` files. You can also specify the types of **Warning:** Check the environment variable configuration before running the commands below.
your environment variables in [`types/env.d.ts`](types/env.d.ts).
- Automatic hot-reloads for the `Main` and `Renderer` processes. 1. Copy and rename [example.env](https://github.com/jeffvli/feishin/blob/dev/example.env) to `.env` and make any changes necessary
2. Run the compose file: `docker compose --file docker-compose.yml --env-file .env up`
Vite provides many useful features, such as: `TypeScript`, `TSX/JSX`, `CSS/JSON Importing`, `CSS Modules`
, `Web Assembly` and much more. ### Docker
[See all Vite features](https://vitejs.dev/guide/features.html). **Warning:** Check the environment variable configuration before running the commands below.
### TypeScript [![TypeScript version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/typescript?label=%20)][typescript] (optional) **Run a postgres database container:**
- The latest version of TypeScript is used for all the source code.
- **Vite** supports TypeScript out of the box. However, it does not support type checking.
- Code formatting rules follow the latest TypeScript recommendations and best practices thanks
to [@typescript-eslint/eslint-plugin](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin).
**[See this discussion](https://github.com/cawa-93/vite-electron-builder/discussions/339)** if you want completely
remove TypeScript.
### Vue [![Vue version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/vue?label=%20&)][vue] (optional)
- By default, web pages are built using [Vue]. However, you can easily change that. Or not use additional frameworks at
all.
- Code formatting rules follow the latest Vue recommendations and best practices thanks to [eslint-plugin-vue].
See [examples of web pages for different frameworks](https://github.com/vitejs/vite/tree/main/packages/create-vite).
### Continuous Integration
- The configured workflow will check the types for each push and PR.
- The configured workflow will check the code style for each push and PR.
- **Automatic tests**
used [Vitest ![Vitest version](https://img.shields.io/github/package-json/dependency-version/cawa-93/vite-electron-builder/dev/vitest?label=%20&color=yellow)][vitest]
-- A blazing fast test framework powered by Vite.
- Unit tests are placed within each package and are ran separately.
- End-to-end tests are placed in the root [`tests`](tests) directory and use [playwright].
![зображення](https://user-images.githubusercontent.com/1662812/204089645-d0209ce2-7259-4b66-ac71-22a7f608be35.png)
### Publishing
- Each time you push changes to the `main` branch, the [`release`](.github/workflows/release.yml) workflow starts, which creates a new draft release. For each next commit will be created and replaced artifacts. That way you will always have draft with latest artifacts, and the release can be published once it is ready.
- Code signing supported. See [`release` workflow](.github/workflows/release.yml).
- **Auto-update is supported**. After the release is published, all client applications will download the new version
and install updates silently.
This template configured for GitHub, but electron-builder supports multiple auto-update servers. See [docs](https://www.electron.build/configuration/publish).
## How it works
The template requires a minimum amount [dependencies](package.json). Only **Vite** is used for building, nothing more.
### Project Structure
The structure of this template is very similar to the structure of a monorepo.
```mermaid
flowchart TB;
packages/preload <-. IPC Messages .-> packages/main
subgraph packages/main
M[index.ts] --> EM[Electron Main Process Modules]
M --> N2[Node.js API]
end
subgraph packages/preload
P[index.ts] --> N[Node.js API]
P --> ED[External dependencies]
P --> ER[Electron Renderer Process Modules]
end
subgraph packages/renderer
R[index.html] --> W[Web API]
R --> BD[Bundled dependencies]
R --> F[Web Frameforks]
end
packages/renderer -- Call Exposed API --> P
```
The entire source code of the project is divided into three modules (packages) that are each bundled independently:
- [`packages/renderer`](packages/renderer). Responsible for the contents of the application window. In fact, it is a
regular web application. In developer mode, you can even open it in a browser. The development and build process is
the same as for classic web applications. Access to low-level API electrons or Node.js is done through the _preload_
layer.
- [`packages/preload`](packages/preload). Acts as an intermediate bridge between the _renderer_ process and the API
exposed by electron and Node.js. Runs in an _isolated browser context_, but has direct access to the full Node.js
functionality.
See [Checklist: Security Recommendations](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content)
.
- [`packages/main`](packages/main)
Electron [**main script**](https://www.electronjs.org/docs/tutorial/quick-start#create-the-main-script-file). This is
the main process that powers the application. It manages creating and handling the spawned BrowserWindow, setting and
enforcing secure permissions and request handlers. You can also configure it to do much more as per your need, such
as: logging, reporting statistics and health status among others.
### Build web resources
The `main` and `preload` packages are built in [library mode](https://vitejs.dev/guide/build.html#library-mode) as it is
simple javascript.
The `renderer` package builds as a regular web app.
### Compile App
The next step is to package a ready to distribute Electron app for macOS, Windows and Linux with "auto update" support
out of the box.
To do this, use [electron-builder]:
- Using the npm script `compile`: This script is configured to compile the application as quickly as possible. It is not
ready for distribution, it is compiled only for the current platform and is used for debugging.
- Using GitHub Actions: The application is compiled for any platform and ready-to-distribute files are automatically
added as a draft to the GitHub releases page.
### Working with dependencies
Because the `renderer` works and builds like a _regular web application_, you can only use dependencies that support the
browser or compile to a browser-friendly format.
This means that in the `renderer` you are free to use any frontend dependencies such as Vue, React, lodash, axios and so
on.However, you _CANNOT_ use any native Node.js APIs, such as, `systeminformation`. These APIs are _only_ available in
a Node.js runtime environment and will cause your application to crash if used in the `renderer` layer. Instead, if you
need access to Node.js runtime APIs in your frontend, export a function form the `preload` package.
All dependencies that require Node.js api can be used in
the [`preload` script](https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts).
#### Expose in main world
Here is an example. Let's say you need to read some data from the file system or database in the renderer.
In the preload context, create a function that reads and returns data. To make the function announced in the preload
available in the render, you usually need to call
the [`electron.contextBridge.exposeInMainWorld`](https://www.electronjs.org/ru/docs/latest/api/context-bridge). However,
this template uses the [unplugin-auto-expose](https://github.com/cawa-93/unplugin-auto-expose) plugin, so you just need
to export the method from the preload. The `exposeInMainWorld` will be called automatically.
```ts
// preload/index.ts
import { readFile } from 'node:fs/promises';
// Encapsulate types if you use typescript
interface UserData {
prop: string
}
// Encapsulate all node.js api
// Everything you exported from preload/index.ts may be called in renderer
export function getUserData(): Promise<UserData> {
return readFile('/path/to/file/in/user/filesystem.json', {encoding:'utf8'}).then(JSON.parse);
}
```
Now you can import and call the method in renderer
```ts
// renderer/anywere/component.ts
import { getUserData } from '#preload'
const userData = await getUserData()
```
[Read more about Security Considerations](https://www.electronjs.org/docs/tutorial/context-isolation#security-considerations).
### Working with Electron API
Although the preload has access to all of Node.js's API, it **still runs in the BrowserWindow context**, so a limited
electron modules are available in it. Check the [electron docs](https://www.electronjs.org/ru/docs/latest/api/clipboard)
for full list of available methods.
All other electron methods can be invoked in the `main`.
As a result, the architecture of interaction between all modules is as follows:
```mermaid
sequenceDiagram
renderer->>+preload: Read data from file system
preload->>-renderer: Data
renderer->>preload: Maximize window
activate preload
preload-->>main: Invoke IPC command
activate main
main-->>preload: IPC response
deactivate main
preload->>renderer: Window maximized
deactivate preload
```
[Read more about Inter-Process Communication](https://www.electronjs.org/docs/latest/tutorial/ipc)
### Modes and Environment Variables
All environment variables are set as part of the `import.meta`, so you can access them vie the following
way: `import.meta.env`.
If you are using TypeScript and want to get code completion you must add all the environment variables to
the [`ImportMetaEnv` in `types/env.d.ts`](types/env.d.ts).
The mode option is used to specify the value of `import.meta.env.MODE` and the corresponding environment variables files
that need to be loaded.
By default, there are two modes:
- `production` is used by default
- `development` is used by `npm run watch` script
When running the build script, the environment variables are loaded from the following files in your project root:
``` ```
.env # loaded in all cases docker run postgres:13 \
.env.local # loaded in all cases, ignored by git -p 5432:5432 \
.env.[mode] # only loaded in specified env mode -e POSTGRES_USER=admin \
.env.[mode].local # only loaded in specified env mode, ignored by git -e POSTGRES_PASSWORD=admin \
-e POSTGRES_DB=feishin
``` ```
To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed to your **Run the Feishin server container:**
Vite-processed code.
For example let's take the following `.env` file:
``` ```
DB_PASSWORD=foobar docker run jeffvictorli/feishin:latest \
VITE_SOME_KEY=123 -p 8643:9321 \
-e APP_BASE_URL=http://192.168.0.1:8643 \
-e DATABASE_PORT=5432 \
-e DATABASE_URL=postgresql://admin:admin@localhost:5432/feishin?schema=public \
-e TOKEN_SECRET=secret
``` ```
Only `VITE_SOME_KEY` will be exposed as `import.meta.env.VITE_SOME_KEY` to your client source code, but `DB_PASSWORD` **Docker Environment Variables**
will not.
## Contribution ```
APP_BASE_URL — The URL the site will be accessible at from your server (needed for CORS)
See [Contributing Guide](contributing.md). DATABASE_PORT — The port of your running postgres container
DATABASE_URL — The connection string to your postgres instance following this format: postgresql://<DB_USERNAME>:<DB_PASSWORD>@<DB_URL>/<DB_NAME>?schema=public
[vite]: https://github.com/vitejs/vite/ Replace the following:
<DB_USERNAME> — The admin username of your postgres container (POSTGRES_USER)
<DB_PASSWORD> — The admin password of your postgres container (POSTGRES_PASSWORD)
<DB_NAME> — The name of the database created in your postgres container (POSTGRES_DB)
<DB_URL> — The URL the postgres container is reachable from
[electron]: https://github.com/electron/electron Example: postgresql://admin:password@192.168.0.1:5432/feishin?schema=public
[electron-builder]: https://github.com/electron-userland/electron-builder TOKEN_SECRET — The string used to sign auth tokens
[vue]: https://github.com/vuejs/vue-next (optional) TOKEN_EXPIRATION — The time before the auth JWT expires
[vue-router]: https://github.com/vuejs/vue-router-next/ (optional) TOKEN_REFRESH_EXPIRATION - The time before the auth JWT refresh token expires
```
[typescript]: https://github.com/microsoft/TypeScript/ ### After installing the server and database
[playwright]: https://playwright.dev You can access the desktop client via the [latest release](https://github.com/jeffvli/feishin/releases), or you can visit the web client at your server URL (e.g http://192.168.0.1:8643).
[vitest]: https://vitest.dev ## FAQ
[vue-tsc]: https://github.com/johnsoncodehk/vue-tsc ### Why is there a red lock next to the server I want to select?
[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue If the server is specified to "require user credentials", you will need to add and enable your own credentials to access it. Since the songs and images aren't proxied by the Feishin backend, the server credentials would otherwise be leaked to any user that has access to it. The added credentials are stored locally in the browser and are then used to generate the audio and image URLs in the client.
[cawa-93-github]: https://github.com/cawa-93/ ### What music servers does Feishin support?
[cawa-93-sponsor]: https://www.patreon.com/Kozack/ Feishin supports any music server that implements a [Subsonic](http://www.subsonic.org/pages/api.jsp), [Navidrome](https://www.navidrome.org/), or [Jellyfin](https://jellyfin.org/) API.
- [Jellyfin](https://github.com/jellyfin/jellyfin)
- [Navidrome](https://github.com/navidrome/navidrome)
- [Airsonic](https://github.com/airsonic/airsonic)
- [Airsonic-Advanced](https://github.com/airsonic-advanced/airsonic-advanced)
- [Gonic](https://github.com/sentriz/gonic)
- [Astiga](https://asti.ga/)
- [Supysonic](https://github.com/spl0k/supysonic)
### Why does Feishin use its own database and backend instead of just use (insert music server)'s API?
Feishin was an idea I had after I ran into usage limitations while building out [Sonixd](https://github.com/jeffvli/sonixd). Each music server has their own quirks, and I decided I wanted to consolidate and extend their features with my own backend implemntation which includes: web/desktop clients, advanced filtering, smart playlists, desktop MPV player, and more.
### Can I use (insert database) instead of Postgresql?
Due to [Prisma limitations](https://www.prisma.io/docs/concepts/components/prisma-migrate/prisma-migrate-limitations-issues#you-cannot-automatically-switch-database-providers), there is no easy way to switch to a different database provider at this time.
## Development
Built and tested using Node `v16.15.0`.
This project is built off of [electron-react-boilerplate](https://github.com/electron-react-boilerplate/electron-react-boilerplate) v4.6.0.
### Developing with Docker Compose
1. Copy and rename the `example.env` to `.env.dev` and make any changes necessary
2. **Run the server**: Use `npm run docker:up` to build and run the dev server
1. Prisma studio available on `http://localhost:5555`
2. Server available on `http://localhost:8643`
3. Default seeded login credentials are `admin/admin`
3. **Run the client**: Use `npm run start` to run the development Electron client
1. The web version of the client is available on `http://localhost:4343`
**Docker Compose files**
```
docker-compose.yml — The public compose file for running the latest release
docker-compose.dev.yml - Build and run the development environment locally (includes Prisma studio)
docker-compose.prod.yml - Build and run the production environment locally
```
### NPM Scripts:
```
$ npm run package — Packages the application for the local system
$ npm run start — Runs the development Electron and web client
$ npm run start:web — Runs the development web client
$ npm run docker:up — Builds and starts the docker development environment using the 'docker-compose.dev.yml' file
$ npm run docker:down — Stops the running docker development environment
$ npm run docker:dbpush — Pushes any schema changes made in 'schema.prisma' to the docker development database without migrating
$ npm run docker:migrate - Migrates any schema changes made in 'schema.prisma' and creates a migration file
$ npm run docker:createmigrate - Creates a migration file for any schema changes made in 'schema.prisma' without applying the migration
$ npm run docker:reset - Resets the docker development database and applies the default seed
$ npm run prod:buildserver - Builds and tags the server docker images locally with the 'latest' and '$VERSION' tags
$ npm run prod:publishserver - Pushes the locally build server docker images to docker hub
```
## License
[GNU General Public License v3.0 ©](https://github.com/jeffvli/sonixd-rewrite/blob/dev/LICENSE)
+31
View File
@@ -0,0 +1,31 @@
type Styles = Record<string, string>;
declare module '*.svg' {
const content: string;
export default content;
}
declare module '*.png' {
const content: string;
export default content;
}
declare module '*.jpg' {
const content: string;
export default content;
}
declare module '*.scss' {
const content: Styles;
export default content;
}
declare module '*.sass' {
const content: Styles;
export default content;
}
declare module '*.css' {
const content: Styles;
export default content;
}
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>
Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

-34
View File
@@ -1,34 +0,0 @@
# Contributing
First and foremost, thank you! We appreciate that you want to contribute to vite-electron-builder, your time is
valuable, and your contributions mean a lot to us.
## Issues
Do not create issues about bumping dependencies unless a bug has been identified, and you can demonstrate that it
effects this library.
**Help us to help you**
Remember that were here to help, but not to make guesses about what you need help with:
- Whatever bug or issue you're experiencing, assume that it will not be as obvious to the maintainers as it is to you.
- Spell it out completely. Keep in mind that maintainers need to think about _all potential use cases_ of a library.
It's important that you explain how you're using a library so that maintainers can make that connection and solve the
issue.
_It can't be understated how frustrating and draining it can be to maintainers to have to ask clarifying questions on
the most basic things, before it's even possible to start debugging. Please try to make the best use of everyone's time
involved, including yourself, by providing this information up front._
## Repo Setup
The package manager used to install and link dependencies must be npm v7 or later.
1. Clone repo
1. `npm run watch` start electron app in watch mode.
1. `npm run compile` build app but for local debugging only.
1. `npm run lint` lint your code.
1. `npm run typecheck` Run typescript check.
1. `npm run test` Run app test.
1. `npm run format` Reformat all codebase to project code style.
+49
View File
@@ -0,0 +1,49 @@
version: '3'
services:
db:
container_name: feishin_db
image: postgres:13
volumes:
- ${DATABASE_PERSIST_PATH}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${DATABASE_USERNAME}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}
ports:
- '${DATABASE_PORT}:5432'
restart: unless-stopped
server:
container_name: feishin_server
volumes:
- ./server:/app # Synchronise docker container with local change
- /app/node_modules # Avoid re-copying local node_modules. Cache in container.
build:
context: ./server
dockerfile: Dockerfile
depends_on:
- db
environment:
- APP_BASE_URL=${APP_BASE_URL}
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public&connection_limit=14&pool_timeout=20
- DATABASE_PORT=${DATABASE_PORT}
- TOKEN_SECRET=${TOKEN_SECRET}
- TOKEN_EXPIRATION=${TOKEN_EXPIRATION}
- TOKEN_REFRESH_EXPIRATION=${TOKEN_REFRESH_EXPIRATION}
ports:
- '8643:9321'
restart: unless-stopped
prisma:
container_name: feishin_prisma_studio
volumes:
- ./server/prisma:/app/prisma
build:
context: ./server/prisma
dockerfile: Dockerfile
depends_on:
- db
- server
environment:
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public
ports:
- '5555:5555'
restart: unless-stopped
+32
View File
@@ -0,0 +1,32 @@
version: '3'
services:
db:
container_name: feishin_db
image: postgres:13
volumes:
- ${DATABASE_PERSIST_PATH}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${DATABASE_USERNAME}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}
ports:
- '${DATABASE_PORT}:5432'
restart: unless-stopped
server:
container_name: feishin
build:
context: .
dockerfile: Dockerfile
image: feishin
depends_on:
- db
environment:
- APP_BASE_URL=${APP_BASE_URL}
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public&connection_limit=14&pool_timeout=20
- DATABASE_PORT=${DATABASE_PORT}
- TOKEN_SECRET=${TOKEN_SECRET}
- TOKEN_EXPIRATION=${TOKEN_EXPIRATION}
- TOKEN_REFRESH_EXPIRATION=${TOKEN_REFRESH_EXPIRATION}
ports:
- '8643:9321'
restart: unless-stopped
+29
View File
@@ -0,0 +1,29 @@
version: '3'
services:
db:
container_name: feishin_db
image: postgres:13
volumes:
- ${DATABASE_PERSIST_PATH}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${DATABASE_USERNAME}
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
- POSTGRES_DB=${DATABASE_NAME}
ports:
- '${DATABASE_PORT}:5432'
restart: unless-stopped
server:
container_name: feishin
image: jeffvictorli/feishin:latest
depends_on:
- db
environment:
- APP_BASE_URL=${APP_BASE_URL}
- DATABASE_URL=postgresql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@db/${DATABASE_NAME}?schema=public&connection_limit=14&pool_timeout=20
- DATABASE_PORT=${DATABASE_PORT}
- TOKEN_SECRET=${TOKEN_SECRET}
- TOKEN_EXPIRATION=${TOKEN_EXPIRATION}
- TOKEN_REFRESH_EXPIRATION=${TOKEN_REFRESH_EXPIRATION}
ports:
- '8643:9321'
restart: unless-stopped
+5
View File
@@ -0,0 +1,5 @@
./wait-for-it.sh db:$1 --timeout=20 --strict -- echo "db is up"
npx prisma migrate deploy
npx ts-node prisma/seed.ts
pm2-runtime server.js
+9
View File
@@ -0,0 +1,9 @@
DATABASE_USERNAME=admin
DATABASE_PASSWORD=admin
DATABASE_NAME=feishin
DATABASE_PORT=5432
DATABASE_PERSIST_PATH=C:/docker/feishin/db
TOKEN_SECRET=SUPERSECRET
TOKEN_EXPIRATION=30m
TOKEN_REFRESH_EXPIRATION=90d
APP_BASE_URL=http://localhost:8643
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Alex Kozack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+17993 -6858
View File
File diff suppressed because it is too large Load Diff
+283 -78
View File
@@ -1,52 +1,205 @@
{ {
"name": "feishin", "name": "feishin",
"description": "A full featured Jellyfin/Subsonic/Navidrome music player.", "productName": "Feishin",
"version": "1.0.0-alpha1", "description": "Feishin music server",
"version": "0.0.1-alpha1",
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"lint:styles": "npx stylelint **/*.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electron -r ts-node/register/transpile-only ./src/main/main.ts",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"start:web": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.web.ts",
"test": "jest",
"prepare": "husky install",
"i18next": "i18next -c src/renderer/i18n/i18next-parser.config.js",
"prod:buildserver": "pwsh -c \"./scripts/server-build.ps1\"",
"prod:publishserver": "pwsh -c \"./scripts/server-publish.ps1\"",
"docker:up": "docker compose --file docker-compose.dev.yml --env-file .env.dev up --detach && docker compose --file docker-compose.dev.yml --env-file .env.dev logs -f",
"docker:down": "docker compose --file docker-compose.dev.yml --env-file .env.dev down && docker image rm feishin_prisma",
"docker:dbpush": "cd server && npx prisma generate && docker exec -ti feishin_server sh -c \"npx prisma generate && npx prisma db push\"",
"docker:migrate": "cd server && npx prisma generate && docker exec -ti feishin_server sh -c \"npx prisma migrate dev\"",
"docker:createmigrate": "cd server && npx prisma generate && docker exec -ti feishin_server sh -c \"npx prisma migrate dev --create-only\"",
"docker:reset": "docker exec -ti feishin_server sh -c \"npx prisma migrate reset && npx prisma db push && npx ts-node prisma/seed.ts\""
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"cross-env NODE_ENV=development eslint --cache"
],
"*.json,.{eslintrc,prettierrc}": [
"prettier --ignore-path .eslintignore --parser json --write"
],
"*.{css,scss}": [
"prettier --ignore-path .eslintignore --single-quote --write"
],
"*.{html,md,yml}": [
"prettier --ignore-path .eslintignore --single-quote --write"
]
},
"build": {
"productName": "Feishin",
"appId": "org.jeffvli.feishin",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"asar": true,
"asarUnpack": "**\\*.{node,dll}",
"files": [
"dist",
"node_modules",
"package.json"
],
"afterSign": ".erb/scripts/notarize.js",
"mac": {
"target": {
"target": "default",
"arch": [
"arm64",
"x64"
]
},
"type": "distribution",
"hardenedRuntime": true,
"entitlements": "assets/entitlements.mac.plist",
"entitlementsInherit": "assets/entitlements.mac.plist",
"gatekeeperAssess": false
},
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis",
"zip"
]
},
"linux": {
"target": [
"AppImage",
"tar.xz"
],
"icon": "assets/icons/placeholder.png",
"category": "Development"
},
"directories": {
"app": "release/app",
"buildResources": "assets",
"output": "release/build"
},
"extraResources": [
"./assets/**"
],
"publish": {
"provider": "github",
"owner": "jeffvli",
"repo": "feishin"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/jeffvli/feishin.git"
},
"author": { "author": {
"name": "jeffvli", "name": "jeffvli",
"email": "jeffvictorli@gmail.com", "url": "https://github.com/jeffvli/"
"url": "https://github.com/jeffvli"
}, },
"main": "packages/main/dist/index.cjs", "contributors": [],
"scripts": { "license": "GPL-3.0",
"build": "npm run build:main && npm run build:preload && npm run build:renderer", "bugs": {
"build:main": "cd ./packages/main && vite build", "url": "https://github.com/jeffvli/feishin/issues"
"build:preload": "cd ./packages/preload && vite build", },
"build:renderer": "cd ./packages/renderer && vite build", "keywords": [
"compile": "cross-env MODE=production npm run build && electron-builder build --config .electron-builder.config.js --dir", "subsonic",
"test": "npm run test:main && npm run test:preload && npm run test:renderer && npm run test:e2e", "navidrome",
"test:e2e": "npm run build && vitest run", "airsonic",
"test:main": "vitest run -r packages/main --passWithNoTests", "jellyfin",
"test:preload": "vitest run -r packages/preload --passWithNoTests", "react",
"test:renderer": "vitest run -r packages/renderer --passWithNoTests", "electron"
"watch": "node scripts/watch.mjs", ],
"lint": "eslint . --ext js,mjs,cjs,ts,mts,cts,tsx", "homepage": "https://github.com/jeffvli/feishin",
"typecheck:main": "tsc --noEmit -p packages/main/tsconfig.json", "jest": {
"typecheck:preload": "tsc --noEmit -p packages/preload/tsconfig.json", "testURL": "http://localhost/",
"typecheck:renderer": "tsc --noEmit -p packages/renderer/tsconfig.json", "testEnvironment": "jsdom",
"typecheck": "npm run typecheck:main && npm run typecheck:preload && npm run typecheck:renderer", "transform": {
"postinstall": "cross-env ELECTRON_RUN_AS_NODE=1 electron scripts/update-electron-vendors.mjs", "\\.(ts|tsx|js|jsx)$": "ts-jest"
"format": "npx prettier --write \"**/*.{js,mjs,cjs,ts,mts,cts,tsx,json}\"" },
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/.erb/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js",
"jsx",
"ts",
"tsx",
"json"
],
"moduleDirectories": [
"node_modules",
"release/app/node_modules"
],
"testPathIgnorePatterns": [
"release/app/dist"
],
"setupFiles": [
"./.erb/scripts/check-build-exists.ts"
]
}, },
"devDependencies": { "devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
"@stylelint/postcss-css-in-js": "^0.38.0",
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.0.0",
"@types/electron-localshortcut": "^3.1.0", "@types/electron-localshortcut": "^3.1.0",
"@types/lodash": "^4.14.191", "@types/jest": "^27.4.1",
"@types/lodash": "^4.14.188",
"@types/md5": "^2.3.2", "@types/md5": "^2.3.2",
"@types/node": "18.11.10", "@types/node": "^17.0.23",
"@types/react": "^18.0.26", "@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9", "@types/react-dom": "^18.0.8",
"@types/react-slider": "^1.3.1", "@types/react-slider": "^1.3.1",
"@types/react-test-renderer": "^17.0.1",
"@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5", "@types/react-window": "^1.8.5",
"@types/react-window-infinite-loader": "^1.0.6", "@types/react-window-infinite-loader": "^1.0.6",
"@types/styled-components": "^5.1.26", "@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^5.45.1", "@types/terser-webpack-plugin": "^5.0.4",
"@typescript-eslint/parser": "^5.45.1", "@types/webpack-bundle-analyzer": "^4.4.1",
"cross-env": "7.0.3", "@types/webpack-env": "^1.16.3",
"electron": "22.0.0", "@typescript-eslint/eslint-plugin": "^5.18.0",
"electron-builder": "23.6.0", "@typescript-eslint/parser": "^5.18.0",
"browserslist-config-erb": "^0.0.3",
"chalk": "^4.1.2",
"concurrently": "^7.1.0",
"core-js": "^3.21.1",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"detect-port": "^1.3.0",
"electron": "^21.2.0",
"electron-builder": "^23.0.3",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"eslint": "8.29.0", "electron-notarize": "^1.2.1",
"electron-rebuild": "^3.2.7",
"electronmon": "^2.0.2",
"eslint": "^8.12.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-erb": "^4.0.3", "eslint-config-erb": "^4.0.3",
"eslint-import-resolver-typescript": "^2.7.1", "eslint-import-resolver-typescript": "^2.7.1",
@@ -54,75 +207,127 @@
"eslint-plugin-compat": "^4.0.2", "eslint-plugin-compat": "^4.0.2",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.1.3", "eslint-plugin-jest": "^26.1.3",
"eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.31.11", "eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-sort-keys-fix": "^1.1.2",
"eslint-plugin-typescript-sort-keys": "^2.1.0", "eslint-plugin-typescript-sort-keys": "^2.1.0",
"happy-dom": "7.7.2", "file-loader": "^6.2.0",
"nano-staged": "0.8.0", "html-webpack-plugin": "^5.5.0",
"playwright": "1.27.1", "husky": "^7.0.4",
"rollup-plugin-visualizer": "^5.8.3", "i18next-parser": "^6.3.0",
"sass": "^1.56.1", "identity-obj-proxy": "^3.0.0",
"simple-git-hooks": "2.8.1", "jest": "^27.5.1",
"stylelint": "^14.16.0", "lint-staged": "^12.3.7",
"mini-css-extract-plugin": "^2.6.0",
"postcss-scss": "^4.0.4",
"postcss-syntax": "^0.36.2",
"prettier": "^2.6.2",
"react-refresh": "^0.12.0",
"react-refresh-typescript": "^2.0.4",
"react-test-renderer": "^18.0.0",
"rimraf": "^3.0.2",
"sass": "^1.49.11",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"stylelint": "^14.9.1",
"stylelint-config-rational-order": "^0.1.2", "stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard-scss": "^6.1.0", "stylelint-config-standard-scss": "^4.0.0",
"stylelint-config-styled-components": "^0.1.1", "stylelint-config-styled-components": "^0.1.1",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"stylelint-processor-styled-components": "^1.10.0", "stylelint-processor-styled-components": "^1.10.0",
"typescript": "4.9.3", "terser-webpack-plugin": "^5.3.1",
"unplugin-auto-expose": "0.0.4", "ts-jest": "^27.1.4",
"vite": "3.2.4", "ts-loader": "^9.2.8",
"vitest": "0.25.3" "ts-node": "^10.7.0",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"typescript": "^4.6.4",
"typescript-plugin-styled-components": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.71.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.0",
"webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"@ag-grid-community/client-side-row-model": "^28.2.1", "@emotion/react": "^11.10.4",
"@ag-grid-community/core": "^28.2.1", "@jellyfin/client-axios": "^10.7.8",
"@ag-grid-community/react": "^28.2.1", "@mantine/core": "^5.8.0",
"@ag-grid-community/styles": "^28.2.1", "@mantine/dates": "^5.8.0",
"@mantine/core": "^5.9.2", "@mantine/dropzone": "^5.8.0",
"@mantine/dates": "^5.9.2", "@mantine/form": "^5.8.0",
"@mantine/dropzone": "^5.9.2", "@mantine/hooks": "^5.8.0",
"@mantine/form": "^5.9.2", "@mantine/modals": "^5.8.0",
"@mantine/hooks": "^5.9.2", "@mantine/notifications": "^5.8.0",
"@mantine/modals": "^5.9.2", "@mantine/spotlight": "^5.8.0",
"@mantine/notifications": "^5.9.2", "@tanstack/react-query": "^4.16.1",
"@mantine/spotlight": "^5.9.2", "@tanstack/react-query-devtools": "^4.16.1",
"@tanstack/react-query": "^4.19.1", "ag-grid-community": "^28.2.1",
"@tanstack/react-query-devtools": "^4.19.1", "ag-grid-react": "^28.2.1",
"@vitejs/plugin-react": "^2.2.0", "axios": "^0.27.2",
"dayjs": "^1.11.6",
"electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1", "electron-localshortcut": "^3.2.1",
"electron-log": "^4.4.6",
"electron-store": "^8.1.0", "electron-store": "^8.1.0",
"electron-updater": "5.3.0", "electron-updater": "^4.6.5",
"fast-average-color": "^9.1.1",
"format-duration": "^2.0.0", "format-duration": "^2.0.0",
"framer-motion": "^7.6.19", "framer-motion": "^6.4.2",
"immer": "^9.0.16", "history": "^5.3.0",
"i18next": "^21.6.16",
"immer": "^9.0.15",
"is-electron": "^2.2.1", "is-electron": "^2.2.1",
"ky": "^0.32.2",
"ky-universal": "^0.11.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"md5": "^2.3.0", "md5": "^2.3.0",
"memoize-one": "^6.0.0", "nanoid": "^3.3.3",
"nanoid": "^4.0.0", "net": "^1.0.2",
"node-mpv": "^2.0.0-beta.2", "node-mpv": "^2.0.0-beta.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4", "react-error-boundary": "^3.1.4",
"react-i18next": "^11.16.7", "react-i18next": "^11.16.7",
"react-icons": "^4.7.1", "react-icons": "^4.6.0",
"react-player": "^2.11.0", "react-player": "^2.11.0",
"react-router": "^6.4.4", "react-router": "^6.3.0",
"react-router-dom": "^6.4.4", "react-router-dom": "^6.3.0",
"react-simple-img": "^3.0.0", "react-simple-img": "^3.0.0",
"react-slider": "^2.0.4", "react-slider": "^2.0.4",
"react-virtualized-auto-sizer": "^1.0.7", "react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.8", "react-window": "^1.8.8",
"react-window-infinite-loader": "^1.0.8", "react-window-infinite-loader": "^1.0.8",
"socket.io-client": "^4.5.3",
"styled-components": "^5.3.6", "styled-components": "^5.3.6",
"zod": "^3.19.1", "zod": "^3.19.1",
"zustand": "^4.1.5" "zustand": "^4.1.4"
},
"resolutions": {
"styled-components": "^5"
},
"devEngines": {
"node": ">=14.x",
"npm": ">=7.x"
},
"browserslist": [],
"prettier": {
"overrides": [
{
"files": [
".prettierrc",
".eslintrc"
],
"options": {
"parser": "json"
}
}
],
"singleQuote": true
},
"electronmon": {
"patterns": [
"!server",
"!src/renderer"
]
} }
} }
@@ -1,3 +0,0 @@
import Store from 'electron-store';
export const store = new Store();
-3
View File
@@ -1,3 +0,0 @@
import './core';
// require(`./${process.platform}`);
-71
View File
@@ -1,71 +0,0 @@
import { app, ipcMain } from 'electron';
import './security-restrictions';
import { restoreOrCreateWindow } from '/@/mainWindow';
ipcMain.on('app-restart', () => {
app.relaunch();
app.exit(0);
});
/**
* Prevent electron from running multiple instances.
*/
const isSingleInstance = app.requestSingleInstanceLock();
if (!isSingleInstance) {
app.quit();
process.exit(0);
}
app.on('second-instance', restoreOrCreateWindow);
/**
* Disable Hardware Acceleration to save more system resources.
*/
app.disableHardwareAcceleration();
/**
* Shout down background process if all windows was closed
*/
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
/**
* @see https://www.electronjs.org/docs/latest/api/app#event-activate-macos Event: 'activate'.
*/
app.on('activate', restoreOrCreateWindow);
/**
* Create the application window when the background process is ready.
*/
app
.whenReady()
.then(restoreOrCreateWindow)
.catch((e) => console.error('Failed create window:', e));
/**
* Install Vue.js or any other extension in development mode only.
* Note: You must install `electron-devtools-installer` manually
*/
// if (import.meta.env.DEV) {
// app.whenReady()
// .then(() => import('electron-devtools-installer'))
// .then(({default: installExtension, VUEJS3_DEVTOOLS}) => installExtension(VUEJS3_DEVTOOLS, {
// loadExtensionOptions: {
// allowFileAccess: true,
// },
// }))
// .catch(e => console.error('Failed install extension:', e));
// }
/**
* Check for new version of the application - production mode only.
*/
if (import.meta.env.PROD) {
app
.whenReady()
.then(() => import('electron-updater'))
.then(({ autoUpdater }) => autoUpdater.checkForUpdatesAndNotify())
.catch((e) => console.error('Failed check updates:', e));
}
-137
View File
@@ -1,137 +0,0 @@
import { app, BrowserWindow, ipcMain } from 'electron';
import { join } from 'path';
import { URL } from 'url';
import { disableMediaKeys, enableMediaKeys } from './features/core/media-keys';
import { store } from './features/core/settings';
import electronLocalShortcut from 'electron-localshortcut';
import './features';
const isDevelopment = process.env.NODE_ENV === 'development';
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
return installer
.default(
extensions.map((name) => installer[name]),
forceDownload,
)
.catch(console.log);
};
let browserWindow: BrowserWindow | null = null;
async function createWindow() {
if (isDevelopment) {
await installExtensions();
}
browserWindow = new BrowserWindow({
show: false, // Use the 'ready-to-show' event to show the instantiated BrowserWindow.
frame: false,
minWidth: 640,
minHeight: 600,
height: 900,
width: 1440,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: false, // Sandbox disabled because the demo of preload script depend on the Node.js api
webviewTag: false, // The webview tag is not recommended. Consider alternatives like an iframe or Electron's BrowserView. @see https://www.electronjs.org/docs/latest/api/webview-tag#warning
preload: join(app.getAppPath(), 'packages/preload/dist/index.cjs'),
backgroundThrottling: false,
},
});
/**
* If the 'show' property of the BrowserWindow's constructor is omitted from the initialization options,
* it then defaults to 'true'. This can cause flickering as the window loads the html content,
* and it also has show problematic behaviour with the closing of the window.
* Use `show: false` and listen to the `ready-to-show` event to show the window.
*
* @see https://github.com/electron/electron/issues/25012 for the afford mentioned issue.
*/
browserWindow.on('ready-to-show', () => {
browserWindow?.show();
if (import.meta.env.DEV) {
browserWindow?.webContents.openDevTools();
}
});
ipcMain.on('window-maximize', () => {
browserWindow?.maximize();
});
ipcMain.on('window-unmaximize', () => {
browserWindow?.unmaximize();
});
ipcMain.on('window-minimize', () => {
browserWindow?.minimize();
});
ipcMain.on('window-close', () => {
browserWindow?.close();
});
electronLocalShortcut.register(browserWindow, 'Ctrl+Shift+I', () => {
browserWindow?.webContents.openDevTools();
});
const globalMediaKeysEnabled = store.get('global_media_hotkeys') as boolean;
if (globalMediaKeysEnabled) {
enableMediaKeys(browserWindow);
}
ipcMain.on('global-media-keys-enable', () => {
enableMediaKeys(browserWindow);
});
ipcMain.on('global-media-keys-disable', () => {
disableMediaKeys();
});
/**
* URL for main window.
* Vite dev server for development.
* `file://../renderer/index.html` for production and test.
*/
const pageUrl =
import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL !== undefined
? import.meta.env.VITE_DEV_SERVER_URL
: new URL('../renderer/dist/index.html', 'file://' + __dirname).toString();
await browserWindow.loadURL(pageUrl);
return browserWindow;
}
/**
* Restore an existing BrowserWindow or Create a new BrowserWindow.
*/
export async function restoreOrCreateWindow() {
let window = BrowserWindow.getAllWindows().find((w) => !w.isDestroyed());
if (window === undefined) {
window = await createWindow();
}
if (window.isMinimized()) {
window.restore();
}
window.focus();
}
ipcMain.on('app-restart', () => {
app.relaunch();
app.exit(0);
});
export const getBrowserWindow = () => {
return browserWindow;
};
-135
View File
@@ -1,135 +0,0 @@
import {app, shell} from 'electron';
import {URL} from 'url';
type Permissions =
| 'clipboard-read'
| 'media'
| 'display-capture'
| 'mediaKeySystem'
| 'geolocation'
| 'notifications'
| 'midi'
| 'midiSysex'
| 'pointerLock'
| 'fullscreen'
| 'openExternal'
| 'unknown';
/**
* A list of origins that you allow open INSIDE the application and permissions for them.
*
* In development mode you need allow open `VITE_DEV_SERVER_URL`.
*/
const ALLOWED_ORIGINS_AND_PERMISSIONS = new Map<string, Set<Permissions>>(
import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL
? [[new URL(import.meta.env.VITE_DEV_SERVER_URL).origin, new Set()]]
: [],
);
/**
* A list of origins that you allow open IN BROWSER.
* Navigation to the origins below is only possible if the link opens in a new window.
*
* @example
* <a
* target="_blank"
* href="https://github.com/"
* >
*/
const ALLOWED_EXTERNAL_ORIGINS = new Set<`https://${string}`>(['https://github.com']);
app.on('web-contents-created', (_, contents) => {
/**
* Block navigation to origins not on the allowlist.
*
* Navigation exploits are quite common. If an attacker can convince the app to navigate away from its current page,
* they can possibly force the app to open arbitrary web resources/websites on the web.
*
* @see https://www.electronjs.org/docs/latest/tutorial/security#13-disable-or-limit-navigation
*/
contents.on('will-navigate', (event, url) => {
const {origin} = new URL(url);
if (ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
return;
}
// Prevent navigation
event.preventDefault();
if (import.meta.env.DEV) {
console.warn(`Blocked navigating to disallowed origin: ${origin}`);
}
});
/**
* Block requests for disallowed permissions.
* By default, Electron will automatically approve all permission requests.
*
* @see https://www.electronjs.org/docs/latest/tutorial/security#5-handle-session-permission-requests-from-remote-content
*/
contents.session.setPermissionRequestHandler((webContents, permission, callback) => {
const {origin} = new URL(webContents.getURL());
const permissionGranted = !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission);
callback(permissionGranted);
if (!permissionGranted && import.meta.env.DEV) {
console.warn(`${origin} requested permission for '${permission}', but was rejected.`);
}
});
/**
* Hyperlinks leading to allowed sites are opened in the default browser.
*
* The creation of new `webContents` is a common attack vector. Attackers attempt to convince the app to create new windows,
* frames, or other renderer processes with more privileges than they had before; or with pages opened that they couldn't open before.
* You should deny any unexpected window creation.
*
* @see https://www.electronjs.org/docs/latest/tutorial/security#14-disable-or-limit-creation-of-new-windows
* @see https://www.electronjs.org/docs/latest/tutorial/security#15-do-not-use-openexternal-with-untrusted-content
*/
contents.setWindowOpenHandler(({url}) => {
const {origin} = new URL(url);
// @ts-expect-error Type checking is performed in runtime.
if (ALLOWED_EXTERNAL_ORIGINS.has(origin)) {
// Open url in default browser.
shell.openExternal(url).catch(console.error);
} else if (import.meta.env.DEV) {
console.warn(`Blocked the opening of a disallowed origin: ${origin}`);
}
// Prevent creating a new window.
return {action: 'deny'};
});
/**
* Verify webview options before creation.
*
* Strip away preload scripts, disable Node.js integration, and ensure origins are on the allowlist.
*
* @see https://www.electronjs.org/docs/latest/tutorial/security#12-verify-webview-options-before-creation
*/
contents.on('will-attach-webview', (event, webPreferences, params) => {
const {origin} = new URL(params.src);
if (!ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
if (import.meta.env.DEV) {
console.warn(`A webview tried to attach ${params.src}, but was blocked.`);
}
event.preventDefault();
return;
}
// Strip away preload scripts if unused or verify their location is legitimate.
delete webPreferences.preload;
// @ts-expect-error `preloadURL` exists. - @see https://www.electronjs.org/docs/latest/api/web-contents#event-will-attach-webview
delete webPreferences.preloadURL;
// Disable Node.js integration
webPreferences.nodeIntegration = false;
// Enable contextIsolation
webPreferences.contextIsolation = true;
});
});
-72
View File
@@ -1,72 +0,0 @@
import type {MockedClass} from 'vitest';
import {beforeEach, expect, test, vi} from 'vitest';
import {restoreOrCreateWindow} from '../src/mainWindow';
import {BrowserWindow} from 'electron';
/**
* Mock real electron BrowserWindow API
*/
vi.mock('electron', () => {
// Use "as unknown as" because vi.fn() does not have static methods
const bw = vi.fn() as unknown as MockedClass<typeof BrowserWindow>;
bw.getAllWindows = vi.fn(() => bw.mock.instances);
bw.prototype.loadURL = vi.fn((_: string, __?: Electron.LoadURLOptions) => Promise.resolve());
// Use "any" because the on function is overloaded
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bw.prototype.on = vi.fn<any>();
bw.prototype.destroy = vi.fn();
bw.prototype.isDestroyed = vi.fn();
bw.prototype.isMinimized = vi.fn();
bw.prototype.focus = vi.fn();
bw.prototype.restore = vi.fn();
const app: Pick<Electron.App, 'getAppPath'> = {
getAppPath(): string {
return '';
},
};
return {BrowserWindow: bw, app};
});
beforeEach(() => {
vi.clearAllMocks();
});
test('Should create a new window', async () => {
const {mock} = vi.mocked(BrowserWindow);
expect(mock.instances).toHaveLength(0);
await restoreOrCreateWindow();
expect(mock.instances).toHaveLength(1);
expect(mock.instances[0].loadURL).toHaveBeenCalledOnce();
expect(mock.instances[0].loadURL).toHaveBeenCalledWith(expect.stringMatching(/index\.html$/));
});
test('Should restore an existing window', async () => {
const {mock} = vi.mocked(BrowserWindow);
// Create a window and minimize it.
await restoreOrCreateWindow();
expect(mock.instances).toHaveLength(1);
const appWindow = vi.mocked(mock.instances[0]);
appWindow.isMinimized.mockReturnValueOnce(true);
await restoreOrCreateWindow();
expect(mock.instances).toHaveLength(1);
expect(appWindow.restore).toHaveBeenCalledOnce();
});
test('Should create a new window if the previous one was destroyed', async () => {
const {mock} = vi.mocked(BrowserWindow);
// Create a window and destroy it.
await restoreOrCreateWindow();
expect(mock.instances).toHaveLength(1);
const appWindow = vi.mocked(mock.instances[0]);
appWindow.isDestroyed.mockReturnValueOnce(true);
await restoreOrCreateWindow();
expect(mock.instances).toHaveLength(2);
});
-19
View File
@@ -1,19 +0,0 @@
{
"compilerOptions": {
"module": "esnext",
"target": "esnext",
"sourceMap": false,
"moduleResolution": "Node",
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"isolatedModules": true,
"types": ["node"],
"baseUrl": ".",
"paths": {
"/@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "../../types/**/*.d.ts"],
"exclude": ["**/*.spec.ts", "**/*.test.ts"]
}
-43
View File
@@ -1,43 +0,0 @@
import {node} from '../../.electron-vendors.cache.json';
import {join} from 'node:path';
import {injectAppVersion} from '../../version/inject-app-version-plugin.mjs';
const PACKAGE_ROOT = __dirname;
const PROJECT_ROOT = join(PACKAGE_ROOT, '../..');
/**
* @type {import('vite').UserConfig}
* @see https://vitejs.dev/config/
*/
const config = {
mode: process.env.MODE,
root: PACKAGE_ROOT,
envDir: PROJECT_ROOT,
resolve: {
alias: {
'/@/': join(PACKAGE_ROOT, 'src') + '/',
},
},
build: {
ssr: true,
sourcemap: 'inline',
target: `node${node}`,
outDir: 'dist',
assetsDir: '.',
minify: process.env.MODE !== 'development',
lib: {
entry: 'src/index.ts',
formats: ['cjs'],
},
rollupOptions: {
output: {
entryFileNames: '[name].cjs',
},
},
emptyOutDir: true,
reportCompressedSize: false,
},
plugins: [injectAppVersion(PROJECT_ROOT)],
};
export default config;
-21
View File
@@ -1,21 +0,0 @@
import { ipcRenderer } from 'electron';
const exit = () => {
ipcRenderer.send('window-close');
};
const maximize = () => {
ipcRenderer.send('window-maximize');
};
const minimize = () => {
ipcRenderer.send('window-minimize');
};
const unmaximize = () => {
ipcRenderer.send('window-unmaximize');
};
export const browser = {
minimize,
maximize,
unmaximize,
exit,
};
-10
View File
@@ -1,10 +0,0 @@
/**
* @module preload
*/
export { sha256sum } from './node-crypto';
export { versions } from './versions';
export { localSettings } from './local-settings';
export { browser } from './browser';
export { mpvPlayer, mpvPlayerListener } from './mpv-player';
export { ipc } from './ipc';
-9
View File
@@ -1,9 +0,0 @@
import { ipcRenderer } from 'electron';
const removeAllListeners = (channel: string) => {
ipcRenderer.removeAllListeners(channel);
};
export const ipc = {
removeAllListeners,
};

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