mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 10:22:50 +00:00 
			
		
		
		
	Compare commits
	
		
			120 Commits
		
	
	
		
			2025-09-07
			...
			2025-09-13
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7d1788a079 | ||
| 
						 | 
					83e04257d9 | ||
| 
						 | 
					11c33e34d2 | ||
| 
						 | 
					52df77cab5 | ||
| 
						 | 
					17f6264a03 | ||
| 
						 | 
					9463b0b808 | ||
| 
						 | 
					eba83f3848 | ||
| 
						 | 
					e3f6ea73c3 | ||
| 
						 | 
					06247e742c | ||
| 
						 | 
					4606782d6b | ||
| 
						 | 
					a988acf2d5 | ||
| 
						 | 
					5f44007372 | ||
| 
						 | 
					8b87272e38 | ||
| 
						 | 
					ceccf08f69 | ||
| 
						 | 
					5a80cd7e02 | ||
| 
						 | 
					d617a49766 | ||
| 
						 | 
					7c0d79c115 | ||
| 
						 | 
					0d4a508543 | ||
| 
						 | 
					f979dd2277 | ||
| 
						 | 
					87de220511 | ||
| 
						 | 
					481e70b0aa | ||
| 
						 | 
					6c3a16e579 | ||
| 
						 | 
					394f91c994 | ||
| 
						 | 
					63a268d6c5 | ||
| 
						 | 
					9ee6e23a59 | ||
| 
						 | 
					eceb1cd53c | ||
| 
						 | 
					719c55655d | ||
| 
						 | 
					aeb15c7ea1 | ||
| 
						 | 
					0c7a366782 | ||
| 
						 | 
					93a3a572d4 | ||
| 
						 | 
					8b2645a529 | ||
| 
						 | 
					b72e132699 | ||
| 
						 | 
					f4df8e2ccc | ||
| 
						 | 
					f03f004226 | ||
| 
						 | 
					b13c840bda | ||
| 
						 | 
					ce90647bda | ||
| 
						 | 
					164e487090 | ||
| 
						 | 
					a4660adedd | ||
| 
						 | 
					9352eec854 | ||
| 
						 | 
					4478f8f6e0 | ||
| 
						 | 
					d69cbcd941 | ||
| 
						 | 
					affcfd06cf | ||
| 
						 | 
					b94b088c19 | ||
| 
						 | 
					5385382a88 | ||
| 
						 | 
					d06ad37a22 | ||
| 
						 | 
					72c7d4199b | ||
| 
						 | 
					2e75ced83f | ||
| 
						 | 
					9a28ce0263 | ||
| 
						 | 
					9ed2507bdc | ||
| 
						 | 
					c617870c2b | ||
| 
						 | 
					eb71d21ea3 | ||
| 
						 | 
					762452b2b3 | ||
| 
						 | 
					31cd3e3119 | ||
| 
						 | 
					c25dba9212 | ||
| 
						 | 
					52cefe4a00 | ||
| 
						 | 
					7b8fcab242 | ||
| 
						 | 
					b733e8b5ea | ||
| 
						 | 
					4a4e67a3cf | ||
| 
						 | 
					b00feea00d | ||
| 
						 | 
					c250cec418 | ||
| 
						 | 
					a0af0f5196 | ||
| 
						 | 
					2f0b6e10ab | ||
| 
						 | 
					a41364d91d | ||
| 
						 | 
					e580a5d9c1 | ||
| 
						 | 
					9d059b2a13 | ||
| 
						 | 
					6e32eebdf0 | ||
| 
						 | 
					7f3ebd4296 | ||
| 
						 | 
					0c4d55bd78 | ||
| 
						 | 
					8f05be3f6b | ||
| 
						 | 
					e06cd4b28c | ||
| 
						 | 
					b9242b3232 | ||
| 
						 | 
					63f6d772ea | ||
| 
						 | 
					652df1f370 | ||
| 
						 | 
					f4b5baf308 | ||
| 
						 | 
					cc36fd5434 | ||
| 
						 | 
					3e182978ad | ||
| 
						 | 
					e7adf1a9b5 | ||
| 
						 | 
					36fb86eaf8 | ||
| 
						 | 
					495c47224e | ||
| 
						 | 
					7af71384b4 | ||
| 
						 | 
					e318152ee3 | ||
| 
						 | 
					17dc7c90a1 | ||
| 
						 | 
					226a5bce8f | ||
| 
						 | 
					ffd2ed01b9 | ||
| 
						 | 
					3348e2fff0 | ||
| 
						 | 
					def41f66f4 | ||
| 
						 | 
					02a1a732f8 | ||
| 
						 | 
					2494af7e1c | ||
| 
						 | 
					0c4711d99f | ||
| 
						 | 
					039f73a5ed | ||
| 
						 | 
					74b58d5b10 | ||
| 
						 | 
					61044104a2 | ||
| 
						 | 
					fac612077a | ||
| 
						 | 
					135b3ff964 | ||
| 
						 | 
					393b853119 | ||
| 
						 | 
					6bb2938e34 | ||
| 
						 | 
					fbf16fd54d | ||
| 
						 | 
					d6d4fd034f | ||
| 
						 | 
					aa4999dac6 | ||
| 
						 | 
					b6c0b50e79 | ||
| 
						 | 
					0f07f1927e | ||
| 
						 | 
					4493d86e51 | ||
| 
						 | 
					fbfeeeb88f | ||
| 
						 | 
					2366111bba | ||
| 
						 | 
					5fb42b87f1 | ||
| 
						 | 
					500c35c58d | ||
| 
						 | 
					ca3446c90e | ||
| 
						 | 
					4c3d42d5d1 | ||
| 
						 | 
					299a10efe8 | ||
| 
						 | 
					7adac2a342 | ||
| 
						 | 
					eb58b10d75 | ||
| 
						 | 
					5e46d81c45 | ||
| 
						 | 
					2963926c45 | ||
| 
						 | 
					d9a0b863a8 | ||
| 
						 | 
					db6369f3c6 | ||
| 
						 | 
					d450e263f0 | ||
| 
						 | 
					462960d9bf | ||
| 
						 | 
					8ea4829e8a | ||
| 
						 | 
					c5d23dc883 | ||
| 
						 | 
					3dc973e4ac | 
							
								
								
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -10,8 +10,166 @@
 | 
				
			|||||||
> [!CAUTION]
 | 
					> [!CAUTION]
 | 
				
			||||||
Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
 | 
					Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Palmr: hotfix #7622 [@vhsdream](https://github.com/vhsdream) ([#7625](https://github.com/community-scripts/ProxmoxVE/pull/7625))
 | 
				
			||||||
 | 
					    - ollama: fix: ccurl continue on interrupts [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7620](https://github.com/community-scripts/ProxmoxVE/pull/7620))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🔧 Refactor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - pdm: refactor for beta version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7619](https://github.com/community-scripts/ProxmoxVE/pull/7619))
 | 
				
			||||||
 | 
					    - Immich: bump to v1.142.0 [@vhsdream](https://github.com/vhsdream) ([#7594](https://github.com/community-scripts/ProxmoxVE/pull/7594))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - fix: tagline grammar [@jonathanwuki](https://github.com/jonathanwuki) ([#7621](https://github.com/community-scripts/ProxmoxVE/pull/7621))
 | 
				
			||||||
 | 
					- fix: grammar/capitalization for links and taglines [@jonathanwuki](https://github.com/jonathanwuki) ([#7609](https://github.com/community-scripts/ProxmoxVE/pull/7609))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🆕 New Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Stylus ([#7588](https://github.com/community-scripts/ProxmoxVE/pull/7588))
 | 
				
			||||||
 | 
					- UHF ([#7589](https://github.com/community-scripts/ProxmoxVE/pull/7589))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Tweak: post-pve-install: create folder if Not exist  [@JVKeller](https://github.com/JVKeller) ([#7598](https://github.com/community-scripts/ProxmoxVE/pull/7598))
 | 
				
			||||||
 | 
					    - Update openwebui.sh [@webmogul1](https://github.com/webmogul1) ([#7582](https://github.com/community-scripts/ProxmoxVE/pull/7582))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - [core]: add fallback if mariadb upstream unreachable [@MickLesk](https://github.com/MickLesk) ([#7599](https://github.com/community-scripts/ProxmoxVE/pull/7599))
 | 
				
			||||||
 | 
					    - ESPHome: Increase default disk size [@tremor021](https://github.com/tremor021) ([#7600](https://github.com/community-scripts/ProxmoxVE/pull/7600))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-11
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🆕 New Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - telegraf ([#7576](https://github.com/community-scripts/ProxmoxVE/pull/7576))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - [core] Sort tools.func functions alphabeticaly [@tremor021](https://github.com/tremor021) ([#7569](https://github.com/community-scripts/ProxmoxVE/pull/7569))
 | 
				
			||||||
 | 
					- mobile subscription nag fix [@dvino](https://github.com/dvino) ([#7567](https://github.com/community-scripts/ProxmoxVE/pull/7567))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - alpine-install: switch to using GitHub to fetch tools when using GitHub [@burritosoftware](https://github.com/burritosoftware) ([#7566](https://github.com/community-scripts/ProxmoxVE/pull/7566))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Add margin-bottom to Most Viewed Scripts header to unifi UI [@BramSuurdje](https://github.com/BramSuurdje) ([#7572](https://github.com/community-scripts/ProxmoxVE/pull/7572))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 📝 Script Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Fix frontend url [@r1cebank](https://github.com/r1cebank) ([#7578](https://github.com/community-scripts/ProxmoxVE/pull/7578))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🆕 New Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Autocaliweb ([#7515](https://github.com/community-scripts/ProxmoxVE/pull/7515))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Palmr: fix #7556 [@vhsdream](https://github.com/vhsdream) ([#7558](https://github.com/community-scripts/ProxmoxVE/pull/7558))
 | 
				
			||||||
 | 
					    - Wizarr: Fix DB migrations [@vhsdream](https://github.com/vhsdream) ([#7552](https://github.com/community-scripts/ProxmoxVE/pull/7552))
 | 
				
			||||||
 | 
					    - fix: pmg - split no-nag script into separate config files [@MickLesk](https://github.com/MickLesk) ([#7540](https://github.com/community-scripts/ProxmoxVE/pull/7540))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Update Palmr to Support new v3.2.1 [@vhsdream](https://github.com/vhsdream) ([#7526](https://github.com/community-scripts/ProxmoxVE/pull/7526))
 | 
				
			||||||
 | 
					    - add external installer warnings and user confirmation in several LXC's [@MickLesk](https://github.com/MickLesk) ([#7539](https://github.com/community-scripts/ProxmoxVE/pull/7539))
 | 
				
			||||||
 | 
					    - Booklore: Add Bookdrop location to .env [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([#7533](https://github.com/community-scripts/ProxmoxVE/pull/7533))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🔧 Refactor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Refactor: audiobookshelf [@MickLesk](https://github.com/MickLesk) ([#7538](https://github.com/community-scripts/ProxmoxVE/pull/7538))
 | 
				
			||||||
 | 
					    - Refactor: Blocky [@MickLesk](https://github.com/MickLesk) ([#7537](https://github.com/community-scripts/ProxmoxVE/pull/7537))
 | 
				
			||||||
 | 
					    - Improve npmplus credential retrieval and messaging [@MickLesk](https://github.com/MickLesk) ([#7532](https://github.com/community-scripts/ProxmoxVE/pull/7532))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 💥 Breaking Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Remove Pingvin Share [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7553](https://github.com/community-scripts/ProxmoxVE/pull/7553))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 📝 Script Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - set updateable to true for several lxc JSON-configs [@MickLesk](https://github.com/MickLesk) ([#7534](https://github.com/community-scripts/ProxmoxVE/pull/7534))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-09
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Tududi: v0.81 [@vhsdream](https://github.com/vhsdream) ([#7517](https://github.com/community-scripts/ProxmoxVE/pull/7517))
 | 
				
			||||||
 | 
					    - WGDashboard: Revert back to old update method [@tremor021](https://github.com/tremor021) ([#7500](https://github.com/community-scripts/ProxmoxVE/pull/7500))
 | 
				
			||||||
 | 
					    - AdventureLog: remove folder during update process [@MickLesk](https://github.com/MickLesk) ([#7507](https://github.com/community-scripts/ProxmoxVE/pull/7507))
 | 
				
			||||||
 | 
					    - PLANKA: Fix backup and restore commands [@tremor021](https://github.com/tremor021) ([#7505](https://github.com/community-scripts/ProxmoxVE/pull/7505))
 | 
				
			||||||
 | 
					    - Recyclarr: Suppress config creation output [@tremor021](https://github.com/tremor021) ([#7502](https://github.com/community-scripts/ProxmoxVE/pull/7502))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🔧 Refactor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Pulse: standardise install/update with Pulse repo script [@vhsdream](https://github.com/vhsdream) ([#7519](https://github.com/community-scripts/ProxmoxVE/pull/7519))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Refactor GitHubStarsButton to wrap in Link component for external navigation [@BramSuurdje](https://github.com/BramSuurdje) ([#7492](https://github.com/community-scripts/ProxmoxVE/pull/7492))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Bump vite from 7.0.0 to 7.1.5 in /frontend [@dependabot[bot]](https://github.com/dependabot[bot]) ([#7522](https://github.com/community-scripts/ProxmoxVE/pull/7522))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 📝 Script Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - swizzin: Change category from nvr to media [@MickLesk](https://github.com/MickLesk) ([#7511](https://github.com/community-scripts/ProxmoxVE/pull/7511))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-09-08
 | 
					## 2025-09-08
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - CT's: fix missing variable declaration (actualBudget, openziti, umlautadaptarr) [@MickLesk](https://github.com/MickLesk) ([#7483](https://github.com/community-scripts/ProxmoxVE/pull/7483))
 | 
				
			||||||
 | 
					    - karakeep: fix service file [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7482](https://github.com/community-scripts/ProxmoxVE/pull/7482))
 | 
				
			||||||
 | 
					    - Update searxng-install.sh [@sebguy](https://github.com/sebguy) ([#7469](https://github.com/community-scripts/ProxmoxVE/pull/7469))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Immich: bump to version 1.141.1 [@vhsdream](https://github.com/vhsdream) ([#7418](https://github.com/community-scripts/ProxmoxVE/pull/7418))
 | 
				
			||||||
 | 
					    - [core]: switch all base_settings to variables [@MickLesk](https://github.com/MickLesk) ([#7479](https://github.com/community-scripts/ProxmoxVE/pull/7479))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 💥 Breaking Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - RustDesk Server: Update the credentials info [@tremor021](https://github.com/tremor021) ([#7473](https://github.com/community-scripts/ProxmoxVE/pull/7473))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Format numerical values in DataFetcher component for better readability [@BramSuurdje](https://github.com/BramSuurdje) ([#7477](https://github.com/community-scripts/ProxmoxVE/pull/7477))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - feat: enhance github stars button to be better looking and more compact [@BramSuurdje](https://github.com/BramSuurdje) ([#7464](https://github.com/community-scripts/ProxmoxVE/pull/7464))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-09-07
 | 
					## 2025-09-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 🚀 Updated Scripts
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,13 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
 | 
				
			|||||||
# Source: https://actualbudget.org/
 | 
					# Source: https://actualbudget.org/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="Actual Budget"
 | 
					APP="Actual Budget"
 | 
				
			||||||
var_tags="finance"
 | 
					var_tags="${var_tags:-finance}"
 | 
				
			||||||
var_cpu="2"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="2048"
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
var_disk="4"
 | 
					var_disk="${var_disk:-4}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ function update_script() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    msg_info "Backup Old Installation"
 | 
					    msg_info "Backup Old Installation"
 | 
				
			||||||
    cp -r /opt/adventurelog /opt/adventurelog-backup
 | 
					    cp -r /opt/adventurelog /opt/adventurelog-backup
 | 
				
			||||||
 | 
					    rm -rf /opt/adventurelog
 | 
				
			||||||
    msg_ok "Backup done"
 | 
					    msg_ok "Backup done"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog"
 | 
					    fetch_and_deploy_gh_release "adventurelog" "seanmorley15/adventurelog"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ APP="audiobookshelf"
 | 
				
			|||||||
var_tags="${var_tags:-podcast;audiobook}"
 | 
					var_tags="${var_tags:-podcast;audiobook}"
 | 
				
			||||||
var_cpu="${var_cpu:-2}"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="${var_ram:-2048}"
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
var_disk="${var_disk:-4}"
 | 
					var_disk="${var_disk:-5}"
 | 
				
			||||||
var_os="${var_os:-debian}"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="${var_version:-12}"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="${var_unprivileged:-1}"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
@@ -20,15 +20,19 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
    header_info
 | 
					  header_info
 | 
				
			||||||
    check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
    check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
    if [[ ! -f /etc/apt/trusted.gpg.d/audiobookshelf-ppa.asc ]]; then
 | 
					  if [[ ! -f /etc/default/audiobookshelf ]]; then
 | 
				
			||||||
        msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    echo "This application receives updates through the APT package manager."
 | 
					 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  msg_info "Updating $APP LXC"
 | 
				
			||||||
 | 
					  $STD apt-get update
 | 
				
			||||||
 | 
					  $STD apt-get -y upgrade
 | 
				
			||||||
 | 
					  msg_ok "Updated $APP LXC"
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
@@ -38,4 +42,4 @@ description
 | 
				
			|||||||
msg_ok "Completed Successfully!\n"
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:13378${CL}"
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:13378${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								ct/autocaliweb.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								ct/autocaliweb.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: vhsdream
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://github.com/gelbphoenix/autocaliweb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="Autocaliweb"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-ebooks}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-6}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					  header_info
 | 
				
			||||||
 | 
					  check_container_storage
 | 
				
			||||||
 | 
					  check_container_resources
 | 
				
			||||||
 | 
					  if [[ ! -d /opt/autocaliweb ]]; then
 | 
				
			||||||
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setup_uv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//')
 | 
				
			||||||
 | 
					  if check_for_gh_release "autocaliweb" "gelbphoenix/autocaliweb"; then
 | 
				
			||||||
 | 
					    msg_info "Stopping Services"
 | 
				
			||||||
 | 
					    systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper
 | 
				
			||||||
 | 
					    msg_ok "Stopped Services"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSTALL_DIR="/opt/autocaliweb"
 | 
				
			||||||
 | 
					    export VIRTUAL_ENV="${INSTALL_DIR}/venv"
 | 
				
			||||||
 | 
					    $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh}
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
 | 
				
			||||||
 | 
					    msg_info "Updating ${APP}"
 | 
				
			||||||
 | 
					    cd "$INSTALL_DIR"
 | 
				
			||||||
 | 
					    if [[ ! -d "$VIRTUAL_ENV" ]]; then
 | 
				
			||||||
 | 
					      $STD uv venv "$VIRTUAL_ENV"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    $STD uv sync --all-extras --active
 | 
				
			||||||
 | 
					    cd "$INSTALL_DIR"/koreader/plugins
 | 
				
			||||||
 | 
					    PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)"
 | 
				
			||||||
 | 
					    echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					    echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					    echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					    $STD zip -r koplugin.zip acwsync.koplugin/
 | 
				
			||||||
 | 
					    cp -r koplugin.zip "$INSTALL_DIR"/cps/static
 | 
				
			||||||
 | 
					    mkdir -p "$INSTALL_DIR"/metadata_temp
 | 
				
			||||||
 | 
					    $STD tar -xf ~/autocaliweb_bkp.tar --directory /
 | 
				
			||||||
 | 
					    KEPUB_VERSION="$(/usr/bin/kepubify --version)"
 | 
				
			||||||
 | 
					    CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)"
 | 
				
			||||||
 | 
					    echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE
 | 
				
			||||||
 | 
					    echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE
 | 
				
			||||||
 | 
					    sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE
 | 
				
			||||||
 | 
					    chown -R acw:acw "$INSTALL_DIR"
 | 
				
			||||||
 | 
					    rm ~/autocaliweb_bkp.tar
 | 
				
			||||||
 | 
					    msg_ok "Updated $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting Services"
 | 
				
			||||||
 | 
					    systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper
 | 
				
			||||||
 | 
					    msg_ok "Started Services"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}"
 | 
				
			||||||
							
								
								
									
										46
									
								
								ct/blocky.sh
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								ct/blocky.sh
									
									
									
									
									
								
							@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
 | 
				
			|||||||
# Copyright (c) 2021-2025 tteck
 | 
					# Copyright (c) 2021-2025 tteck
 | 
				
			||||||
# Author: tteck (tteckster)
 | 
					# Author: tteck (tteckster)
 | 
				
			||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
# Source: https://0xerr0r.github.io/blocky/latest/
 | 
					# Source: https://0xerr0r.github.io/blocky
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="Blocky"
 | 
					APP="Blocky"
 | 
				
			||||||
var_tags="${var_tags:-adblock}"
 | 
					var_tags="${var_tags:-adblock}"
 | 
				
			||||||
@@ -20,18 +20,38 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
    header_info
 | 
					  header_info
 | 
				
			||||||
    check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
    check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
    if [[ ! -d /var ]]; then
 | 
					  if [[ ! -d /opt/blocky ]]; then
 | 
				
			||||||
        msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    msg_info "Updating $APP LXC"
 | 
					 | 
				
			||||||
    $STD apt-get update
 | 
					 | 
				
			||||||
    $STD apt-get -y upgrade
 | 
					 | 
				
			||||||
    msg_ok "Updated $APP LXC"
 | 
					 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if check_for_gh_release "blocky" "0xERR0R/blocky"; then
 | 
				
			||||||
 | 
					    msg_info "Stopping Service"
 | 
				
			||||||
 | 
					    systemctl stop blocky
 | 
				
			||||||
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Backup Config"
 | 
				
			||||||
 | 
					    mv /opt/blocky/config.yml /opt/config.yml
 | 
				
			||||||
 | 
					    msg_ok "Backed Up Config"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Removing Old Version"
 | 
				
			||||||
 | 
					    rm -rf /opt/blocky
 | 
				
			||||||
 | 
					    msg_ok "Removed Old Version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "blocky" "0xERR0R/blocky" "prebuild" "latest" "/opt/blocky" "blocky_*_linux_x86_64.tar.gz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Restore Config"
 | 
				
			||||||
 | 
					    mv /opt/config.yml /opt/blocky/config.yml
 | 
				
			||||||
 | 
					    msg_ok "Restored Config"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
 | 
					    systemctl start blocky
 | 
				
			||||||
 | 
					    msg_ok "Started Service"
 | 
				
			||||||
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
@@ -41,4 +61,4 @@ description
 | 
				
			|||||||
msg_ok "Completed Successfully!\n"
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}"
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ APP="ESPHome"
 | 
				
			|||||||
var_tags="${var_tags:-automation}"
 | 
					var_tags="${var_tags:-automation}"
 | 
				
			||||||
var_cpu="${var_cpu:-2}"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="${var_ram:-1024}"
 | 
					var_ram="${var_ram:-1024}"
 | 
				
			||||||
var_disk="${var_disk:-4}"
 | 
					var_disk="${var_disk:-10}"
 | 
				
			||||||
var_os="${var_os:-debian}"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="${var_version:-12}"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="${var_unprivileged:-1}"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								ct/headers/autocaliweb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/autocaliweb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					    ___         __                   ___               __  
 | 
				
			||||||
 | 
					   /   | __  __/ /_____  _________ _/ (_)      _____  / /_ 
 | 
				
			||||||
 | 
					  / /| |/ / / / __/ __ \/ ___/ __ `/ / / | /| / / _ \/ __ \
 | 
				
			||||||
 | 
					 / ___ / /_/ / /_/ /_/ / /__/ /_/ / / /| |/ |/ /  __/ /_/ /
 | 
				
			||||||
 | 
					/_/  |_\__,_/\__/\____/\___/\__,_/_/_/ |__/|__/\___/_.___/ 
 | 
				
			||||||
 | 
					                                                           
 | 
				
			||||||
@@ -1,6 +0,0 @@
 | 
				
			|||||||
    ____  _                   _     
 | 
					 | 
				
			||||||
   / __ \(_)___  ____ __   __(_)___ 
 | 
					 | 
				
			||||||
  / /_/ / / __ \/ __ `/ | / / / __ \
 | 
					 | 
				
			||||||
 / ____/ / / / / /_/ /| |/ / / / / /
 | 
					 | 
				
			||||||
/_/   /_/_/ /_/\__, / |___/_/_/ /_/ 
 | 
					 | 
				
			||||||
              /____/                
 | 
					 | 
				
			||||||
							
								
								
									
										6
									
								
								ct/headers/stylus
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/stylus
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					   _____ __        __          
 | 
				
			||||||
 | 
					  / ___// /___  __/ /_  _______
 | 
				
			||||||
 | 
					  \__ \/ __/ / / / / / / / ___/
 | 
				
			||||||
 | 
					 ___/ / /_/ /_/ / / /_/ (__  ) 
 | 
				
			||||||
 | 
					/____/\__/\__, /_/\__,_/____/  
 | 
				
			||||||
 | 
					         /____/                
 | 
				
			||||||
							
								
								
									
										6
									
								
								ct/headers/telegraf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/telegraf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					   __       __                      ____
 | 
				
			||||||
 | 
					  / /____  / /__  ____ __________ _/ __/
 | 
				
			||||||
 | 
					 / __/ _ \/ / _ \/ __ `/ ___/ __ `/ /_  
 | 
				
			||||||
 | 
					/ /_/  __/ /  __/ /_/ / /  / /_/ / __/  
 | 
				
			||||||
 | 
					\__/\___/_/\___/\__, /_/   \__,_/_/     
 | 
				
			||||||
 | 
					               /____/                   
 | 
				
			||||||
							
								
								
									
										6
									
								
								ct/headers/uhf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/uhf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					   __  ____  ________
 | 
				
			||||||
 | 
					  / / / / / / / ____/
 | 
				
			||||||
 | 
					 / / / / /_/ / /_    
 | 
				
			||||||
 | 
					/ /_/ / __  / __/    
 | 
				
			||||||
 | 
					\____/_/ /_/_/       
 | 
				
			||||||
 | 
					                     
 | 
				
			||||||
							
								
								
									
										15
									
								
								ct/immich.sh
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								ct/immich.sh
									
									
									
									
									
								
							@@ -61,7 +61,7 @@ function update_script() {
 | 
				
			|||||||
    done
 | 
					    done
 | 
				
			||||||
    msg_ok "Image-processing libraries up to date"
 | 
					    msg_ok "Image-processing libraries up to date"
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
  RELEASE="1.140.1"
 | 
					  RELEASE="1.142.0"
 | 
				
			||||||
  if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
 | 
					  if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
 | 
				
			||||||
    msg_info "Stopping Services"
 | 
					    msg_info "Stopping Services"
 | 
				
			||||||
    systemctl stop immich-web
 | 
					    systemctl stop immich-web
 | 
				
			||||||
@@ -160,15 +160,14 @@ EOF
 | 
				
			|||||||
    cd "$SRC_DIR"/machine-learning
 | 
					    cd "$SRC_DIR"/machine-learning
 | 
				
			||||||
    mkdir -p "$ML_DIR"
 | 
					    mkdir -p "$ML_DIR"
 | 
				
			||||||
    export VIRTUAL_ENV="${ML_DIR}"/ml-venv
 | 
					    export VIRTUAL_ENV="${ML_DIR}"/ml-venv
 | 
				
			||||||
    $STD /usr/local/bin/uv venv "$VIRTUAL_ENV"
 | 
					 | 
				
			||||||
    if [[ -f ~/.openvino ]]; then
 | 
					    if [[ -f ~/.openvino ]]; then
 | 
				
			||||||
      msg_info "Updating HW-accelerated machine-learning"
 | 
					      msg_info "Updating HW-accelerated machine-learning"
 | 
				
			||||||
      /usr/local/bin/uv -q sync --extra openvino --no-cache --active
 | 
					      $STD /usr/local/bin/uv sync --extra openvino --no-cache --active
 | 
				
			||||||
      patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so"
 | 
					      patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.11/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-311-x86_64-linux-gnu.so"
 | 
				
			||||||
      msg_ok "Updated HW-accelerated machine-learning"
 | 
					      msg_ok "Updated HW-accelerated machine-learning"
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      msg_info "Updating machine-learning"
 | 
					      msg_info "Updating machine-learning"
 | 
				
			||||||
      /usr/local/bin/uv -q sync --extra cpu --no-cache --active
 | 
					      $STD /usr/local/bin/uv sync --extra cpu --no-cache --active
 | 
				
			||||||
      msg_ok "Updated machine-learning"
 | 
					      msg_ok "Updated machine-learning"
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    cd "$SRC_DIR"
 | 
					    cd "$SRC_DIR"
 | 
				
			||||||
@@ -208,7 +207,7 @@ function compile_libjxl() {
 | 
				
			|||||||
  JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0"
 | 
					  JPEGLI_LIBJPEG_LIBRARY_VERSION="62.3.0"
 | 
				
			||||||
  # : "${LIBJXL_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libjxl.json)}"
 | 
					  # : "${LIBJXL_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libjxl.json)}"
 | 
				
			||||||
  : "${LIBJXL_REVISION:=794a5dcf0d54f9f0b20d288a12e87afb91d20dfc}"
 | 
					  : "${LIBJXL_REVISION:=794a5dcf0d54f9f0b20d288a12e87afb91d20dfc}"
 | 
				
			||||||
  if [[ "${update:-}" ]] || [[ "$LIBJXL_REVISION" != "$(grep 'libjxl' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
					  if [[ "$LIBJXL_REVISION" != "$(grep 'libjxl' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
				
			||||||
    msg_info "Recompiling libjxl"
 | 
					    msg_info "Recompiling libjxl"
 | 
				
			||||||
    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
					    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
				
			||||||
    $STD git clone https://github.com/libjxl/libjxl.git "$SOURCE"
 | 
					    $STD git clone https://github.com/libjxl/libjxl.git "$SOURCE"
 | 
				
			||||||
@@ -289,7 +288,7 @@ function compile_libraw() {
 | 
				
			|||||||
  local update
 | 
					  local update
 | 
				
			||||||
  # : "${LIBRAW_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libraw.json)}"
 | 
					  # : "${LIBRAW_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libraw.json)}"
 | 
				
			||||||
  : "${LIBRAW_REVISION:=09bea31181b43e97959ee5452d91e5bc66365f1f}"
 | 
					  : "${LIBRAW_REVISION:=09bea31181b43e97959ee5452d91e5bc66365f1f}"
 | 
				
			||||||
  if [[ "${update:-}" ]] || [[ "$LIBRAW_REVISION" != "$(grep 'libraw' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
					  if [[ "$LIBRAW_REVISION" != "$(grep 'libraw' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
				
			||||||
    msg_info "Recompiling libraw"
 | 
					    msg_info "Recompiling libraw"
 | 
				
			||||||
    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
					    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
				
			||||||
    $STD git clone https://github.com/libraw/libraw.git "$SOURCE"
 | 
					    $STD git clone https://github.com/libraw/libraw.git "$SOURCE"
 | 
				
			||||||
@@ -311,7 +310,7 @@ function compile_imagemagick() {
 | 
				
			|||||||
  SOURCE=$SOURCE_DIR/imagemagick
 | 
					  SOURCE=$SOURCE_DIR/imagemagick
 | 
				
			||||||
  # : "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/imagemagick.json)}"
 | 
					  # : "${IMAGEMAGICK_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/imagemagick.json)}"
 | 
				
			||||||
  : "${IMAGEMAGICK_REVISION:=8289a3388a085ad5ae81aa6812f21554bdfd54f2}"
 | 
					  : "${IMAGEMAGICK_REVISION:=8289a3388a085ad5ae81aa6812f21554bdfd54f2}"
 | 
				
			||||||
  if [[ "${update:-}" ]] || [[ "$IMAGEMAGICK_REVISION" != "$(grep 'imagemagick' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
					  if [[ "$IMAGEMAGICK_REVISION" != "$(grep 'imagemagick' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
				
			||||||
    msg_info "Recompiling ImageMagick"
 | 
					    msg_info "Recompiling ImageMagick"
 | 
				
			||||||
    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
					    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
				
			||||||
    $STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE"
 | 
					    $STD git clone https://github.com/ImageMagick/ImageMagick.git "$SOURCE"
 | 
				
			||||||
@@ -332,7 +331,7 @@ function compile_libvips() {
 | 
				
			|||||||
  SOURCE=$SOURCE_DIR/libvips
 | 
					  SOURCE=$SOURCE_DIR/libvips
 | 
				
			||||||
  # : "${LIBVIPS_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libvips.json)}"
 | 
					  # : "${LIBVIPS_REVISION:=$(jq -cr '.revision' "$BASE_DIR"/server/sources/libvips.json)}"
 | 
				
			||||||
  : "${LIBVIPS_REVISION:=8fa37a64547e392d3808eed8d72adab7e02b3d00}"
 | 
					  : "${LIBVIPS_REVISION:=8fa37a64547e392d3808eed8d72adab7e02b3d00}"
 | 
				
			||||||
  if [[ "${update:-}" ]] || [[ "$LIBVIPS_REVISION" != "$(grep 'libvips' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
					  if [[ "$LIBVIPS_REVISION" != "$(grep 'libvips' ~/.immich_library_revisions | awk '{print $2}')" ]]; then
 | 
				
			||||||
    msg_info "Recompiling libvips"
 | 
					    msg_info "Recompiling libvips"
 | 
				
			||||||
    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
					    if [[ -d "$SOURCE" ]]; then rm -rf "$SOURCE"; fi
 | 
				
			||||||
    $STD git clone https://github.com/libvips/libvips.git "$SOURCE"
 | 
					    $STD git clone https://github.com/libvips/libvips.git "$SOURCE"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ function update_script() {
 | 
				
			|||||||
    msg_ok "Services Stopped"
 | 
					    msg_ok "Services Stopped"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TMP_TAR=$(mktemp --suffix=.tgz)
 | 
					    TMP_TAR=$(mktemp --suffix=.tgz)
 | 
				
			||||||
    curl -fL# -o "${TMP_TAR}" "https://github.com/ollama/ollama/releases/download/${RELEASE}/ollama-linux-amd64.tgz"
 | 
					    curl -fL# -C - -o "${TMP_TAR}" "https://github.com/ollama/ollama/releases/download/${RELEASE}/ollama-linux-amd64.tgz"
 | 
				
			||||||
    msg_info "Updating Ollama to ${RELEASE}"
 | 
					    msg_info "Updating Ollama to ${RELEASE}"
 | 
				
			||||||
    rm -rf /usr/local/lib/ollama
 | 
					    rm -rf /usr/local/lib/ollama
 | 
				
			||||||
    rm -rf /usr/local/bin/ollama
 | 
					    rm -rf /usr/local/bin/ollama
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,11 +33,17 @@ function update_script() {
 | 
				
			|||||||
    OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
 | 
					    OLLAMA_VERSION=$(ollama -v | awk '{print $NF}')
 | 
				
			||||||
    RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
 | 
					    RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
 | 
				
			||||||
    if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
 | 
					    if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then
 | 
				
			||||||
 | 
					      msg_info "Stopping Service"
 | 
				
			||||||
 | 
					      systemctl stop ollama
 | 
				
			||||||
 | 
					      msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					      curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz
 | 
				
			||||||
      rm -rf /usr/lib/ollama
 | 
					      rm -rf /usr/lib/ollama
 | 
				
			||||||
      rm -rf /usr/bin/ollama
 | 
					      rm -rf /usr/bin/ollama
 | 
				
			||||||
      curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz
 | 
					 | 
				
			||||||
      tar -C /usr -xzf ollama-linux-amd64.tgz
 | 
					      tar -C /usr -xzf ollama-linux-amd64.tgz
 | 
				
			||||||
      rm -rf ollama-linux-amd64.tgz
 | 
					      rm -rf ollama-linux-amd64.tgz
 | 
				
			||||||
 | 
					      msg_info "Starting Service"
 | 
				
			||||||
 | 
					      systemctl start ollama
 | 
				
			||||||
 | 
					      msg_info "Started Service"
 | 
				
			||||||
      msg_ok "Ollama updated to version $RELEASE"
 | 
					      msg_ok "Ollama updated to version $RELEASE"
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      msg_ok "Ollama is already up to date."
 | 
					      msg_ok "Ollama is already up to date."
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,13 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/m
 | 
				
			|||||||
# Source: https://github.com/openziti/ziti
 | 
					# Source: https://github.com/openziti/ziti
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="openziti-controller"
 | 
					APP="openziti-controller"
 | 
				
			||||||
var_tags="network;openziti-controller"
 | 
					var_tags="${var_tags:-network;openziti-controller}"
 | 
				
			||||||
var_cpu="2"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="1024"
 | 
					var_ram="${var_ram:-1024}"
 | 
				
			||||||
var_disk="8"
 | 
					var_disk="${var_disk:-8}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
@@ -20,18 +20,18 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
   header_info
 | 
					  header_info
 | 
				
			||||||
   check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
   check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
   if [[ ! -d /opt/openziti ]]; then
 | 
					  if [[ ! -d /opt/openziti ]]; then
 | 
				
			||||||
      msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
      exit
 | 
					    exit
 | 
				
			||||||
   fi
 | 
					  fi
 | 
				
			||||||
   msg_info "Updating $APP LXC"
 | 
					  msg_info "Updating $APP LXC"
 | 
				
			||||||
   $STD apt-get update
 | 
					  $STD apt-get update
 | 
				
			||||||
   $STD apt-get -y upgrade
 | 
					  $STD apt-get -y upgrade
 | 
				
			||||||
   msg_ok "Updated $APP LXC"
 | 
					  msg_ok "Updated $APP LXC"
 | 
				
			||||||
   exit
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
@@ -41,4 +41,4 @@ description
 | 
				
			|||||||
msg_ok "Completed Successfully!\n"
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:<port>/zac${CL}"
 | 
					echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:<port>/zac${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ function update_script() {
 | 
				
			|||||||
    msg_ok "Stopped Services"
 | 
					    msg_ok "Stopped Services"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cp /opt/palmr/apps/server/.env /opt/palmr.env
 | 
					    cp /opt/palmr/apps/server/.env /opt/palmr.env
 | 
				
			||||||
 | 
					    rm -rf /opt/palmr
 | 
				
			||||||
    fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr"
 | 
					    fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)"
 | 
					    PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)"
 | 
				
			||||||
@@ -42,9 +43,9 @@ function update_script() {
 | 
				
			|||||||
    cd /opt/palmr/apps/server
 | 
					    cd /opt/palmr/apps/server
 | 
				
			||||||
    mv /opt/palmr.env /opt/palmr/apps/server/.env
 | 
					    mv /opt/palmr.env /opt/palmr/apps/server/.env
 | 
				
			||||||
    $STD pnpm install
 | 
					    $STD pnpm install
 | 
				
			||||||
    $STD pnpm dlx prisma generate
 | 
					    $STD npx prisma generate
 | 
				
			||||||
    $STD pnpm dlx prisma migrate deploy
 | 
					    $STD npx prisma migrate deploy
 | 
				
			||||||
    $STD pnpm dlx prisma db push
 | 
					    $STD npx prisma db push
 | 
				
			||||||
    $STD pnpm build
 | 
					    $STD pnpm build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cd /opt/palmr/apps/web
 | 
					    cd /opt/palmr/apps/web
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,73 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					 | 
				
			||||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
					 | 
				
			||||||
# Copyright (c) 2021-2025 tteck
 | 
					 | 
				
			||||||
# Author: tteck (tteckster)
 | 
					 | 
				
			||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
					 | 
				
			||||||
# Source: https://stonith404.github.io/pingvin-share/introduction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
APP="Pingvin"
 | 
					 | 
				
			||||||
var_tags="${var_tags:-sharing}"
 | 
					 | 
				
			||||||
var_cpu="${var_cpu:-2}"
 | 
					 | 
				
			||||||
var_ram="${var_ram:-2048}"
 | 
					 | 
				
			||||||
var_disk="${var_disk:-8}"
 | 
					 | 
				
			||||||
var_os="${var_os:-debian}"
 | 
					 | 
				
			||||||
var_version="${var_version:-12}"
 | 
					 | 
				
			||||||
var_unprivileged="${var_unprivileged:-1}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
header_info "$APP"
 | 
					 | 
				
			||||||
variables
 | 
					 | 
				
			||||||
color
 | 
					 | 
				
			||||||
catch_errors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function update_script() {
 | 
					 | 
				
			||||||
  header_info
 | 
					 | 
				
			||||||
  check_container_storage
 | 
					 | 
				
			||||||
  check_container_resources
 | 
					 | 
				
			||||||
  if [[ ! -d /opt/pingvin-share ]]; then
 | 
					 | 
				
			||||||
    msg_error "No ${APP} Installation Found!"
 | 
					 | 
				
			||||||
    exit
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  RELEASE=$(curl -fsSL https://api.github.com/repos/stonith404/pingvin-share/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
					 | 
				
			||||||
  if [[ ! -f /opt/pingvin_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/pingvin_version.txt)" ]]; then
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg_info "Stopping Pingvin Share"
 | 
					 | 
				
			||||||
    systemctl stop pm2-root.service
 | 
					 | 
				
			||||||
    msg_ok "Stopped Pingvin Share"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg_info "Updating Pingvin Share to v${RELEASE}"
 | 
					 | 
				
			||||||
    cd /opt
 | 
					 | 
				
			||||||
    curl -fsSL "https://github.com/stonith404/pingvin-share/archive/refs/tags/v${RELEASE}.zip" -o $(basename "https://github.com/stonith404/pingvin-share/archive/refs/tags/v${RELEASE}.zip")
 | 
					 | 
				
			||||||
    $STD unzip v${RELEASE}.zip
 | 
					 | 
				
			||||||
    cp -rf pingvin-share-${RELEASE}/* /opt/pingvin-share
 | 
					 | 
				
			||||||
    cd /opt/pingvin-share
 | 
					 | 
				
			||||||
    cd backend
 | 
					 | 
				
			||||||
    $STD npm install
 | 
					 | 
				
			||||||
    $STD npm run build
 | 
					 | 
				
			||||||
    cd ../frontend
 | 
					 | 
				
			||||||
    $STD npm install
 | 
					 | 
				
			||||||
    $STD npm run build
 | 
					 | 
				
			||||||
    echo "${RELEASE}" >"/opt/pingvin_version.txt"
 | 
					 | 
				
			||||||
    rm -rf /opt/v${RELEASE}.zip
 | 
					 | 
				
			||||||
    rm -rf /opt/pingvin-share-${RELEASE}
 | 
					 | 
				
			||||||
    msg_ok "Updated Pingvin Share to v${RELEASE}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg_info "Starting Pingvin Share"
 | 
					 | 
				
			||||||
    systemctl start pm2-root.service
 | 
					 | 
				
			||||||
    msg_ok "Started Pingvin Share"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    msg_ok "Updated Successfully"
 | 
					 | 
				
			||||||
    exit
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    msg_ok "No update required. Pingvin Share is already at v${RELEASE}."
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
start
 | 
					 | 
				
			||||||
build_container
 | 
					 | 
				
			||||||
description
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
msg_ok "Completed Successfully!\n"
 | 
					 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"
 | 
					 | 
				
			||||||
							
								
								
									
										25
									
								
								ct/planka.sh
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								ct/planka.sh
									
									
									
									
									
								
							@@ -34,12 +34,13 @@ function update_script() {
 | 
				
			|||||||
    msg_ok "Stopped $APP"
 | 
					    msg_ok "Stopped $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Backing up data"
 | 
					    msg_info "Backing up data"
 | 
				
			||||||
    mkdir -p /opt/planka-backup/{favicons,user-avatars,background-images,attachments}
 | 
					    BK="/opt/planka-backup"
 | 
				
			||||||
    mv /opt/planka/.env /opt/planka-backup
 | 
					    mkdir -p "$BK"/{favicons,user-avatars,background-images,attachments}
 | 
				
			||||||
    [ -d /opt/planka/public/favicons ] && find /opt/planka/public/favicons -maxdepth 1 -type f -exec mv -t /opt/planka-backup/favicons {} +
 | 
					    [ -f /opt/planka/.env ] && mv /opt/planka/.env "$BK"/
 | 
				
			||||||
    [ -d /opt/planka/public/user-avatars ] && find /opt/planka/public/user-avatars -maxdepth 1 -type f -exec mv -t /opt/planka-backup/user-avatars {} +
 | 
					    [ -d /opt/planka/public/favicons ] && cp -a /opt/planka/public/favicons/. "$BK/favicons/"
 | 
				
			||||||
    [ -d /opt/planka/public/background-images ] && find /opt/planka/public/background-images -maxdepth 1 -type f -exec mv -t /opt/planka-backup/background-images {} +
 | 
					    [ -d /opt/planka/public/user-avatars ] && cp -a /opt/planka/public/user-avatars/. "$BK/user-avatars/"
 | 
				
			||||||
    [ -d /opt/planka/private/attachments ] && find /opt/planka/private/attachments -maxdepth 1 -type f -exec mv -t /opt/planka-backup/attachments {} +
 | 
					    [ -d /opt/planka/public/background-images ] && cp -a /opt/planka/public/background-images/. "$BK/background-images/"
 | 
				
			||||||
 | 
					    [ -d /opt/planka/private/attachments ] && cp -a /opt/planka/private/attachments/. "$BK/attachments/"
 | 
				
			||||||
    rm -rf /opt/planka
 | 
					    rm -rf /opt/planka
 | 
				
			||||||
    msg_ok "Backed up data"
 | 
					    msg_ok "Backed up data"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,11 +52,13 @@ function update_script() {
 | 
				
			|||||||
    msg_ok "Updated Frontend"
 | 
					    msg_ok "Updated Frontend"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Restoring data"
 | 
					    msg_info "Restoring data"
 | 
				
			||||||
    mv /opt/planka-backup/.env /opt/planka/
 | 
					    [ -f "$BK/.env" ] && mv "$BK/.env" /opt/planka/.env
 | 
				
			||||||
    [ -d /opt/planka-backup/favicons ] && find /opt/planka-backup/favicons -maxdepth 1 -type f -exec mv -t /opt/planka/public/favicons {} +
 | 
					    mkdir -p /opt/planka/public/{favicons,user-avatars,background-images} /opt/planka/private/attachments
 | 
				
			||||||
    [ -d /opt/planka-backup/user-avatars ] && find /opt/planka-backup/user-avatars -maxdepth 1 -type f -exec mv -t /opt/planka/public/user-avatars {} +
 | 
					    [ -d "$BK/favicons" ] && cp -a "$BK/favicons/." /opt/planka/public/favicons/
 | 
				
			||||||
    [ -d /opt/planka-backup/background-images ] && find /opt/planka-backup/background-images -maxdepth 1 -type f -exec mv -t /opt/planka/public/background-images {} +
 | 
					    [ -d "$BK/user-avatars" ] && cp -a "$BK/user-avatars/." /opt/planka/public/user-avatars/
 | 
				
			||||||
    [ -d /opt/planka-backup/attachments ] && find /opt/planka-backup/attachments -maxdepth 1 -type f -exec mv -t /opt/planka/private/attachments {} +
 | 
					    [ -d "$BK/background-images" ] && cp -a "$BK/background-images/." /opt/planka/public/background-images/
 | 
				
			||||||
 | 
					    [ -d "$BK/attachments" ] && cp -a "$BK/attachments/." /opt/planka/private/attachments/
 | 
				
			||||||
 | 
					    rm -rf "$BK"
 | 
				
			||||||
    msg_ok "Restored data"
 | 
					    msg_ok "Restored data"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Starting $APP"
 | 
					    msg_info "Starting $APP"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,16 @@ function update_script() {
 | 
				
			|||||||
        msg_error "No ${APP} Installation Found!"
 | 
					        msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
        exit
 | 
					        exit
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then
 | 
				
			||||||
 | 
					        msg_info "Updating outdated outdated source formats"
 | 
				
			||||||
 | 
					        echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" > /etc/apt/sources.list.d/pdm-test.list
 | 
				
			||||||
 | 
					        curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg
 | 
				
			||||||
 | 
					        rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list
 | 
				
			||||||
 | 
					        $STD apt-get update
 | 
				
			||||||
 | 
					        msg_ok "Updated old sources"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Updating $APP LXC"
 | 
					    msg_info "Updating $APP LXC"
 | 
				
			||||||
    $STD apt-get update
 | 
					    $STD apt-get update
 | 
				
			||||||
    $STD apt-get -y upgrade
 | 
					    $STD apt-get -y upgrade
 | 
				
			||||||
@@ -41,4 +51,4 @@ description
 | 
				
			|||||||
msg_ok "Completed Successfully!\n"
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}"
 | 
					echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								ct/pulse.sh
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								ct/pulse.sh
									
									
									
									
									
								
							@@ -28,10 +28,6 @@ function update_script() {
 | 
				
			|||||||
    exit
 | 
					    exit
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if [[ ! -f ~/.pulse ]]; then
 | 
					 | 
				
			||||||
    msg_error "Old Installation Found! Please recreate the container due big changes in the software."
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
  fi
 | 
					 | 
				
			||||||
  if check_for_gh_release "pulse" "rcourtman/Pulse"; then
 | 
					  if check_for_gh_release "pulse" "rcourtman/Pulse"; then
 | 
				
			||||||
    SERVICE_PATH="/etc/systemd/system"
 | 
					    SERVICE_PATH="/etc/systemd/system"
 | 
				
			||||||
    msg_info "Stopping Services"
 | 
					    msg_info "Stopping Services"
 | 
				
			||||||
@@ -43,19 +39,20 @@ function update_script() {
 | 
				
			|||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "pulse" "rcourtman/Pulse" "prebuild" "latest" "/opt/pulse" "*-linux-amd64.tar.gz"
 | 
					    fetch_and_deploy_gh_release "pulse" "rcourtman/Pulse" "prebuild" "latest" "/opt/pulse" "*-linux-amd64.tar.gz"
 | 
				
			||||||
 | 
					    ln -sf /opt/pulse/bin/pulse /usr/local/bin/pulse
 | 
				
			||||||
    chown -R pulse:pulse /etc/pulse /opt/pulse
 | 
					    chown -R pulse:pulse /etc/pulse /opt/pulse
 | 
				
			||||||
    if [[ -f "$SERVICE_PATH"/pulse.service ]]; then
 | 
					    if [[ -f "$SERVICE_PATH"/pulse-backend.service ]]; then
 | 
				
			||||||
      mv "$SERVICE_PATH"/pulse.service "$SERVICE_PATH"/pulse-backend.service
 | 
					      mv "$SERVICE_PATH"/pulse-backend.service "$SERVICE_PATH"/pulse.service
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
    sed -i -e 's|pulse/pulse|pulse/bin/pulse|' \
 | 
					    sed -i -e 's|pulse/pulse|pulse/bin/pulse|' \
 | 
				
			||||||
      -e 's/^Environment="API.*$//' "$SERVICE_PATH"/pulse-backend.service
 | 
					      -e 's/^Environment="API.*$//' "$SERVICE_PATH"/pulse.service
 | 
				
			||||||
    systemctl daemon-reload
 | 
					    systemctl daemon-reload
 | 
				
			||||||
    if grep -q 'pulse-home:/bin/bash' /etc/passwd; then
 | 
					    if grep -q 'pulse-home:/bin/bash' /etc/passwd; then
 | 
				
			||||||
      usermod -s /usr/sbin/nologin pulse
 | 
					      usermod -s /usr/sbin/nologin pulse
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Starting Services"
 | 
					    msg_info "Starting Services"
 | 
				
			||||||
    systemctl start pulse-backend
 | 
					    systemctl start pulse
 | 
				
			||||||
    msg_ok "Started Services"
 | 
					    msg_ok "Started Services"
 | 
				
			||||||
    msg_ok "Updated Successfully"
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ function update_script() {
 | 
				
			|||||||
    msg_ok "Stopped Service"
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-x64.tar.xz"
 | 
					    fetch_and_deploy_gh_release "recyclarr" "recyclarr/recyclarr" "prebuild" "latest" "/usr/local/bin" "recyclarr-linux-x64.tar.xz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Starting Service"
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
    systemctl start recyclarr
 | 
					    systemctl start recyclarr
 | 
				
			||||||
    msg_ok "Started Service"
 | 
					    msg_ok "Started Service"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								ct/stylus.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ct/stylus.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: luismco
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://github.com/mmastrac/stylus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="Stylus"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-network}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-1}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-1024}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-2}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					var_fuse="${var_fuse:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					  header_info
 | 
				
			||||||
 | 
					  check_container_storage
 | 
				
			||||||
 | 
					  check_container_resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [[ ! -d /opt/stylus ]]; then
 | 
				
			||||||
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if check_for_gh_release "stylus" "mmastrac/stylus"; then
 | 
				
			||||||
 | 
					    msg_info "Stopping $APP"
 | 
				
			||||||
 | 
					    systemctl stop stylus
 | 
				
			||||||
 | 
					    msg_ok "Stopped $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting $APP"
 | 
				
			||||||
 | 
					    systemctl start stylus
 | 
				
			||||||
 | 
					    msg_ok "Started $APP"
 | 
				
			||||||
 | 
					    msg_ok "Update Successful"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}"
 | 
				
			||||||
							
								
								
									
										52
									
								
								ct/telegraf.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								ct/telegraf.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: CrazyWolf13
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://github.com/influxdata/telegraf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="telegraf"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-collector;metrics}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-1}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-1024}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-4}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					  header_info
 | 
				
			||||||
 | 
					  check_container_storage
 | 
				
			||||||
 | 
					  check_container_resources
 | 
				
			||||||
 | 
					  if [[ ! -f /etc/telegraf/telegraf.conf ]]; then
 | 
				
			||||||
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  msg_info "Stopping $APP"
 | 
				
			||||||
 | 
					  systemctl stop telegraf
 | 
				
			||||||
 | 
					  msg_ok "Stopped $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  msg_info "Updating $APP"
 | 
				
			||||||
 | 
					  $STD apt-get update
 | 
				
			||||||
 | 
					  $STD apt-get upgrade telegraf -y
 | 
				
			||||||
 | 
					  msg_ok "Updated $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  msg_info "Starting $APP"
 | 
				
			||||||
 | 
					  systemctl start telegraf
 | 
				
			||||||
 | 
					  msg_ok "Started $APP"
 | 
				
			||||||
 | 
					  msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
							
								
								
									
										11
									
								
								ct/tududi.sh
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ct/tududi.sh
									
									
									
									
									
								
							@@ -27,17 +27,22 @@ function update_script() {
 | 
				
			|||||||
    msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  NODE_VERSION="22" setup_nodejs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if check_for_gh_release "tududi" "chrisvel/tududi"; then
 | 
					  if check_for_gh_release "tududi" "chrisvel/tududi"; then
 | 
				
			||||||
    msg_info "Stopping Service"
 | 
					    msg_info "Stopping Service"
 | 
				
			||||||
    systemctl stop tududi
 | 
					    systemctl stop tududi
 | 
				
			||||||
    msg_ok "Stopped Service"
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Remove and backup Files"
 | 
					    msg_info "Remove and backup Files"
 | 
				
			||||||
 | 
					    DB="$(sed -n '/^DB_FILE/s/[^=]*=//p' /opt/tududi/backend/.env)"
 | 
				
			||||||
 | 
					    export DB_FILE="$DB"
 | 
				
			||||||
    cp /opt/tududi/backend/.env /opt/tududi.env
 | 
					    cp /opt/tududi/backend/.env /opt/tududi.env
 | 
				
			||||||
    rm -rf /opt/tududi/backend/dist
 | 
					    rm -rf /opt/tududi/backend/dist
 | 
				
			||||||
    msg_ok "Backup and removed Files"
 | 
					    msg_ok "Backup and removed Files"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "v0.80" "/opt/tududi"
 | 
					    fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "latest" "/opt/tududi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Updating ${APP}"
 | 
					    msg_info "Updating ${APP}"
 | 
				
			||||||
    cd /opt/tududi
 | 
					    cd /opt/tududi
 | 
				
			||||||
@@ -48,6 +53,10 @@ function update_script() {
 | 
				
			|||||||
    mv ./public/locales ./backend/dist
 | 
					    mv ./public/locales ./backend/dist
 | 
				
			||||||
    mv ./public/favicon.* ./backend/dist
 | 
					    mv ./public/favicon.* ./backend/dist
 | 
				
			||||||
    mv /opt/tududi.env /opt/tududi/.env
 | 
					    mv /opt/tududi.env /opt/tududi/.env
 | 
				
			||||||
 | 
					    sed -i -e 's|/tududi$|/tududi/backend|' \
 | 
				
			||||||
 | 
					      -e 's|npm run start|bash /opt/tududi/backend/cmd/start.sh|' \
 | 
				
			||||||
 | 
					      /etc/systemd/system/tududi.service
 | 
				
			||||||
 | 
					    systemctl daemon-reload
 | 
				
			||||||
    msg_ok "Updated $APP"
 | 
					    msg_ok "Updated $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Starting Service"
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										63
									
								
								ct/uhf.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								ct/uhf.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: zackwithak13
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://www.uhfapp.com/server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="UHF"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-media}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-8}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					  header_info
 | 
				
			||||||
 | 
					  check_container_storage
 | 
				
			||||||
 | 
					  check_container_resources
 | 
				
			||||||
 | 
					  if [[ ! -d /opt/uhf-server ]]; then
 | 
				
			||||||
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  if check_for_gh_release "uhf-server" "swapplications/uhf-server-dist"; then
 | 
				
			||||||
 | 
					    msg_info "Stopping Service"
 | 
				
			||||||
 | 
					    systemctl stop uhf-server
 | 
				
			||||||
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Updating ${APP} LXC"
 | 
				
			||||||
 | 
					    $STD apt-get update
 | 
				
			||||||
 | 
					    $STD apt-get -y upgrade
 | 
				
			||||||
 | 
					    msg_ok "Updated ${APP} LXC"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip"
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
 | 
					    systemctl start uhf-server
 | 
				
			||||||
 | 
					    msg_ok "Started Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Cleaning up"
 | 
				
			||||||
 | 
					    $STD apt-get -y autoremove
 | 
				
			||||||
 | 
					    $STD apt-get -y autoclean
 | 
				
			||||||
 | 
					    msg_ok "Cleaned"
 | 
				
			||||||
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7568${CL}"
 | 
				
			||||||
@@ -6,13 +6,13 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
 | 
				
			|||||||
# Source: https://github.com/PCJones/UmlautAdaptarr
 | 
					# Source: https://github.com/PCJones/UmlautAdaptarr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="UmlautAdaptarr"
 | 
					APP="UmlautAdaptarr"
 | 
				
			||||||
var_tags="arr"
 | 
					var_tags="${var_tags:-arr}"
 | 
				
			||||||
var_cpu="1"
 | 
					var_cpu="${var_cpu:-1}"
 | 
				
			||||||
var_ram="512"
 | 
					var_ram="${var_ram:-512}"
 | 
				
			||||||
var_disk="4"
 | 
					var_disk="${var_disk:-4}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
@@ -20,33 +20,33 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
    header_info
 | 
					  header_info
 | 
				
			||||||
    check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
    check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
    if [[ ! -d /opt/UmlautAdaptarr ]]; then
 | 
					  if [[ ! -d /opt/UmlautAdaptarr ]]; then
 | 
				
			||||||
        msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    RELEASE=$(curl -fsSL https://api.github.com/repos/PCJones/Umlautadaptarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
					 | 
				
			||||||
    if [[ ! -f /opt/UmlautAdaptarr_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/UmlautAdaptarr_version.txt)" ]]; then
 | 
					 | 
				
			||||||
        msg_info "Stopping Service"
 | 
					 | 
				
			||||||
        systemctl stop umlautadaptarr
 | 
					 | 
				
			||||||
        msg_ok "Stopped Service"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        msg_info "Updating ${APP}"
 | 
					 | 
				
			||||||
        temp_file=$(mktemp)
 | 
					 | 
				
			||||||
        curl -fsSL "https://github.com/PCJones/Umlautadaptarr/releases/download/${RELEASE}/linux-x64.zip" -o $temp_file
 | 
					 | 
				
			||||||
        $STD unzip -u $temp_file '*/**' -d /opt/UmlautAdaptarr
 | 
					 | 
				
			||||||
        msg_ok "Updated ${APP}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        msg_info "Starting Service"
 | 
					 | 
				
			||||||
        systemctl start umlautadaptarr
 | 
					 | 
				
			||||||
        msg_ok "Started Service"
 | 
					 | 
				
			||||||
        msg_ok "$APP has been updated to ${RELEASE}."
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        msg_ok "No update required. ${APP} is already at ${RELEASE}"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  RELEASE=$(curl -fsSL https://api.github.com/repos/PCJones/Umlautadaptarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
				
			||||||
 | 
					  if [[ ! -f /opt/UmlautAdaptarr_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/UmlautAdaptarr_version.txt)" ]]; then
 | 
				
			||||||
 | 
					    msg_info "Stopping Service"
 | 
				
			||||||
 | 
					    systemctl stop umlautadaptarr
 | 
				
			||||||
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Updating ${APP}"
 | 
				
			||||||
 | 
					    temp_file=$(mktemp)
 | 
				
			||||||
 | 
					    curl -fsSL "https://github.com/PCJones/Umlautadaptarr/releases/download/${RELEASE}/linux-x64.zip" -o $temp_file
 | 
				
			||||||
 | 
					    $STD unzip -u $temp_file '*/**' -d /opt/UmlautAdaptarr
 | 
				
			||||||
 | 
					    msg_ok "Updated ${APP}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
 | 
					    systemctl start umlautadaptarr
 | 
				
			||||||
 | 
					    msg_ok "Started Service"
 | 
				
			||||||
 | 
					    msg_ok "$APP has been updated to ${RELEASE}."
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    msg_ok "No update required. ${APP} is already at ${RELEASE}"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
build_container
 | 
					build_container
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,12 @@ function update_script() {
 | 
				
			|||||||
    msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 | 
					  if ! dpkg -s git >/dev/null 2>&1; then
 | 
				
			||||||
 | 
					    msg_info "Installing git"
 | 
				
			||||||
 | 
					    $STD apt-get update
 | 
				
			||||||
 | 
					    $STD apt-get install -y git
 | 
				
			||||||
 | 
					    msg_ok "Installed git"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
  apt-get update
 | 
					  apt-get update
 | 
				
			||||||
  apt-get -y upgrade
 | 
					  apt-get -y upgrade
 | 
				
			||||||
  if [[ -d /etc/wgdashboard ]]; then
 | 
					  if [[ -d /etc/wgdashboard ]]; then
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								ct/wizarr.sh
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								ct/wizarr.sh
									
									
									
									
									
								
							@@ -39,20 +39,23 @@ function update_script() {
 | 
				
			|||||||
    msg_info "Creating Backup"
 | 
					    msg_info "Creating Backup"
 | 
				
			||||||
    BACKUP_FILE="/opt/wizarr_backup_$(date +%F).tar.gz"
 | 
					    BACKUP_FILE="/opt/wizarr_backup_$(date +%F).tar.gz"
 | 
				
			||||||
    $STD tar -czf "$BACKUP_FILE" /opt/wizarr/{.env,start.sh} /opt/wizarr/database/ &>/dev/null
 | 
					    $STD tar -czf "$BACKUP_FILE" /opt/wizarr/{.env,start.sh} /opt/wizarr/database/ &>/dev/null
 | 
				
			||||||
 | 
					    rm -rf /opt/wizarr/migrations/versions/*
 | 
				
			||||||
    msg_ok "Backup Created"
 | 
					    msg_ok "Backup Created"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr"
 | 
					    fetch_and_deploy_gh_release "wizarr" "wizarrrr/wizarr"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Updating $APP"
 | 
					    msg_info "Updating $APP"
 | 
				
			||||||
    cd /opt/wizarr
 | 
					    cd /opt/wizarr
 | 
				
			||||||
    $STD /usr/local/bin/uv lock
 | 
					    $STD /usr/local/bin/uv sync --frozen
 | 
				
			||||||
    $STD /usr/local/bin/uv sync --locked
 | 
					    $STD /usr/local/bin/uv run --frozen pybabel compile -d app/translations
 | 
				
			||||||
    $STD /usr/local/bin/uv run pybabel compile -d app/translations
 | 
					 | 
				
			||||||
    $STD npm --prefix app/static install
 | 
					    $STD npm --prefix app/static install
 | 
				
			||||||
    $STD npm --prefix app/static run build:css
 | 
					    $STD npm --prefix app/static run build:css
 | 
				
			||||||
    mkdir -p ./.cache
 | 
					    mkdir -p ./.cache
 | 
				
			||||||
    $STD tar -xf "$BACKUP_FILE" --directory=/
 | 
					    $STD tar -xf "$BACKUP_FILE" --directory=/
 | 
				
			||||||
    $STD /usr/local/bin/uv run flask db upgrade
 | 
					    $STD /usr/local/bin/uv run --frozen flask db upgrade
 | 
				
			||||||
 | 
					    if ! grep -q 'frozen' /opt/wizarr/start.sh; then
 | 
				
			||||||
 | 
					      sed -i 's/run/& --frozen/' /opt/wizarr/start.sh
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
    msg_ok "Updated $APP"
 | 
					    msg_ok "Updated $APP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Starting $APP"
 | 
					    msg_info "Starting $APP"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								frontend/components.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								frontend/components.json
									
									
									
										generated
									
									
									
								
							@@ -13,5 +13,8 @@
 | 
				
			|||||||
  "aliases": {
 | 
					  "aliases": {
 | 
				
			||||||
    "components": "@/components",
 | 
					    "components": "@/components",
 | 
				
			||||||
    "utils": "@/lib/utils"
 | 
					    "utils": "@/lib/utils"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "registries": {
 | 
				
			||||||
 | 
					    "@animate-ui": "https://animate-ui.com/r/{name}.json"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										134
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -31,8 +31,9 @@
 | 
				
			|||||||
        "date-fns": "^4.1.0",
 | 
					        "date-fns": "^4.1.0",
 | 
				
			||||||
        "framer-motion": "^11.18.2",
 | 
					        "framer-motion": "^11.18.2",
 | 
				
			||||||
        "fuse.js": "^7.1.0",
 | 
					        "fuse.js": "^7.1.0",
 | 
				
			||||||
        "lucide-react": "^0.453.0",
 | 
					        "lucide-react": "^0.542.0",
 | 
				
			||||||
        "mini-svg-data-uri": "^1.4.4",
 | 
					        "mini-svg-data-uri": "^1.4.4",
 | 
				
			||||||
 | 
					        "motion": "^12.23.12",
 | 
				
			||||||
        "next": "15.5.2",
 | 
					        "next": "15.5.2",
 | 
				
			||||||
        "next-themes": "^0.4.4",
 | 
					        "next-themes": "^0.4.4",
 | 
				
			||||||
        "nuqs": "^2.4.1",
 | 
					        "nuqs": "^2.4.1",
 | 
				
			||||||
@@ -46,6 +47,7 @@
 | 
				
			|||||||
        "react-dom": "19.0.0",
 | 
					        "react-dom": "19.0.0",
 | 
				
			||||||
        "react-icons": "^5.5.0",
 | 
					        "react-icons": "^5.5.0",
 | 
				
			||||||
        "react-simple-typewriter": "^5.0.1",
 | 
					        "react-simple-typewriter": "^5.0.1",
 | 
				
			||||||
 | 
					        "react-use-measure": "^2.1.7",
 | 
				
			||||||
        "sharp": "^0.33.5",
 | 
					        "sharp": "^0.33.5",
 | 
				
			||||||
        "simple-icons": "^13.21.0",
 | 
					        "simple-icons": "^13.21.0",
 | 
				
			||||||
        "sonner": "^1.7.4",
 | 
					        "sonner": "^1.7.4",
 | 
				
			||||||
@@ -7664,11 +7666,14 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/fdir": {
 | 
					    "node_modules/fdir": {
 | 
				
			||||||
      "version": "6.4.6",
 | 
					      "version": "6.5.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
 | 
					      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=12.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
      "peerDependencies": {
 | 
					      "peerDependencies": {
 | 
				
			||||||
        "picomatch": "^3 || ^4"
 | 
					        "picomatch": "^3 || ^4"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@@ -9293,12 +9298,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/lucide-react": {
 | 
					    "node_modules/lucide-react": {
 | 
				
			||||||
      "version": "0.453.0",
 | 
					      "version": "0.542.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.453.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==",
 | 
					      "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==",
 | 
				
			||||||
      "license": "ISC",
 | 
					      "license": "ISC",
 | 
				
			||||||
      "peerDependencies": {
 | 
					      "peerDependencies": {
 | 
				
			||||||
        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
 | 
					        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/magic-string": {
 | 
					    "node_modules/magic-string": {
 | 
				
			||||||
@@ -10339,6 +10344,32 @@
 | 
				
			|||||||
        "pathe": "^2.0.1"
 | 
					        "pathe": "^2.0.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "framer-motion": "^12.23.12",
 | 
				
			||||||
 | 
					        "tslib": "^2.4.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": "*",
 | 
				
			||||||
 | 
					        "react": "^18.0.0 || ^19.0.0",
 | 
				
			||||||
 | 
					        "react-dom": "^18.0.0 || ^19.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/motion-dom": {
 | 
					    "node_modules/motion-dom": {
 | 
				
			||||||
      "version": "11.18.1",
 | 
					      "version": "11.18.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
 | 
				
			||||||
@@ -10354,6 +10385,48 @@
 | 
				
			|||||||
      "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
 | 
					      "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/framer-motion": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "motion-dom": "^12.23.12",
 | 
				
			||||||
 | 
					        "motion-utils": "^12.23.6",
 | 
				
			||||||
 | 
					        "tslib": "^2.4.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": "*",
 | 
				
			||||||
 | 
					        "react": "^18.0.0 || ^19.0.0",
 | 
				
			||||||
 | 
					        "react-dom": "^18.0.0 || ^19.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/motion-dom": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "motion-utils": "^12.23.6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/motion-utils": {
 | 
				
			||||||
 | 
					      "version": "12.23.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
 | 
				
			||||||
 | 
					      "license": "MIT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/ms": {
 | 
					    "node_modules/ms": {
 | 
				
			||||||
      "version": "2.1.3",
 | 
					      "version": "2.1.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
 | 
				
			||||||
@@ -11362,9 +11435,9 @@
 | 
				
			|||||||
      "license": "ISC"
 | 
					      "license": "ISC"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/picomatch": {
 | 
					    "node_modules/picomatch": {
 | 
				
			||||||
      "version": "4.0.2",
 | 
					      "version": "4.0.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
 | 
				
			||||||
      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
 | 
					      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
@@ -11990,6 +12063,21 @@
 | 
				
			|||||||
        "react": ">= 0.14.0"
 | 
					        "react": ">= 0.14.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/react-use-measure": {
 | 
				
			||||||
 | 
					      "version": "2.1.7",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "react": ">=16.13",
 | 
				
			||||||
 | 
					        "react-dom": ">=16.13"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/read-cache": {
 | 
					    "node_modules/read-cache": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
 | 
				
			||||||
@@ -13307,14 +13395,14 @@
 | 
				
			|||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/tinyglobby": {
 | 
					    "node_modules/tinyglobby": {
 | 
				
			||||||
      "version": "0.2.14",
 | 
					      "version": "0.2.15",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
 | 
				
			||||||
      "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
 | 
					      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "fdir": "^6.4.4",
 | 
					        "fdir": "^6.5.0",
 | 
				
			||||||
        "picomatch": "^4.0.2"
 | 
					        "picomatch": "^4.0.3"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
        "node": ">=12.0.0"
 | 
					        "node": ">=12.0.0"
 | 
				
			||||||
@@ -13837,19 +13925,19 @@
 | 
				
			|||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/vite": {
 | 
					    "node_modules/vite": {
 | 
				
			||||||
      "version": "7.0.0",
 | 
					      "version": "7.1.5",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
 | 
				
			||||||
      "integrity": "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==",
 | 
					      "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "peer": true,
 | 
					      "peer": true,
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "esbuild": "^0.25.0",
 | 
					        "esbuild": "^0.25.0",
 | 
				
			||||||
        "fdir": "^6.4.6",
 | 
					        "fdir": "^6.5.0",
 | 
				
			||||||
        "picomatch": "^4.0.2",
 | 
					        "picomatch": "^4.0.3",
 | 
				
			||||||
        "postcss": "^8.5.6",
 | 
					        "postcss": "^8.5.6",
 | 
				
			||||||
        "rollup": "^4.40.0",
 | 
					        "rollup": "^4.43.0",
 | 
				
			||||||
        "tinyglobby": "^0.2.14"
 | 
					        "tinyglobby": "^0.2.15"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "bin": {
 | 
					      "bin": {
 | 
				
			||||||
        "vite": "bin/vite.js"
 | 
					        "vite": "bin/vite.js"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								frontend/package.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								frontend/package.json
									
									
									
										generated
									
									
									
								
							@@ -38,8 +38,9 @@
 | 
				
			|||||||
    "date-fns": "^4.1.0",
 | 
					    "date-fns": "^4.1.0",
 | 
				
			||||||
    "framer-motion": "^11.18.2",
 | 
					    "framer-motion": "^11.18.2",
 | 
				
			||||||
    "fuse.js": "^7.1.0",
 | 
					    "fuse.js": "^7.1.0",
 | 
				
			||||||
    "lucide-react": "^0.453.0",
 | 
					    "lucide-react": "^0.542.0",
 | 
				
			||||||
    "mini-svg-data-uri": "^1.4.4",
 | 
					    "mini-svg-data-uri": "^1.4.4",
 | 
				
			||||||
 | 
					    "motion": "^12.23.12",
 | 
				
			||||||
    "next": "15.5.2",
 | 
					    "next": "15.5.2",
 | 
				
			||||||
    "next-themes": "^0.4.4",
 | 
					    "next-themes": "^0.4.4",
 | 
				
			||||||
    "nuqs": "^2.4.1",
 | 
					    "nuqs": "^2.4.1",
 | 
				
			||||||
@@ -53,6 +54,7 @@
 | 
				
			|||||||
    "react-dom": "19.0.0",
 | 
					    "react-dom": "19.0.0",
 | 
				
			||||||
    "react-icons": "^5.5.0",
 | 
					    "react-icons": "^5.5.0",
 | 
				
			||||||
    "react-simple-typewriter": "^5.0.1",
 | 
					    "react-simple-typewriter": "^5.0.1",
 | 
				
			||||||
 | 
					    "react-use-measure": "^2.1.7",
 | 
				
			||||||
    "sharp": "^0.33.5",
 | 
					    "sharp": "^0.33.5",
 | 
				
			||||||
    "simple-icons": "^13.21.0",
 | 
					    "simple-icons": "^13.21.0",
 | 
				
			||||||
    "sonner": "^1.7.4",
 | 
					    "sonner": "^1.7.4",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-19",
 | 
					  "date_created": "2024-05-19",
 | 
				
			||||||
  "type": "addon",
 | 
					  "type": "addon",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://docs.netbird.io/",
 | 
					  "documentation": "https://docs.netbird.io/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "addon",
 | 
					  "type": "addon",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://tailscale.com/kb/1017/install",
 | 
					  "documentation": "https://tailscale.com/kb/1017/install",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 3142,
 | 
					  "interface_port": 3142,
 | 
				
			||||||
  "documentation": "https://www.unix-ag.uni-kl.de/~bloch/acng/html/index.html",
 | 
					  "documentation": "https://www.unix-ag.uni-kl.de/~bloch/acng/html/index.html",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 13378,
 | 
					  "interface_port": 13378,
 | 
				
			||||||
  "documentation": "https://www.audiobookshelf.org/guides/",
 | 
					  "documentation": "https://www.audiobookshelf.org/guides/",
 | 
				
			||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
      "resources": {
 | 
					      "resources": {
 | 
				
			||||||
        "cpu": 2,
 | 
					        "cpu": 2,
 | 
				
			||||||
        "ram": 2048,
 | 
					        "ram": 2048,
 | 
				
			||||||
        "hdd": 4,
 | 
					        "hdd": 5,
 | 
				
			||||||
        "os": "debian",
 | 
					        "os": "debian",
 | 
				
			||||||
        "version": "12"
 | 
					        "version": "12"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								frontend/public/json/autocaliweb.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								frontend/public/json/autocaliweb.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "Autocaliweb",
 | 
				
			||||||
 | 
					  "slug": "autocaliweb",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    13
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-10",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "interface_port": 8083,
 | 
				
			||||||
 | 
					  "documentation": "https://github.com/gelbphoenix/autocaliweb/wiki",
 | 
				
			||||||
 | 
					  "config_path": "/etc/autocaliweb",
 | 
				
			||||||
 | 
					  "website": "https://github.com/gelbphoenix/autocaliweb",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/autocaliweb.webp",
 | 
				
			||||||
 | 
					  "description": "A modern web management system for eBooks, eComics and PDFs",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/autocaliweb.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 2,
 | 
				
			||||||
 | 
					        "ram": 2048,
 | 
				
			||||||
 | 
					        "hdd": 6,
 | 
				
			||||||
 | 
					        "os": "Debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": "admin",
 | 
				
			||||||
 | 
					    "password": "admin123"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-01-20",
 | 
					  "date_created": "2025-01-20",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8090,
 | 
					  "interface_port": 8090,
 | 
				
			||||||
  "documentation": "https://beszel.dev/guide/what-is-beszel",
 | 
					  "documentation": "https://beszel.dev/guide/what-is-beszel",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 4000,
 | 
					  "interface_port": 4000,
 | 
				
			||||||
  "documentation": "https://0xerr0r.github.io/blocky/latest/configuration/",
 | 
					  "documentation": "https://0xerr0r.github.io/blocky/latest/configuration/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-05-22",
 | 
					  "date_created": "2025-05-22",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://github.com/favonia/cloudflare-ddns/blob/main/README.markdown",
 | 
					  "documentation": "https://github.com/favonia/cloudflare-ddns/blob/main/README.markdown",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/",
 | 
					  "documentation": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 4000,
 | 
					  "interface_port": 4000,
 | 
				
			||||||
  "documentation": "https://dashy.to/docs",
 | 
					  "documentation": "https://dashy.to/docs",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": true,
 | 
					  "privileged": true,
 | 
				
			||||||
  "interface_port": 80,
 | 
					  "interface_port": 80,
 | 
				
			||||||
  "documentation": "https://github.com/dresden-elektronik/deconz-rest-plugin/wiki",
 | 
					  "documentation": "https://github.com/dresden-elektronik/deconz-rest-plugin/wiki",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8112,
 | 
					  "interface_port": 8112,
 | 
				
			||||||
  "documentation": "https://www.deluge-torrent.org/userguide/",
 | 
					  "documentation": "https://www.deluge-torrent.org/userguide/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
      "resources": {
 | 
					      "resources": {
 | 
				
			||||||
        "cpu": 2,
 | 
					        "cpu": 2,
 | 
				
			||||||
        "ram": 1024,
 | 
					        "ram": 1024,
 | 
				
			||||||
        "hdd": 4,
 | 
					        "hdd": 10,
 | 
				
			||||||
        "os": "debian",
 | 
					        "os": "debian",
 | 
				
			||||||
        "version": "12"
 | 
					        "version": "12"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8083,
 | 
					  "interface_port": 8083,
 | 
				
			||||||
  "documentation": "https://fhem.de/#Documentation",
 | 
					  "documentation": "https://fhem.de/#Documentation",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-06-18",
 | 
					  "date_created": "2025-06-18",
 | 
				
			||||||
  "type": "addon",
 | 
					  "type": "addon",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8080,
 | 
					  "interface_port": 8080,
 | 
				
			||||||
  "documentation": "https://github.com/gtsteffaniak/filebrowser/wiki/Getting-Started",
 | 
					  "documentation": "https://github.com/gtsteffaniak/filebrowser/wiki/Getting-Started",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "addon",
 | 
					  "type": "addon",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8080,
 | 
					  "interface_port": 8080,
 | 
				
			||||||
  "documentation": null,
 | 
					  "documentation": null,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8581,
 | 
					  "interface_port": 8581,
 | 
				
			||||||
  "documentation": "https://github.com/homebridge/homebridge/wiki",
 | 
					  "documentation": "https://github.com/homebridge/homebridge/wiki",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": true,
 | 
					  "privileged": true,
 | 
				
			||||||
  "interface_port": 8090,
 | 
					  "interface_port": 8090,
 | 
				
			||||||
  "documentation": "https://github.com/awawa-dev/HyperHDR/wiki",
 | 
					  "documentation": "https://github.com/awawa-dev/HyperHDR/wiki",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8086,
 | 
					  "interface_port": 8086,
 | 
				
			||||||
  "documentation": "https://docs.influxdata.com/",
 | 
					  "documentation": "https://docs.influxdata.com/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-07-29",
 | 
					  "date_created": "2025-07-29",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 80,
 | 
					  "interface_port": 80,
 | 
				
			||||||
  "documentation": "https://doc.jeedom.com",
 | 
					  "documentation": "https://doc.jeedom.com",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-12-26",
 | 
					  "date_created": "2024-12-26",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8080,
 | 
					  "interface_port": 8080,
 | 
				
			||||||
  "documentation": "https://www.jenkins.io/doc/",
 | 
					  "documentation": "https://www.jenkins.io/doc/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 5299,
 | 
					  "interface_port": 5299,
 | 
				
			||||||
  "documentation": "https://lazylibrarian.gitlab.io/lazylibrarian.gitlab.io/",
 | 
					  "documentation": "https://lazylibrarian.gitlab.io/lazylibrarian.gitlab.io/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-08-06",
 | 
					  "date_created": "2024-08-06",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 17170,
 | 
					  "interface_port": 17170,
 | 
				
			||||||
  "documentation": "https://github.com/lldap/lldap/blob/main/README.md",
 | 
					  "documentation": "https://github.com/lldap/lldap/blob/main/README.md",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 3306,
 | 
					  "interface_port": 3306,
 | 
				
			||||||
  "documentation": "https://github.com/community-scripts/ProxmoxVE/discussions/192",
 | 
					  "documentation": "https://github.com/community-scripts/ProxmoxVE/discussions/192",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-01-30",
 | 
					  "date_created": "2025-01-30",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8065,
 | 
					  "interface_port": 8065,
 | 
				
			||||||
  "documentation": "https://docs.mattermost.com/",
 | 
					  "documentation": "https://docs.mattermost.com/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-18",
 | 
					  "date_created": "2024-05-18",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 27017,
 | 
					  "interface_port": 27017,
 | 
				
			||||||
  "documentation": "https://www.mongodb.com/docs/manual/",
 | 
					  "documentation": "https://www.mongodb.com/docs/manual/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://mosquitto.org/documentation/",
 | 
					  "documentation": "https://mosquitto.org/documentation/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-03-13",
 | 
					  "date_created": "2025-03-13",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 81,
 | 
					  "interface_port": 81,
 | 
				
			||||||
  "documentation": "https://github.com/ZoeyVid/NPMplus/blob/develop/README.md",
 | 
					  "documentation": "https://github.com/ZoeyVid/NPMplus/blob/develop/README.md",
 | 
				
			||||||
@@ -39,7 +39,7 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "default_credentials": {
 | 
					  "default_credentials": {
 | 
				
			||||||
    "username": "root",
 | 
					    "username": "admin@example.org",
 | 
				
			||||||
    "password": null
 | 
					    "password": null
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "notes": [
 | 
					  "notes": [
 | 
				
			||||||
@@ -52,7 +52,7 @@
 | 
				
			|||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "text": "Application credentials: `cat /opt/.npm_pwd`",
 | 
					      "text": "Application credentials: `cat /opt/.npm_pwd` - if file not exist in LXC check docker logs for password with `docker logs npmplus`",
 | 
				
			||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 80,
 | 
					  "interface_port": 80,
 | 
				
			||||||
  "documentation": "https://docs.ntfy.sh/",
 | 
					  "documentation": "https://docs.ntfy.sh/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 80,
 | 
					  "interface_port": 80,
 | 
				
			||||||
  "documentation": "https://docs.openmediavault.org/en/stable/",
 | 
					  "documentation": "https://docs.openmediavault.org/en/stable/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8443,
 | 
					  "interface_port": 8443,
 | 
				
			||||||
  "documentation": "https://www.openhab.org/docs/",
 | 
					  "documentation": "https://www.openhab.org/docs/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,11 +33,7 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "notes": [
 | 
					  "notes": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "text": "This LXC is very memory-hungry when updating; it requires at least 6GB RAM, but RAM may be reduced to as low as 2GB when running normally",
 | 
					      "text": "To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`, and uncomment `CUSTOM_PATH` to add the path to your bind mount",
 | 
				
			||||||
      "type": "warning"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "text": "To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`",
 | 
					 | 
				
			||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://github.com/leiweibau/Pi.Alert/blob/main/README.md",
 | 
					  "documentation": "https://github.com/leiweibau/Pi.Alert/blob/main/README.md",
 | 
				
			||||||
@@ -31,10 +31,5 @@
 | 
				
			|||||||
    "username": null,
 | 
					    "username": null,
 | 
				
			||||||
    "password": null
 | 
					    "password": null
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "notes": [
 | 
					  "notes": []
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "text": "WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
 | 
					 | 
				
			||||||
      "type": "warning"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,10 +35,6 @@
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
      "text": "With Privileged/Unprivileged Hardware Acceleration Support",
 | 
					      "text": "With Privileged/Unprivileged Hardware Acceleration Support",
 | 
				
			||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "text": "WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
 | 
					 | 
				
			||||||
      "type": "warning"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +1,48 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "Podman Home Assistant Container",
 | 
					  "name": "Podman Home Assistant Container",
 | 
				
			||||||
    "slug": "podman-homeassistant",
 | 
					  "slug": "podman-homeassistant",
 | 
				
			||||||
    "categories": [
 | 
					  "categories": [
 | 
				
			||||||
        16
 | 
					    16
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
    "date_created": "2024-04-29",
 | 
					  "date_created": "2024-04-29",
 | 
				
			||||||
    "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
    "updateable": true,
 | 
					  "updateable": true,
 | 
				
			||||||
    "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
    "interface_port": 8123,
 | 
					  "interface_port": 8123,
 | 
				
			||||||
    "documentation": "https://www.home-assistant.io/docs/",
 | 
					  "documentation": "https://www.home-assistant.io/docs/",
 | 
				
			||||||
    "website": "https://www.home-assistant.io/",
 | 
					  "website": "https://www.home-assistant.io/",
 | 
				
			||||||
    "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/home-assistant.webp",
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/home-assistant.webp",
 | 
				
			||||||
    "config_path": "/var/lib/containers/storage/volumes/hass_config/_data",
 | 
					  "config_path": "/var/lib/containers/storage/volumes/hass_config/_data",
 | 
				
			||||||
    "description": "A standalone Podman container-based installation of Home Assistant Core means that the Home Assistant Core software is installed inside a container managed by Podman, separate from the host operating system. This provides a flexible and scalable solution for running the software, as the container can be easily moved between host systems or isolated from other processes for security. Podman is a popular open-source tool for managing containers that is similar to Docker, but designed for use on Linux systems without a daemon.\r\n\r\n\ud83d\udec8 If the LXC is created Privileged, the script will automatically set up USB passthrough.",
 | 
					  "description": "A standalone Podman container-based installation of Home Assistant Core means that the Home Assistant Core software is installed inside a container managed by Podman, separate from the host operating system. This provides a flexible and scalable solution for running the software, as the container can be easily moved between host systems or isolated from other processes for security. Podman is a popular open-source tool for managing containers that is similar to Docker, but designed for use on Linux systems without a daemon.\r\n\r\n\ud83d\udec8 If the LXC is created Privileged, the script will automatically set up USB passthrough.",
 | 
				
			||||||
    "install_methods": [
 | 
					  "install_methods": [
 | 
				
			||||||
        {
 | 
					    {
 | 
				
			||||||
            "type": "default",
 | 
					      "type": "default",
 | 
				
			||||||
            "script": "ct/podman-homeassistant.sh",
 | 
					      "script": "ct/podman-homeassistant.sh",
 | 
				
			||||||
            "resources": {
 | 
					      "resources": {
 | 
				
			||||||
                "cpu": 2,
 | 
					        "cpu": 2,
 | 
				
			||||||
                "ram": 2048,
 | 
					        "ram": 2048,
 | 
				
			||||||
                "hdd": 16,
 | 
					        "hdd": 16,
 | 
				
			||||||
                "os": "debian",
 | 
					        "os": "debian",
 | 
				
			||||||
                "version": "12"
 | 
					        "version": "12"
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
    "default_credentials": {
 | 
					  "default_credentials": {
 | 
				
			||||||
        "username": null,
 | 
					    "username": null,
 | 
				
			||||||
        "password": null
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "If the LXC is created Privileged, the script will automatically set up USB passthrough.",
 | 
				
			||||||
 | 
					      "type": "warning"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "notes": [
 | 
					    {
 | 
				
			||||||
        {
 | 
					      "text": "config path: `/var/lib/containers/storage/volumes/hass_config/_data`",
 | 
				
			||||||
            "text": "If the LXC is created Privileged, the script will automatically set up USB passthrough.",
 | 
					      "type": "info"
 | 
				
			||||||
            "type": "warning"
 | 
					    },
 | 
				
			||||||
        },
 | 
					    {
 | 
				
			||||||
        {
 | 
					      "text": "Options to Install Portainer or Portainer Agent",
 | 
				
			||||||
            "text": "config path: `/var/lib/containers/storage/volumes/hass_config/_data`",
 | 
					      "type": "warning"
 | 
				
			||||||
            "type": "info"
 | 
					    }
 | 
				
			||||||
        },
 | 
					  ]
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "text": "Options to Install Portainer or Portainer Agent",
 | 
					 | 
				
			||||||
            "type": "warning"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "text": "WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
 | 
					 | 
				
			||||||
            "type": "warning"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-02-02",
 | 
					  "date_created": "2025-02-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 5232,
 | 
					  "interface_port": 5232,
 | 
				
			||||||
  "documentation": "https://radicale.org/master.html#documentation-1",
 | 
					  "documentation": "https://radicale.org/master.html#documentation-1",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
  "install_methods": [
 | 
					  "install_methods": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "type": "default",
 | 
					      "type": "default",
 | 
				
			||||||
      "script": "ct/resilio-sync.sh",
 | 
					      "script": "ct/resiliosync.sh",
 | 
				
			||||||
      "resources": {
 | 
					      "resources": {
 | 
				
			||||||
        "cpu": 2,
 | 
					        "cpu": 2,
 | 
				
			||||||
        "ram": 2048,
 | 
					        "ram": 2048,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,11 @@
 | 
				
			|||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "text": "Login credentials: `cat ~/rustdesk.creds`",
 | 
					      "text": "To set admin password on Debian, type `cd /var/lib/rustdesk-api && rustdesk-api reset-admin-pwd <yournewpasswordhere>` inside LXC.",
 | 
				
			||||||
 | 
					      "type": "info"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "To see admin password on Alpine, type `cat ~/rustdesk.creds` inside LXC.",
 | 
				
			||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": null,
 | 
					  "interface_port": null,
 | 
				
			||||||
  "documentation": "https://oss.oetiker.ch/smokeping/doc/index.en.html",
 | 
					  "documentation": "https://oss.oetiker.ch/smokeping/doc/index.en.html",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								frontend/public/json/stylus.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								frontend/public/json/stylus.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "Stylus",
 | 
				
			||||||
 | 
					  "slug": "stylus",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    4
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-12",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "interface_port": 8000,
 | 
				
			||||||
 | 
					  "documentation": "https://mmastrac.github.io/stylus/",
 | 
				
			||||||
 | 
					  "website": "https://github.com/mmastrac/stylus",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp",
 | 
				
			||||||
 | 
					  "config_path": "/opt/stylus/config.yaml",
 | 
				
			||||||
 | 
					  "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/stylus.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 1,
 | 
				
			||||||
 | 
					        "ram": 1024,
 | 
				
			||||||
 | 
					        "hdd": 2,
 | 
				
			||||||
 | 
					        "os": "debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": null,
 | 
				
			||||||
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  "name": "Swizzin",
 | 
					  "name": "Swizzin",
 | 
				
			||||||
  "slug": "swizzin",
 | 
					  "slug": "swizzin",
 | 
				
			||||||
  "categories": [
 | 
					  "categories": [
 | 
				
			||||||
    15
 | 
					    13
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2025-08-19",
 | 
					  "date_created": "2025-08-19",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8384,
 | 
					  "interface_port": 8384,
 | 
				
			||||||
  "documentation": "https://docs.syncthing.net/",
 | 
					  "documentation": "https://docs.syncthing.net/",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8181,
 | 
					  "interface_port": 8181,
 | 
				
			||||||
  "documentation": "https://github.com/Tautulli/Tautulli/wiki",
 | 
					  "documentation": "https://github.com/Tautulli/Tautulli/wiki",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								frontend/public/json/telegraf.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								frontend/public/json/telegraf.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "Telegraf",
 | 
				
			||||||
 | 
					  "slug": "telegraf",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    9
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-11",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "interface_port": null,
 | 
				
			||||||
 | 
					  "documentation": "https://docs.influxdata.com/telegraf/v1/",
 | 
				
			||||||
 | 
					  "config_path": "/etc/telegraf/telegraf.conf",
 | 
				
			||||||
 | 
					  "website": "https://github.com/influxdata/telegraf",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/telegraf.webp",
 | 
				
			||||||
 | 
					  "description": "Telegraf collects and sends time series data from databases, systems, and IoT sensors. It has no external dependencies, is easy to install, and requires minimal memory.",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/telegraf.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 1,
 | 
				
			||||||
 | 
					        "ram": 1024,
 | 
				
			||||||
 | 
					        "hdd": 4,
 | 
				
			||||||
 | 
					        "os": "Debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": null,
 | 
				
			||||||
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "Make sure to configure an output for the telegraf config and start the service with `systemctl start telegraf`.",
 | 
				
			||||||
 | 
					      "type": "info"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										35
									
								
								frontend/public/json/uhf.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								frontend/public/json/uhf.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "UHF Server",
 | 
				
			||||||
 | 
					  "slug": "uhf",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    13
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-12",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "interface_port": 7568,
 | 
				
			||||||
 | 
					  "documentation": "https://www.uhfapp.com/server",
 | 
				
			||||||
 | 
					  "website": "https://www.uhfapp.com/",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/uhf.webp",
 | 
				
			||||||
 | 
					  "config_path": "/etc/uhf-server/",
 | 
				
			||||||
 | 
					  "description": "UHF Server is a powerful companion app that lets you seamlessly schedule and record your favorite shows from the UHF app.",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/uhf.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 2,
 | 
				
			||||||
 | 
					        "ram": 2048,
 | 
				
			||||||
 | 
					        "hdd": 8,
 | 
				
			||||||
 | 
					        "os": "Debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": null,
 | 
				
			||||||
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
  "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
  "updateable": false,
 | 
					  "updateable": true,
 | 
				
			||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8443,
 | 
					  "interface_port": 8443,
 | 
				
			||||||
  "documentation": null,
 | 
					  "documentation": null,
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -38,6 +38,7 @@ const DataFetcher: React.FC = () => {
 | 
				
			|||||||
  const [currentPage, setCurrentPage] = useState(1);
 | 
					  const [currentPage, setCurrentPage] = useState(1);
 | 
				
			||||||
  const [itemsPerPage, setItemsPerPage] = useState(25);
 | 
					  const [itemsPerPage, setItemsPerPage] = useState(25);
 | 
				
			||||||
  const [sortConfig, setSortConfig] = useState<{ key: string; direction: "ascending" | "descending" } | null>(null);
 | 
					  const [sortConfig, setSortConfig] = useState<{ key: string; direction: "ascending" | "descending" } | null>(null);
 | 
				
			||||||
 | 
					  const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const fetchSummary = async () => {
 | 
					    const fetchSummary = async () => {
 | 
				
			||||||
@@ -129,19 +130,24 @@ const DataFetcher: React.FC = () => {
 | 
				
			|||||||
      <p className="text-lg font-bold mt-4"> </p>
 | 
					      <p className="text-lg font-bold mt-4"> </p>
 | 
				
			||||||
      <div className="mb-4 flex justify-between items-center">
 | 
					      <div className="mb-4 flex justify-between items-center">
 | 
				
			||||||
        <p className="text-lg font-bold">
 | 
					        <p className="text-lg font-bold">
 | 
				
			||||||
          {summary?.total_entries}
 | 
					          {nf.format(
 | 
				
			||||||
 | 
					            summary?.total_entries ?? 0,
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          results found
 | 
					          results found
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <p className="text-lg font">
 | 
					        <p className="text-lg font">
 | 
				
			||||||
          Status Legend: 🔄 installing
 | 
					          Status Legend: 🔄 installing
 | 
				
			||||||
          {summary?.status_count.installing ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.installing ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ✔️ completed
 | 
					          | ✔️ completed
 | 
				
			||||||
          {summary?.status_count.done ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.done ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ❌ failed
 | 
					          | ❌ failed
 | 
				
			||||||
          {summary?.status_count.failed ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.failed ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ❓ unknown
 | 
					          | ❓ unknown
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ const inter = Inter({ subsets: ["latin"] });
 | 
				
			|||||||
export const metadata: Metadata = {
 | 
					export const metadata: Metadata = {
 | 
				
			||||||
  title: "Proxmox VE Helper-Scripts",
 | 
					  title: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
  description:
 | 
					  description:
 | 
				
			||||||
    "The official website for the Proxmox VE Helper-Scripts (Community) Repository. Featuring over 300+ scripts to help you manage your Proxmox VE environment.",
 | 
					    "The official website for the Proxmox VE Helper-Scripts (Community) repository. Featuring over 300+ scripts to help you manage your Proxmox Virtual Environment.",
 | 
				
			||||||
  applicationName: "Proxmox VE Helper-Scripts",
 | 
					  applicationName: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
  generator: "Next.js",
 | 
					  generator: "Next.js",
 | 
				
			||||||
  referrer: "origin-when-cross-origin",
 | 
					  referrer: "origin-when-cross-origin",
 | 
				
			||||||
@@ -57,7 +57,7 @@ export const metadata: Metadata = {
 | 
				
			|||||||
  openGraph: {
 | 
					  openGraph: {
 | 
				
			||||||
    title: "Proxmox VE Helper-Scripts",
 | 
					    title: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      "The official website for the Proxmox VE Helper-Scripts (Community) Repository. Featuring over 300+ scripts to help you manage your Proxmox VE environment.",
 | 
					      "The official website for the Proxmox VE Helper-Scripts (Community) repository. Featuring over 300+ scripts to help you manage your Proxmox Virtual Environment.",
 | 
				
			||||||
    url: `https://community-scripts.github.io/${basePath}/`,
 | 
					    url: `https://community-scripts.github.io/${basePath}/`,
 | 
				
			||||||
    siteName: "Proxmox VE Helper-Scripts",
 | 
					    siteName: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
    images: [
 | 
					    images: [
 | 
				
			||||||
@@ -76,7 +76,7 @@ export const metadata: Metadata = {
 | 
				
			|||||||
    title: "Proxmox VE Helper-Scripts",
 | 
					    title: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
    creator: "@BramSuurdje",
 | 
					    creator: "@BramSuurdje",
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      "The official website for the Proxmox VE Helper-Scripts (Community) Repository. Featuring over 300+ scripts to help you manage your Proxmox VE environment.",
 | 
					      "The official website for the Proxmox VE Helper-Scripts (Community) repository. Featuring over 300+ scripts to help you manage your Proxmox Virtual Environment.",
 | 
				
			||||||
    images: [`https://community-scripts.github.io/${basePath}/defaultimg.png`],
 | 
					    images: [`https://community-scripts.github.io/${basePath}/defaultimg.png`],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  manifest: "/manifest.webmanifest",
 | 
					  manifest: "/manifest.webmanifest",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ export default function manifest(): MetadataRoute.Manifest {
 | 
				
			|||||||
    name: "Proxmox VE Helper-Scripts",
 | 
					    name: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
    short_name: "Proxmox VE Helper-Scripts",
 | 
					    short_name: "Proxmox VE Helper-Scripts",
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      "A Re-designed Front-end for the Proxmox VE Helper-Scripts Repository. Featuring over 200+ scripts to help you manage your Proxmox VE environment.",
 | 
					      "A redesigned front-end for the Proxmox VE Helper-Scripts repository. Featuring over 300+ scripts to help you manage your Proxmox Virtual Environment.",
 | 
				
			||||||
    theme_color: "#030712",
 | 
					    theme_color: "#030712",
 | 
				
			||||||
    background_color: "#030712",
 | 
					    background_color: "#030712",
 | 
				
			||||||
    display: "standalone",
 | 
					    display: "standalone",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,8 +113,8 @@ export default function Page() {
 | 
				
			|||||||
                <p>
 | 
					                <p>
 | 
				
			||||||
                  With 300+ scripts to help you manage your
 | 
					                  With 300+ scripts to help you manage your
 | 
				
			||||||
                  {" "}
 | 
					                  {" "}
 | 
				
			||||||
                  <b>Proxmox VE environment</b>
 | 
					                  <b>Proxmox VE</b>
 | 
				
			||||||
                  . Whether you're a seasoned
 | 
					                  , whether you're a seasoned
 | 
				
			||||||
                  user or a newcomer, we've got you covered.
 | 
					                  user or a newcomer, we've got you covered.
 | 
				
			||||||
                </p>
 | 
					                </p>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,7 +143,7 @@ export function MostViewedScripts({ items }: { items: Category[] }) {
 | 
				
			|||||||
    <div className="">
 | 
					    <div className="">
 | 
				
			||||||
      {mostViewedScripts.length > 0 && (
 | 
					      {mostViewedScripts.length > 0 && (
 | 
				
			||||||
        <>
 | 
					        <>
 | 
				
			||||||
          <h2 className="text-lg font-semibold">Most Viewed Scripts</h2>
 | 
					          <h2 className="text-lg font-semibold mb-1">Most Viewed Scripts</h2>
 | 
				
			||||||
        </>
 | 
					        </>
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      <div className="min-w flex w-full flex-row flex-wrap gap-4">
 | 
					      <div className="min-w flex w-full flex-row flex-wrap gap-4">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { VariantProps } from "class-variance-authority";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cva } from "class-variance-authority";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { ButtonProps as ButtonPrimitiveProps } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Button as ButtonPrimitive,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonVariants = cva(
 | 
				
			||||||
 | 
					  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
 | 
				
			||||||
 | 
					        accent: "bg-accent text-accent-foreground shadow-xs hover:bg-accent/90",
 | 
				
			||||||
 | 
					        destructive:
 | 
				
			||||||
 | 
					          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
 | 
				
			||||||
 | 
					        outline:
 | 
				
			||||||
 | 
					          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
 | 
				
			||||||
 | 
					        secondary:
 | 
				
			||||||
 | 
					          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
 | 
				
			||||||
 | 
					        ghost:
 | 
				
			||||||
 | 
					          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
 | 
				
			||||||
 | 
					        link: "text-primary underline-offset-4 hover:underline",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      size: {
 | 
				
			||||||
 | 
					        "default": "h-9 px-4 py-2 has-[>svg]:px-3",
 | 
				
			||||||
 | 
					        "sm": "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
 | 
				
			||||||
 | 
					        "lg": "h-10 rounded-md px-6 has-[>svg]:px-4",
 | 
				
			||||||
 | 
					        "icon": "size-9",
 | 
				
			||||||
 | 
					        "icon-sm": "size-8 rounded-md",
 | 
				
			||||||
 | 
					        "icon-lg": "size-10 rounded-md",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					      size: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ButtonProps = ButtonPrimitiveProps & VariantProps<typeof buttonVariants>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Button({ className, variant, size, ...props }: ButtonProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ButtonPrimitive
 | 
				
			||||||
 | 
					      className={cn(buttonVariants({ variant, size, className }))}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Button, type ButtonProps, buttonVariants };
 | 
				
			||||||
@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					import type { VariantProps } from "class-variance-authority";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cva } from "class-variance-authority";
 | 
				
			||||||
 | 
					import { StarIcon } from "lucide-react";
 | 
				
			||||||
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { ButtonProps as ButtonPrimitiveProps } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import type { GithubStarsProps } from "@/components/animate-ui/primitives/animate/github-stars";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  GithubStars,
 | 
				
			||||||
 | 
					  GithubStarsIcon,
 | 
				
			||||||
 | 
					  GithubStarsLogo,
 | 
				
			||||||
 | 
					  GithubStarsNumber,
 | 
				
			||||||
 | 
					  GithubStarsParticles,
 | 
				
			||||||
 | 
					} from "@/components/animate-ui/primitives/animate/github-stars";
 | 
				
			||||||
 | 
					import { Button as ButtonPrimitive } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonVariants = cva(
 | 
				
			||||||
 | 
					  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
 | 
				
			||||||
 | 
					        accent: "bg-accent text-accent-foreground shadow-xs hover:bg-accent/90",
 | 
				
			||||||
 | 
					        outline:
 | 
				
			||||||
 | 
					          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
 | 
				
			||||||
 | 
					        ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      size: {
 | 
				
			||||||
 | 
					        default: "h-9 px-4 py-2 has-[>svg]:px-3",
 | 
				
			||||||
 | 
					        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
 | 
				
			||||||
 | 
					        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					      size: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonStarVariants = cva("", {
 | 
				
			||||||
 | 
					  variants: {
 | 
				
			||||||
 | 
					    variant: {
 | 
				
			||||||
 | 
					      default: "fill-neutral-700 stroke-neutral-700 dark:fill-neutral-300 dark:stroke-neutral-300",
 | 
				
			||||||
 | 
					      accent: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					      outline: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					      ghost: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  defaultVariants: {
 | 
				
			||||||
 | 
					    variant: "default",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GitHubStarsButtonProps = Omit<ButtonPrimitiveProps & GithubStarsProps, "asChild" | "children">
 | 
				
			||||||
 | 
					  & VariantProps<typeof buttonVariants>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GitHubStarsButton({
 | 
				
			||||||
 | 
					  className,
 | 
				
			||||||
 | 
					  username,
 | 
				
			||||||
 | 
					  repo,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  delay,
 | 
				
			||||||
 | 
					  inView,
 | 
				
			||||||
 | 
					  inViewMargin,
 | 
				
			||||||
 | 
					  inViewOnce,
 | 
				
			||||||
 | 
					  variant,
 | 
				
			||||||
 | 
					  size,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GitHubStarsButtonProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Link
 | 
				
			||||||
 | 
					      target="_blank"
 | 
				
			||||||
 | 
					      rel="noopener noreferrer"
 | 
				
			||||||
 | 
					      data-umami-event="github-stars"
 | 
				
			||||||
 | 
					      href={`https://github.com/${username}/${repo}`}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <GithubStars
 | 
				
			||||||
 | 
					        asChild
 | 
				
			||||||
 | 
					        username={username}
 | 
				
			||||||
 | 
					        repo={repo}
 | 
				
			||||||
 | 
					        value={value}
 | 
				
			||||||
 | 
					        delay={delay}
 | 
				
			||||||
 | 
					        inView={inView}
 | 
				
			||||||
 | 
					        inViewMargin={inViewMargin}
 | 
				
			||||||
 | 
					        inViewOnce={inViewOnce}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <ButtonPrimitive className={cn(buttonVariants({ variant, size, className }))} {...props}>
 | 
				
			||||||
 | 
					          <GithubStarsLogo />
 | 
				
			||||||
 | 
					          <GithubStarsNumber />
 | 
				
			||||||
 | 
					          <GithubStarsParticles className="text-yellow-500">
 | 
				
			||||||
 | 
					            <GithubStarsIcon
 | 
				
			||||||
 | 
					              icon={StarIcon}
 | 
				
			||||||
 | 
					              data-variant={variant}
 | 
				
			||||||
 | 
					              className={cn(buttonStarVariants({ variant }))}
 | 
				
			||||||
 | 
					              activeClassName="text-yellow-500"
 | 
				
			||||||
 | 
					              size={18}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </GithubStarsParticles>
 | 
				
			||||||
 | 
					        </ButtonPrimitive>
 | 
				
			||||||
 | 
					      </GithubStars>
 | 
				
			||||||
 | 
					    </Link>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { GitHubStarsButton, type GitHubStarsButtonProps };
 | 
				
			||||||
@@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { SlidingNumberProps } from "@/components/animate-ui/primitives/texts/sliding-number";
 | 
				
			||||||
 | 
					import type { ParticlesEffectProps } from "@/components/animate-ui/primitives/effects/particles";
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Particles, ParticlesEffect } from "@/components/animate-ui/primitives/effects/particles";
 | 
				
			||||||
 | 
					import { SlidingNumber } from "@/components/animate-ui/primitives/texts/sliding-number";
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import { getStrictContext } from "@/lib/get-strict-context";
 | 
				
			||||||
 | 
					import { useIsInView } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsContextType = {
 | 
				
			||||||
 | 
					  stars: number;
 | 
				
			||||||
 | 
					  setStars: (stars: number) => void;
 | 
				
			||||||
 | 
					  currentStars: number;
 | 
				
			||||||
 | 
					  setCurrentStars: (stars: number) => void;
 | 
				
			||||||
 | 
					  isCompleted: boolean;
 | 
				
			||||||
 | 
					  isLoading: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [GithubStarsProvider, useGithubStars] = getStrictContext<GithubStarsContextType>("GithubStarsContext");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsProps = WithAsChild<
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    children: React.ReactNode;
 | 
				
			||||||
 | 
					    username?: string;
 | 
				
			||||||
 | 
					    repo?: string;
 | 
				
			||||||
 | 
					    value?: number;
 | 
				
			||||||
 | 
					    delay?: number;
 | 
				
			||||||
 | 
					  } & UseIsInViewOptions
 | 
				
			||||||
 | 
					  & HTMLMotionProps<"div">
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStars({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  username,
 | 
				
			||||||
 | 
					  repo,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GithubStarsProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(ref as React.Ref<HTMLDivElement>, {
 | 
				
			||||||
 | 
					    inView,
 | 
				
			||||||
 | 
					    inViewOnce,
 | 
				
			||||||
 | 
					    inViewMargin,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [stars, setStars] = React.useState(value ?? 0);
 | 
				
			||||||
 | 
					  const [currentStars, setCurrentStars] = React.useState(0);
 | 
				
			||||||
 | 
					  const [isLoading, setIsLoading] = React.useState(true);
 | 
				
			||||||
 | 
					  const isCompleted = React.useMemo(() => currentStars === stars, [currentStars, stars]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (value !== undefined && username && repo)
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    if (!isInView) {
 | 
				
			||||||
 | 
					      setStars(0);
 | 
				
			||||||
 | 
					      setIsLoading(true);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const timeout = setTimeout(() => {
 | 
				
			||||||
 | 
					      fetch(`https://api.github.com/repos/${username}/${repo}`)
 | 
				
			||||||
 | 
					        .then(response => response.json())
 | 
				
			||||||
 | 
					        .then((data) => {
 | 
				
			||||||
 | 
					          if (data && typeof data.stargazers_count === "number") {
 | 
				
			||||||
 | 
					            setStars(data.stargazers_count);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(console.error)
 | 
				
			||||||
 | 
					        .finally(() => setIsLoading(false));
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeout);
 | 
				
			||||||
 | 
					  }, [username, repo, value, isInView, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <GithubStarsProvider
 | 
				
			||||||
 | 
					      value={{
 | 
				
			||||||
 | 
					        stars,
 | 
				
			||||||
 | 
					        currentStars,
 | 
				
			||||||
 | 
					        isCompleted,
 | 
				
			||||||
 | 
					        isLoading,
 | 
				
			||||||
 | 
					        setStars,
 | 
				
			||||||
 | 
					        setCurrentStars,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {!isLoading && (
 | 
				
			||||||
 | 
					        <Component ref={localRef} {...props}>
 | 
				
			||||||
 | 
					          {children}
 | 
				
			||||||
 | 
					        </Component>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </GithubStarsProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsNumberProps = Omit<SlidingNumberProps, "number" | "fromNumber">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsNumber({ padStart = true, ...props }: GithubStarsNumberProps) {
 | 
				
			||||||
 | 
					  const { stars, setCurrentStars } = useGithubStars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SlidingNumber number={stars} fromNumber={0} onNumberChange={setCurrentStars} padStart={padStart} {...props} />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsIconProps<T extends React.ElementType> = {
 | 
				
			||||||
 | 
					  icon: React.ReactElement<T>;
 | 
				
			||||||
 | 
					  color?: string;
 | 
				
			||||||
 | 
					  activeClassName?: string;
 | 
				
			||||||
 | 
					} & React.ComponentProps<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsIcon<T extends React.ElementType>({
 | 
				
			||||||
 | 
					  icon: Icon,
 | 
				
			||||||
 | 
					  color = "currentColor",
 | 
				
			||||||
 | 
					  activeClassName,
 | 
				
			||||||
 | 
					  className,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GithubStarsIconProps<T>) {
 | 
				
			||||||
 | 
					  const { stars, currentStars, isCompleted } = useGithubStars();
 | 
				
			||||||
 | 
					  const fillPercentage = (currentStars / stars) * 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div style={{ position: "relative" }}>
 | 
				
			||||||
 | 
					      <Icon aria-hidden="true" className={cn(className)} {...props} />
 | 
				
			||||||
 | 
					      <Icon
 | 
				
			||||||
 | 
					        aria-hidden="true"
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          position: "absolute",
 | 
				
			||||||
 | 
					          top: 0,
 | 
				
			||||||
 | 
					          left: 0,
 | 
				
			||||||
 | 
					          fill: color,
 | 
				
			||||||
 | 
					          stroke: color,
 | 
				
			||||||
 | 
					          clipPath: `inset(${100 - (isCompleted ? fillPercentage : fillPercentage - 10)}% 0 0 0)`,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        className={cn(className, activeClassName)}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsParticlesProps = ParticlesEffectProps & {
 | 
				
			||||||
 | 
					  children: React.ReactElement;
 | 
				
			||||||
 | 
					  size?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsParticles({ children, size = 4, style, ...props }: GithubStarsParticlesProps) {
 | 
				
			||||||
 | 
					  const { isCompleted } = useGithubStars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Particles animate={isCompleted}>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					      <ParticlesEffect
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          backgroundColor: "currentcolor",
 | 
				
			||||||
 | 
					          borderRadius: "50%",
 | 
				
			||||||
 | 
					          width: size,
 | 
				
			||||||
 | 
					          height: size,
 | 
				
			||||||
 | 
					          ...style,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </Particles>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsLogoProps = React.SVGProps<SVGSVGElement>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsLogo(props: GithubStarsLogoProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <svg role="img" viewBox="0 0 24 24" fill="currentColor" aria-label="GitHub" {...props}>
 | 
				
			||||||
 | 
					      <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path>
 | 
				
			||||||
 | 
					    </svg>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  GithubStars,
 | 
				
			||||||
 | 
					  type GithubStarsContextType,
 | 
				
			||||||
 | 
					  GithubStarsIcon,
 | 
				
			||||||
 | 
					  type GithubStarsIconProps,
 | 
				
			||||||
 | 
					  GithubStarsLogo,
 | 
				
			||||||
 | 
					  type GithubStarsLogoProps,
 | 
				
			||||||
 | 
					  GithubStarsNumber,
 | 
				
			||||||
 | 
					  type GithubStarsNumberProps,
 | 
				
			||||||
 | 
					  GithubStarsParticles,
 | 
				
			||||||
 | 
					  type GithubStarsParticlesProps,
 | 
				
			||||||
 | 
					  type GithubStarsProps,
 | 
				
			||||||
 | 
					  useGithubStars,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										101
									
								
								frontend/src/components/animate-ui/primitives/animate/slot.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								frontend/src/components/animate-ui/primitives/animate/slot.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { isMotionComponent, motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AnyProps = Record<string, unknown>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DOMMotionProps<T extends HTMLElement = HTMLElement> = Omit<
 | 
				
			||||||
 | 
					  HTMLMotionProps<keyof HTMLElementTagNameMap>,
 | 
				
			||||||
 | 
					  "ref"
 | 
				
			||||||
 | 
					> & { ref?: React.Ref<T> };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WithAsChild<Base extends object>
 | 
				
			||||||
 | 
					  = | (Base & { asChild: true; children: React.ReactElement })
 | 
				
			||||||
 | 
					    | (Base & { asChild?: false | undefined });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlotProps<T extends HTMLElement = HTMLElement> = {
 | 
				
			||||||
 | 
					  children?: any;
 | 
				
			||||||
 | 
					} & DOMMotionProps<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeRefs<T>(
 | 
				
			||||||
 | 
					  ...refs: (React.Ref<T> | undefined)[]
 | 
				
			||||||
 | 
					): React.RefCallback<T> {
 | 
				
			||||||
 | 
					  return (node) => {
 | 
				
			||||||
 | 
					    refs.forEach((ref) => {
 | 
				
			||||||
 | 
					      if (!ref)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      if (typeof ref === "function") {
 | 
				
			||||||
 | 
					        ref(node);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        (ref as React.RefObject<T | null>).current = node;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeProps<T extends HTMLElement>(
 | 
				
			||||||
 | 
					  childProps: AnyProps,
 | 
				
			||||||
 | 
					  slotProps: DOMMotionProps<T>,
 | 
				
			||||||
 | 
					): AnyProps {
 | 
				
			||||||
 | 
					  const merged: AnyProps = { ...childProps, ...slotProps };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (childProps.className || slotProps.className) {
 | 
				
			||||||
 | 
					    merged.className = cn(
 | 
				
			||||||
 | 
					      childProps.className as string,
 | 
				
			||||||
 | 
					      slotProps.className as string,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (childProps.style || slotProps.style) {
 | 
				
			||||||
 | 
					    merged.style = {
 | 
				
			||||||
 | 
					      ...(childProps.style as React.CSSProperties),
 | 
				
			||||||
 | 
					      ...(slotProps.style as React.CSSProperties),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return merged;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Slot<T extends HTMLElement = HTMLElement>({
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: SlotProps<T>) {
 | 
				
			||||||
 | 
					  const isAlreadyMotion
 | 
				
			||||||
 | 
					    = typeof children.type === "object"
 | 
				
			||||||
 | 
					      && children.type !== null
 | 
				
			||||||
 | 
					      && isMotionComponent(children.type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Base = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      isAlreadyMotion
 | 
				
			||||||
 | 
					        ? (children.type as React.ElementType)
 | 
				
			||||||
 | 
					        : motion.create(children.type as React.ElementType),
 | 
				
			||||||
 | 
					    [isAlreadyMotion, children.type],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!React.isValidElement(children))
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { ref: childRef, ...childProps } = children.props as AnyProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const mergedProps = mergeProps(childProps, props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Base {...mergedProps} ref={mergeRefs(childRef as React.Ref<T>, ref)} />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  type AnyProps,
 | 
				
			||||||
 | 
					  type DOMMotionProps,
 | 
				
			||||||
 | 
					  Slot,
 | 
				
			||||||
 | 
					  type SlotProps,
 | 
				
			||||||
 | 
					  type WithAsChild,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ButtonProps = WithAsChild<
 | 
				
			||||||
 | 
					  HTMLMotionProps<"button"> & {
 | 
				
			||||||
 | 
					    hoverScale?: number;
 | 
				
			||||||
 | 
					    tapScale?: number;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Button({
 | 
				
			||||||
 | 
					  hoverScale = 1.05,
 | 
				
			||||||
 | 
					  tapScale = 0.95,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ButtonProps) {
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.button;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Component
 | 
				
			||||||
 | 
					      whileTap={{ scale: tapScale }}
 | 
				
			||||||
 | 
					      whileHover={{ scale: hoverScale }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Button, type ButtonProps };
 | 
				
			||||||
@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { AnimatePresence, motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import { getStrictContext } from "@/lib/get-strict-context";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  useIsInView,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Side = "top" | "bottom" | "left" | "right";
 | 
				
			||||||
 | 
					type Align = "start" | "center" | "end";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesContextType = {
 | 
				
			||||||
 | 
					  animate: boolean;
 | 
				
			||||||
 | 
					  isInView: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [ParticlesProvider, useParticles]
 | 
				
			||||||
 | 
					  = getStrictContext<ParticlesContextType>("ParticlesContext");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesProps = WithAsChild<
 | 
				
			||||||
 | 
					  Omit<HTMLMotionProps<"div">, "children"> & {
 | 
				
			||||||
 | 
					    animate?: boolean;
 | 
				
			||||||
 | 
					    children: React.ReactNode;
 | 
				
			||||||
 | 
					  } & UseIsInViewOptions
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Particles({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  animate = true,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  style,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ParticlesProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(
 | 
				
			||||||
 | 
					    ref as React.Ref<HTMLDivElement>,
 | 
				
			||||||
 | 
					    { inView, inViewOnce, inViewMargin },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ParticlesProvider value={{ animate, isInView }}>
 | 
				
			||||||
 | 
					      <Component
 | 
				
			||||||
 | 
					        ref={localRef}
 | 
				
			||||||
 | 
					        style={{ position: "relative", ...style }}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {children}
 | 
				
			||||||
 | 
					      </Component>
 | 
				
			||||||
 | 
					    </ParticlesProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesEffectProps = Omit<HTMLMotionProps<"div">, "children"> & {
 | 
				
			||||||
 | 
					  side?: Side;
 | 
				
			||||||
 | 
					  align?: Align;
 | 
				
			||||||
 | 
					  count?: number;
 | 
				
			||||||
 | 
					  radius?: number;
 | 
				
			||||||
 | 
					  spread?: number;
 | 
				
			||||||
 | 
					  duration?: number;
 | 
				
			||||||
 | 
					  holdDelay?: number;
 | 
				
			||||||
 | 
					  sideOffset?: number;
 | 
				
			||||||
 | 
					  alignOffset?: number;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ParticlesEffect({
 | 
				
			||||||
 | 
					  side = "top",
 | 
				
			||||||
 | 
					  align = "center",
 | 
				
			||||||
 | 
					  count = 6,
 | 
				
			||||||
 | 
					  radius = 30,
 | 
				
			||||||
 | 
					  spread = 360,
 | 
				
			||||||
 | 
					  duration = 0.8,
 | 
				
			||||||
 | 
					  holdDelay = 0.05,
 | 
				
			||||||
 | 
					  sideOffset = 0,
 | 
				
			||||||
 | 
					  alignOffset = 0,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					  style,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ParticlesEffectProps) {
 | 
				
			||||||
 | 
					  const { animate, isInView } = useParticles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const isVertical = side === "top" || side === "bottom";
 | 
				
			||||||
 | 
					  const alignPct = align === "start" ? "0%" : align === "end" ? "100%" : "50%";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const top = isVertical
 | 
				
			||||||
 | 
					    ? side === "top"
 | 
				
			||||||
 | 
					      ? `calc(0% - ${sideOffset}px)`
 | 
				
			||||||
 | 
					      : `calc(100% + ${sideOffset}px)`
 | 
				
			||||||
 | 
					    : `calc(${alignPct} + ${alignOffset}px)`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const left = isVertical
 | 
				
			||||||
 | 
					    ? `calc(${alignPct} + ${alignOffset}px)`
 | 
				
			||||||
 | 
					    : side === "left"
 | 
				
			||||||
 | 
					      ? `calc(0% - ${sideOffset}px)`
 | 
				
			||||||
 | 
					      : `calc(100% + ${sideOffset}px)`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const containerStyle: React.CSSProperties = {
 | 
				
			||||||
 | 
					    position: "absolute",
 | 
				
			||||||
 | 
					    top,
 | 
				
			||||||
 | 
					    left,
 | 
				
			||||||
 | 
					    transform: "translate(-50%, -50%)",
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const angleStep = (spread * (Math.PI / 180)) / Math.max(1, count - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AnimatePresence>
 | 
				
			||||||
 | 
					      {animate
 | 
				
			||||||
 | 
					        && isInView
 | 
				
			||||||
 | 
					        && Array.from({ length: count }).map((_, i) => {
 | 
				
			||||||
 | 
					          const angle = i * angleStep;
 | 
				
			||||||
 | 
					          const x = Math.cos(angle) * radius;
 | 
				
			||||||
 | 
					          const y = Math.sin(angle) * radius;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <motion.div
 | 
				
			||||||
 | 
					              key={i}
 | 
				
			||||||
 | 
					              style={{ ...containerStyle, ...style }}
 | 
				
			||||||
 | 
					              initial={{ scale: 0, opacity: 0 }}
 | 
				
			||||||
 | 
					              animate={{
 | 
				
			||||||
 | 
					                x: `${x}px`,
 | 
				
			||||||
 | 
					                y: `${y}px`,
 | 
				
			||||||
 | 
					                scale: [0, 1, 0],
 | 
				
			||||||
 | 
					                opacity: [0, 1, 0],
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              transition={{
 | 
				
			||||||
 | 
					                duration,
 | 
				
			||||||
 | 
					                delay: delay + i * holdDelay,
 | 
				
			||||||
 | 
					                ease: "easeOut",
 | 
				
			||||||
 | 
					                ...transition,
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              {...props}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
 | 
					    </AnimatePresence>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  Particles,
 | 
				
			||||||
 | 
					  ParticlesEffect,
 | 
				
			||||||
 | 
					  type ParticlesEffectProps,
 | 
				
			||||||
 | 
					  type ParticlesProps,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,338 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { MotionValue, SpringOptions } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  motion,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useMotionValue,
 | 
				
			||||||
 | 
					  useSpring,
 | 
				
			||||||
 | 
					  useTransform,
 | 
				
			||||||
 | 
					} from "motion/react";
 | 
				
			||||||
 | 
					import useMeasure from "react-use-measure";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  useIsInView,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberRollerProps = {
 | 
				
			||||||
 | 
					  prevValue: number;
 | 
				
			||||||
 | 
					  value: number;
 | 
				
			||||||
 | 
					  place: number;
 | 
				
			||||||
 | 
					  transition: SpringOptions;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumberRoller({
 | 
				
			||||||
 | 
					  prevValue,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  place,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					}: SlidingNumberRollerProps) {
 | 
				
			||||||
 | 
					  const startNumber = Math.floor(prevValue / place) % 10;
 | 
				
			||||||
 | 
					  const targetNumber = Math.floor(value / place) % 10;
 | 
				
			||||||
 | 
					  const animatedValue = useSpring(startNumber, transition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    const timeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					      animatedValue.set(targetNumber);
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeoutId);
 | 
				
			||||||
 | 
					  }, [targetNumber, animatedValue, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [measureRef, { height }] = useMeasure();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      ref={measureRef}
 | 
				
			||||||
 | 
					      data-slot="sliding-number-roller"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        position: "relative",
 | 
				
			||||||
 | 
					        display: "inline-block",
 | 
				
			||||||
 | 
					        width: "1ch",
 | 
				
			||||||
 | 
					        overflowX: "visible",
 | 
				
			||||||
 | 
					        overflowY: "clip",
 | 
				
			||||||
 | 
					        lineHeight: 1,
 | 
				
			||||||
 | 
					        fontVariantNumeric: "tabular-nums",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <span style={{ visibility: "hidden" }}>0</span>
 | 
				
			||||||
 | 
					      {Array.from({ length: 10 }, (_, i) => (
 | 
				
			||||||
 | 
					        <SlidingNumberDisplay
 | 
				
			||||||
 | 
					          key={i}
 | 
				
			||||||
 | 
					          motionValue={animatedValue}
 | 
				
			||||||
 | 
					          number={i}
 | 
				
			||||||
 | 
					          height={height}
 | 
				
			||||||
 | 
					          transition={transition}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ))}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberDisplayProps = {
 | 
				
			||||||
 | 
					  motionValue: MotionValue<number>;
 | 
				
			||||||
 | 
					  number: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					  transition: SpringOptions;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumberDisplay({
 | 
				
			||||||
 | 
					  motionValue,
 | 
				
			||||||
 | 
					  number,
 | 
				
			||||||
 | 
					  height,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					}: SlidingNumberDisplayProps) {
 | 
				
			||||||
 | 
					  const y = useTransform(motionValue, (latest) => {
 | 
				
			||||||
 | 
					    if (!height)
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					    const currentNumber = latest % 10;
 | 
				
			||||||
 | 
					    const offset = (10 + number - currentNumber) % 10;
 | 
				
			||||||
 | 
					    let translateY = offset * height;
 | 
				
			||||||
 | 
					    if (offset > 5)
 | 
				
			||||||
 | 
					      translateY -= 10 * height;
 | 
				
			||||||
 | 
					    return translateY;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!height) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <span style={{ visibility: "hidden", position: "absolute" }}>
 | 
				
			||||||
 | 
					        {number}
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <motion.span
 | 
				
			||||||
 | 
					      data-slot="sliding-number-display"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        y,
 | 
				
			||||||
 | 
					        position: "absolute",
 | 
				
			||||||
 | 
					        inset: 0,
 | 
				
			||||||
 | 
					        display: "flex",
 | 
				
			||||||
 | 
					        alignItems: "center",
 | 
				
			||||||
 | 
					        justifyContent: "center",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      transition={{ ...transition, type: "spring" }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {number}
 | 
				
			||||||
 | 
					    </motion.span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberProps = Omit<React.ComponentProps<"span">, "children"> & {
 | 
				
			||||||
 | 
					  number: number;
 | 
				
			||||||
 | 
					  fromNumber?: number;
 | 
				
			||||||
 | 
					  onNumberChange?: (number: number) => void;
 | 
				
			||||||
 | 
					  padStart?: boolean;
 | 
				
			||||||
 | 
					  decimalSeparator?: string;
 | 
				
			||||||
 | 
					  decimalPlaces?: number;
 | 
				
			||||||
 | 
					  thousandSeparator?: string;
 | 
				
			||||||
 | 
					  transition?: SpringOptions;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					} & UseIsInViewOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumber({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  number,
 | 
				
			||||||
 | 
					  fromNumber,
 | 
				
			||||||
 | 
					  onNumberChange,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  padStart = false,
 | 
				
			||||||
 | 
					  decimalSeparator = ".",
 | 
				
			||||||
 | 
					  decimalPlaces = 0,
 | 
				
			||||||
 | 
					  thousandSeparator,
 | 
				
			||||||
 | 
					  transition = { stiffness: 200, damping: 20, mass: 0.4 },
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: SlidingNumberProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(
 | 
				
			||||||
 | 
					    ref as React.Ref<HTMLElement>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      inView,
 | 
				
			||||||
 | 
					      inViewOnce,
 | 
				
			||||||
 | 
					      inViewMargin,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const prevNumberRef = React.useRef<number>(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const hasAnimated = fromNumber !== undefined;
 | 
				
			||||||
 | 
					  const motionVal = useMotionValue(fromNumber ?? 0);
 | 
				
			||||||
 | 
					  const springVal = useSpring(motionVal, { stiffness: 90, damping: 50 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (!hasAnimated)
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    const timeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					      if (isInView)
 | 
				
			||||||
 | 
					        motionVal.set(number);
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeoutId);
 | 
				
			||||||
 | 
					  }, [hasAnimated, isInView, number, motionVal, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [effectiveNumber, setEffectiveNumber] = React.useState(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (hasAnimated) {
 | 
				
			||||||
 | 
					      const inferredDecimals
 | 
				
			||||||
 | 
					        = typeof decimalPlaces === "number" && decimalPlaces >= 0
 | 
				
			||||||
 | 
					          ? decimalPlaces
 | 
				
			||||||
 | 
					          : (() => {
 | 
				
			||||||
 | 
					              const s = String(number);
 | 
				
			||||||
 | 
					              const idx = s.indexOf(".");
 | 
				
			||||||
 | 
					              return idx >= 0 ? s.length - idx - 1 : 0;
 | 
				
			||||||
 | 
					            })();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const factor = 10 ** inferredDecimals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const unsubscribe = springVal.on("change", (latest: number) => {
 | 
				
			||||||
 | 
					        const newValue
 | 
				
			||||||
 | 
					          = inferredDecimals > 0
 | 
				
			||||||
 | 
					            ? Math.round(latest * factor) / factor
 | 
				
			||||||
 | 
					            : Math.round(latest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (effectiveNumber !== newValue) {
 | 
				
			||||||
 | 
					          setEffectiveNumber(newValue);
 | 
				
			||||||
 | 
					          onNumberChange?.(newValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return () => unsubscribe();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      setEffectiveNumber(!isInView ? 0 : Math.abs(Number(number)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [
 | 
				
			||||||
 | 
					    hasAnimated,
 | 
				
			||||||
 | 
					    springVal,
 | 
				
			||||||
 | 
					    isInView,
 | 
				
			||||||
 | 
					    number,
 | 
				
			||||||
 | 
					    decimalPlaces,
 | 
				
			||||||
 | 
					    onNumberChange,
 | 
				
			||||||
 | 
					    effectiveNumber,
 | 
				
			||||||
 | 
					  ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const formatNumber = React.useCallback(
 | 
				
			||||||
 | 
					    (num: number) =>
 | 
				
			||||||
 | 
					      decimalPlaces != null ? num.toFixed(decimalPlaces) : num.toString(),
 | 
				
			||||||
 | 
					    [decimalPlaces],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const numberStr = formatNumber(effectiveNumber);
 | 
				
			||||||
 | 
					  const [newIntStrRaw, newDecStrRaw = ""] = numberStr.split(".");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const finalIntLength = padStart
 | 
				
			||||||
 | 
					    ? Math.max(
 | 
				
			||||||
 | 
					        Math.floor(Math.abs(number)).toString().length,
 | 
				
			||||||
 | 
					        newIntStrRaw.length,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    : newIntStrRaw.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const newIntStr = padStart
 | 
				
			||||||
 | 
					    ? newIntStrRaw.padStart(finalIntLength, "0")
 | 
				
			||||||
 | 
					    : newIntStrRaw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const prevFormatted = formatNumber(prevNumberRef.current);
 | 
				
			||||||
 | 
					  const [prevIntStrRaw = "", prevDecStrRaw = ""] = prevFormatted.split(".");
 | 
				
			||||||
 | 
					  const prevIntStr = padStart
 | 
				
			||||||
 | 
					    ? prevIntStrRaw.padStart(finalIntLength, "0")
 | 
				
			||||||
 | 
					    : prevIntStrRaw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const adjustedPrevInt = React.useMemo(() => {
 | 
				
			||||||
 | 
					    return prevIntStr.length > finalIntLength
 | 
				
			||||||
 | 
					      ? prevIntStr.slice(-finalIntLength)
 | 
				
			||||||
 | 
					      : prevIntStr.padStart(finalIntLength, "0");
 | 
				
			||||||
 | 
					  }, [prevIntStr, finalIntLength]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const adjustedPrevDec = React.useMemo(() => {
 | 
				
			||||||
 | 
					    if (!newDecStrRaw)
 | 
				
			||||||
 | 
					      return "";
 | 
				
			||||||
 | 
					    return prevDecStrRaw.length > newDecStrRaw.length
 | 
				
			||||||
 | 
					      ? prevDecStrRaw.slice(0, newDecStrRaw.length)
 | 
				
			||||||
 | 
					      : prevDecStrRaw.padEnd(newDecStrRaw.length, "0");
 | 
				
			||||||
 | 
					  }, [prevDecStrRaw, newDecStrRaw]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (isInView)
 | 
				
			||||||
 | 
					      prevNumberRef.current = effectiveNumber;
 | 
				
			||||||
 | 
					  }, [effectiveNumber, isInView]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const intPlaces = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      Array.from({ length: finalIntLength }, (_, i) =>
 | 
				
			||||||
 | 
					        10 ** (finalIntLength - i - 1)),
 | 
				
			||||||
 | 
					    [finalIntLength],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  const decPlaces = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      newDecStrRaw
 | 
				
			||||||
 | 
					        ? Array.from({ length: newDecStrRaw.length }, (_, i) =>
 | 
				
			||||||
 | 
					            10 ** (newDecStrRaw.length - i - 1))
 | 
				
			||||||
 | 
					        : [],
 | 
				
			||||||
 | 
					    [newDecStrRaw],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const newDecValue = newDecStrRaw ? Number.parseInt(newDecStrRaw, 10) : 0;
 | 
				
			||||||
 | 
					  const prevDecValue = adjustedPrevDec ? Number.parseInt(adjustedPrevDec, 10) : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      ref={localRef}
 | 
				
			||||||
 | 
					      data-slot="sliding-number"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        display: "inline-flex",
 | 
				
			||||||
 | 
					        alignItems: "center",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {isInView && Number(number) < 0 && (
 | 
				
			||||||
 | 
					        <span style={{ marginRight: "0.25rem" }}>-</span>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {intPlaces.map((place, idx) => {
 | 
				
			||||||
 | 
					        const digitsToRight = intPlaces.length - idx - 1;
 | 
				
			||||||
 | 
					        const isSeparatorPosition
 | 
				
			||||||
 | 
					          = typeof thousandSeparator !== "undefined"
 | 
				
			||||||
 | 
					            && digitsToRight > 0
 | 
				
			||||||
 | 
					            && digitsToRight % 3 === 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <React.Fragment key={`int-${place}`}>
 | 
				
			||||||
 | 
					            <SlidingNumberRoller
 | 
				
			||||||
 | 
					              prevValue={Number.parseInt(adjustedPrevInt, 10)}
 | 
				
			||||||
 | 
					              value={Number.parseInt(newIntStr ?? "0", 10)}
 | 
				
			||||||
 | 
					              place={place}
 | 
				
			||||||
 | 
					              transition={transition}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            {isSeparatorPosition && <span>{thousandSeparator}</span>}
 | 
				
			||||||
 | 
					          </React.Fragment>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      })}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {newDecStrRaw && (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					          <span>{decimalSeparator}</span>
 | 
				
			||||||
 | 
					          {decPlaces.map(place => (
 | 
				
			||||||
 | 
					            <SlidingNumberRoller
 | 
				
			||||||
 | 
					              key={`dec-${place}`}
 | 
				
			||||||
 | 
					              prevValue={prevDecValue}
 | 
				
			||||||
 | 
					              value={newDecValue}
 | 
				
			||||||
 | 
					              place={place}
 | 
				
			||||||
 | 
					              transition={transition}
 | 
				
			||||||
 | 
					              delay={delay}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { SlidingNumber, type SlidingNumberProps };
 | 
				
			||||||
@@ -4,10 +4,10 @@ import Image from "next/image";
 | 
				
			|||||||
import Link from "next/link";
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { navbarLinks } from "@/config/site-config";
 | 
					import { navbarLinks } from "@/config/site-config";
 | 
				
			||||||
import { Button } from "@/components/ui/button";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
 | 
					import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
 | 
				
			||||||
import StarOnGithubButton from "./ui/star-on-github-button";
 | 
					import { GitHubStarsButton } from "./animate-ui/components/buttons/github-stars";
 | 
				
			||||||
 | 
					import { Button } from "./animate-ui/components/buttons/button";
 | 
				
			||||||
import { ThemeToggle } from "./ui/theme-toggle";
 | 
					import { ThemeToggle } from "./ui/theme-toggle";
 | 
				
			||||||
import CommandMenu from "./command-menu";
 | 
					import CommandMenu from "./command-menu";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,31 +39,18 @@ function Navbar() {
 | 
				
			|||||||
            href="/"
 | 
					            href="/"
 | 
				
			||||||
            className="flex cursor-pointer w-full justify-center sm:justify-start flex-row-reverse items-center gap-2 font-semibold sm:flex-row"
 | 
					            className="flex cursor-pointer w-full justify-center sm:justify-start flex-row-reverse items-center gap-2 font-semibold sm:flex-row"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <Image
 | 
					            <Image height={18} unoptimized width={18} alt="logo" src="/ProxmoxVE/logo.png" className="" />
 | 
				
			||||||
              height={18}
 | 
					 | 
				
			||||||
              unoptimized
 | 
					 | 
				
			||||||
              width={18}
 | 
					 | 
				
			||||||
              alt="logo"
 | 
					 | 
				
			||||||
              src="/ProxmoxVE/logo.png"
 | 
					 | 
				
			||||||
              className=""
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <span className="hidden md:block">Proxmox VE Helper-Scripts</span>
 | 
					            <span className="hidden md:block">Proxmox VE Helper-Scripts</span>
 | 
				
			||||||
          </Link>
 | 
					          </Link>
 | 
				
			||||||
          <div className="flex gap-2">
 | 
					          <div className="flex gap-2">
 | 
				
			||||||
            <CommandMenu />
 | 
					            <CommandMenu />
 | 
				
			||||||
            <StarOnGithubButton />
 | 
					            <GitHubStarsButton username="community-scripts" repo="ProxmoxVE" />
 | 
				
			||||||
            {navbarLinks.map(({ href, event, icon, text, mobileHidden }) => (
 | 
					            {navbarLinks.map(({ href, event, icon, text, mobileHidden }) => (
 | 
				
			||||||
              <TooltipProvider key={event}>
 | 
					              <TooltipProvider key={event}>
 | 
				
			||||||
                <Tooltip delayDuration={100}>
 | 
					                <Tooltip delayDuration={100}>
 | 
				
			||||||
                  <TooltipTrigger
 | 
					                  <TooltipTrigger className={mobileHidden ? "hidden lg:block" : ""}>
 | 
				
			||||||
                    className={mobileHidden ? "hidden lg:block" : ""}
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    <Button variant="ghost" size="icon" asChild>
 | 
					                    <Button variant="ghost" size="icon" asChild>
 | 
				
			||||||
                      <Link
 | 
					                      <Link target="_blank" href={href} data-umami-event={event}>
 | 
				
			||||||
                        target="_blank"
 | 
					 | 
				
			||||||
                        href={href}
 | 
					 | 
				
			||||||
                        data-umami-event={event}
 | 
					 | 
				
			||||||
                      >
 | 
					 | 
				
			||||||
                        {icon}
 | 
					                        {icon}
 | 
				
			||||||
                        <span className="sr-only">{text}</span>
 | 
					                        <span className="sr-only">{text}</span>
 | 
				
			||||||
                      </Link>
 | 
					                      </Link>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,9 +10,9 @@ export const basePath = process.env.BASE_PATH || "";
 | 
				
			|||||||
export const navbarLinks = [
 | 
					export const navbarLinks = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    href: `https://github.com/community-scripts/${basePath}`,
 | 
					    href: `https://github.com/community-scripts/${basePath}`,
 | 
				
			||||||
    event: "Github",
 | 
					    event: "GitHub",
 | 
				
			||||||
    icon: <FaGithub className="h-4 w-4" />,
 | 
					    icon: <FaGithub className="h-4 w-4" />,
 | 
				
			||||||
    text: "Github",
 | 
					    text: "GitHub",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    href: `https://discord.gg/2wvnMDgdnU`,
 | 
					    href: `https://discord.gg/2wvnMDgdnU`,
 | 
				
			||||||
@@ -22,9 +22,9 @@ export const navbarLinks = [
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    href: `https://github.com/community-scripts/${basePath}/blob/main/CHANGELOG.md`,
 | 
					    href: `https://github.com/community-scripts/${basePath}/blob/main/CHANGELOG.md`,
 | 
				
			||||||
    event: "Change Log",
 | 
					    event: "Changelog",
 | 
				
			||||||
    icon: <Scroll className="h-4 w-4" />,
 | 
					    icon: <Scroll className="h-4 w-4" />,
 | 
				
			||||||
    text: "Change Log",
 | 
					    text: "Changelog",
 | 
				
			||||||
    mobileHidden: true,
 | 
					    mobileHidden: true,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								frontend/src/hooks/use-is-in-view.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								frontend/src/hooks/use-is-in-view.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import type { UseInViewOptions } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useInView } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UseIsInViewOptions = {
 | 
				
			||||||
 | 
					  inView?: boolean;
 | 
				
			||||||
 | 
					  inViewOnce?: boolean;
 | 
				
			||||||
 | 
					  inViewMargin?: UseInViewOptions["margin"];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function useIsInView<T extends HTMLElement = HTMLElement>(
 | 
				
			||||||
 | 
					  ref: React.Ref<T>,
 | 
				
			||||||
 | 
					  options: UseIsInViewOptions = {},
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const { inView, inViewOnce = false, inViewMargin = "0px" } = options;
 | 
				
			||||||
 | 
					  const localRef = React.useRef<T>(null);
 | 
				
			||||||
 | 
					  React.useImperativeHandle(ref, () => localRef.current as T);
 | 
				
			||||||
 | 
					  const inViewResult = useInView(localRef, {
 | 
				
			||||||
 | 
					    once: inViewOnce,
 | 
				
			||||||
 | 
					    margin: inViewMargin,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const isInView = !inView || inViewResult;
 | 
				
			||||||
 | 
					  return { ref: localRef, isInView };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { useIsInView, type UseIsInViewOptions };
 | 
				
			||||||
							
								
								
									
										36
									
								
								frontend/src/lib/get-strict-context.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								frontend/src/lib/get-strict-context.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getStrictContext<T>(
 | 
				
			||||||
 | 
					  name?: string,
 | 
				
			||||||
 | 
					): readonly [
 | 
				
			||||||
 | 
					    ({
 | 
				
			||||||
 | 
					      value,
 | 
				
			||||||
 | 
					      children,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					      value: T;
 | 
				
			||||||
 | 
					      children?: React.ReactNode;
 | 
				
			||||||
 | 
					    }) => React.JSX.Element,
 | 
				
			||||||
 | 
					    () => T,
 | 
				
			||||||
 | 
					  ] {
 | 
				
			||||||
 | 
					  const Context = React.createContext<T | undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Provider = ({
 | 
				
			||||||
 | 
					    value,
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					  }: {
 | 
				
			||||||
 | 
					    value: T;
 | 
				
			||||||
 | 
					    children?: React.ReactNode;
 | 
				
			||||||
 | 
					  }) => <Context.Provider value={value}>{children}</Context.Provider>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const useSafeContext = () => {
 | 
				
			||||||
 | 
					    const ctx = React.useContext(Context);
 | 
				
			||||||
 | 
					    if (ctx === undefined) {
 | 
				
			||||||
 | 
					      throw new Error(`useContext must be used within ${name ?? "a Provider"}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ctx;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return [Provider, useSafeContext] as const;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { getStrictContext };
 | 
				
			||||||
@@ -13,12 +13,18 @@ setting_up_container
 | 
				
			|||||||
network_check
 | 
					network_check
 | 
				
			||||||
update_os
 | 
					update_os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Installing audiobookshelf"
 | 
					msg_info "Installing Dependencies"
 | 
				
			||||||
 | 
					$STD apt-get install -y ffmpeg
 | 
				
			||||||
 | 
					msg_ok "Installed Dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Setup audiobookshelf"
 | 
				
			||||||
curl -fsSL https://advplyr.github.io/audiobookshelf-ppa/KEY.gpg >/etc/apt/trusted.gpg.d/audiobookshelf-ppa.asc
 | 
					curl -fsSL https://advplyr.github.io/audiobookshelf-ppa/KEY.gpg >/etc/apt/trusted.gpg.d/audiobookshelf-ppa.asc
 | 
				
			||||||
echo "deb [signed-by=/etc/apt/trusted.gpg.d/audiobookshelf-ppa.asc] https://advplyr.github.io/audiobookshelf-ppa ./" >/etc/apt/sources.list.d/audiobookshelf.list
 | 
					echo "deb [signed-by=/etc/apt/trusted.gpg.d/audiobookshelf-ppa.asc] https://advplyr.github.io/audiobookshelf-ppa ./" >/etc/apt/sources.list.d/audiobookshelf.list
 | 
				
			||||||
$STD apt-get update
 | 
					$STD apt update
 | 
				
			||||||
$STD apt install audiobookshelf
 | 
					$STD apt install -y audiobookshelf
 | 
				
			||||||
msg_ok "Installed audiobookshelf"
 | 
					echo "FFMPEG_PATH=/usr/bin/ffmpeg" >>/etc/default/audiobookshelf
 | 
				
			||||||
 | 
					echo "FFPROBE_PATH=/usr/bin/ffprobe" >>/etc/default/audiobookshelf
 | 
				
			||||||
 | 
					msg_ok "Setup audiobookshelf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
motd_ssh
 | 
					motd_ssh
 | 
				
			||||||
customize
 | 
					customize
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										330
									
								
								install/autocaliweb-install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								install/autocaliweb-install.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,330 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2025 Community Scripts ORG
 | 
				
			||||||
 | 
					# Author: vhsdream
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://github.com/gelbphoenix/autocaliweb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					verb_ip6
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					setting_up_container
 | 
				
			||||||
 | 
					network_check
 | 
				
			||||||
 | 
					update_os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Installing dependencies"
 | 
				
			||||||
 | 
					$STD apt-get install -y --no-install-recommends \
 | 
				
			||||||
 | 
					  python3-dev \
 | 
				
			||||||
 | 
					  sqlite3 \
 | 
				
			||||||
 | 
					  build-essential \
 | 
				
			||||||
 | 
					  libldap2-dev \
 | 
				
			||||||
 | 
					  libssl-dev \
 | 
				
			||||||
 | 
					  libsasl2-dev \
 | 
				
			||||||
 | 
					  imagemagick \
 | 
				
			||||||
 | 
					  ghostscript \
 | 
				
			||||||
 | 
					  libmagic1 \
 | 
				
			||||||
 | 
					  libxi6 \
 | 
				
			||||||
 | 
					  libxslt1.1 \
 | 
				
			||||||
 | 
					  libxtst6 \
 | 
				
			||||||
 | 
					  libxrandr2 \
 | 
				
			||||||
 | 
					  libxkbfile1 \
 | 
				
			||||||
 | 
					  libxcomposite1 \
 | 
				
			||||||
 | 
					  libopengl0 \
 | 
				
			||||||
 | 
					  libnss3 \
 | 
				
			||||||
 | 
					  libxkbcommon0 \
 | 
				
			||||||
 | 
					  libegl1 \
 | 
				
			||||||
 | 
					  libxdamage1 \
 | 
				
			||||||
 | 
					  libgl1 \
 | 
				
			||||||
 | 
					  libglx-mesa0 \
 | 
				
			||||||
 | 
					  xz-utils \
 | 
				
			||||||
 | 
					  xdg-utils \
 | 
				
			||||||
 | 
					  inotify-tools \
 | 
				
			||||||
 | 
					  binutils \
 | 
				
			||||||
 | 
					  unrar-free \
 | 
				
			||||||
 | 
					  zip
 | 
				
			||||||
 | 
					msg_ok "Installed dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit"
 | 
				
			||||||
 | 
					KEPUB_VERSION="$(/usr/bin/kepubify --version | awk '{print $2}')"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Installing Calibre"
 | 
				
			||||||
 | 
					CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)"
 | 
				
			||||||
 | 
					CALIBRE_VERSION=${CALIBRE_RELEASE#v}
 | 
				
			||||||
 | 
					curl -fsSL https://github.com/kovidgoyal/calibre/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz
 | 
				
			||||||
 | 
					mkdir -p /opt/calibre
 | 
				
			||||||
 | 
					$STD tar -xf /tmp/calibre.txz -C /opt/calibre
 | 
				
			||||||
 | 
					rm /tmp/calibre.txz
 | 
				
			||||||
 | 
					$STD /opt/calibre/calibre_postinstall
 | 
				
			||||||
 | 
					msg_ok "Calibre installed"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup_uv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Configuring Autocaliweb"
 | 
				
			||||||
 | 
					INSTALL_DIR="/opt/autocaliweb"
 | 
				
			||||||
 | 
					CONFIG_DIR="/etc/autocaliweb"
 | 
				
			||||||
 | 
					CALIBRE_LIB_DIR="/opt/calibre-library"
 | 
				
			||||||
 | 
					INGEST_DIR="/opt/acw-book-ingest"
 | 
				
			||||||
 | 
					SERVICE_USER="acw"
 | 
				
			||||||
 | 
					SERVICE_GROUP="acw"
 | 
				
			||||||
 | 
					SCRIPTS_DIR="${INSTALL_DIR}/scripts"
 | 
				
			||||||
 | 
					export VIRTUAL_ENV="${INSTALL_DIR}/venv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp}
 | 
				
			||||||
 | 
					mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals}
 | 
				
			||||||
 | 
					mkdir -p "$INSTALL_DIR"/{metadata_change_logs,metadata_temp}
 | 
				
			||||||
 | 
					mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"}
 | 
				
			||||||
 | 
					echo "$CALIBRE_VERSION" >"$INSTALL_DIR"/CALIBRE_RELEASE
 | 
				
			||||||
 | 
					echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE
 | 
				
			||||||
 | 
					sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cd "$INSTALL_DIR"
 | 
				
			||||||
 | 
					$STD uv venv "$VIRTUAL_ENV"
 | 
				
			||||||
 | 
					$STD uv sync --all-extras --active
 | 
				
			||||||
 | 
					cat <<EOF >./dirs.json
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "ingest_folder": "$INGEST_DIR",
 | 
				
			||||||
 | 
					  "calibre_library_dir": "$CALIBRE_LIB_DIR",
 | 
				
			||||||
 | 
					  "tmp_conversion_dir": "$CONFIG_DIR/.acw_conversion_tmp"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "$SERVICE_USER"
 | 
				
			||||||
 | 
					ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins
 | 
				
			||||||
 | 
					cat <<EOF >"$INSTALL_DIR"/.env
 | 
				
			||||||
 | 
					ACW_INSTALL_DIR=$INSTALL_DIR
 | 
				
			||||||
 | 
					ACW_CONFIG_DIR=$CONFIG_DIR
 | 
				
			||||||
 | 
					ACW_USER=$SERVICE_USER
 | 
				
			||||||
 | 
					ACW_GROUP=$SERVICE_GROUP
 | 
				
			||||||
 | 
					LIBRARY_DIR=$CALIBRE_LIB_DIR
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					msg_ok "Configured Autocaliweb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Creating ACWSync Plugin for KOReader"
 | 
				
			||||||
 | 
					cd "$INSTALL_DIR"/koreader/plugins
 | 
				
			||||||
 | 
					PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)"
 | 
				
			||||||
 | 
					echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest
 | 
				
			||||||
 | 
					$STD zip -r koplugin.zip acwsync.koplugin/
 | 
				
			||||||
 | 
					cp -r koplugin.zip "$INSTALL_DIR"/cps/static
 | 
				
			||||||
 | 
					msg_ok "Created ACWSync Plugin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Initializing databases"
 | 
				
			||||||
 | 
					KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify")
 | 
				
			||||||
 | 
					EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert")
 | 
				
			||||||
 | 
					CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH")
 | 
				
			||||||
 | 
					curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db
 | 
				
			||||||
 | 
					curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/app.db -o "$CONFIG_DIR"/app.db
 | 
				
			||||||
 | 
					sqlite3 "$CONFIG_DIR/app.db" <<EOS
 | 
				
			||||||
 | 
					UPDATE settings SET
 | 
				
			||||||
 | 
					    config_kepubifypath='$KEPUBIFY_PATH',
 | 
				
			||||||
 | 
					    config_converterpath='$EBOOK_CONVERT_PATH',
 | 
				
			||||||
 | 
					    config_binariesdir='$CALIBRE_BIN_DIR',
 | 
				
			||||||
 | 
					    config_calibre_dir='$CALIBRE_LIB_DIR',
 | 
				
			||||||
 | 
					    config_logfile='$CONFIG_DIR/autocaliweb.log',
 | 
				
			||||||
 | 
					    config_access_logfile='$CONFIG_DIR/access.log'
 | 
				
			||||||
 | 
					WHERE 1=1;
 | 
				
			||||||
 | 
					EOS
 | 
				
			||||||
 | 
					msg_ok "Initialized databases"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Creating scripts and service files"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# auto-ingest watcher
 | 
				
			||||||
 | 
					cat <<EOF >"$SCRIPTS_DIR"/ingest_watcher.sh
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSTALL_PATH="$INSTALL_DIR"
 | 
				
			||||||
 | 
					WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_PATH}/dirs.json | grep -o '[^"]*\$')
 | 
				
			||||||
 | 
					echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Monitor the folder for new files
 | 
				
			||||||
 | 
					/usr/bin/inotifywait -m -r --format="%e %w%f" -e close_write -e moved_to "\$WATCH_FOLDER" |
 | 
				
			||||||
 | 
					while read -r events filepath ; do
 | 
				
			||||||
 | 
					    echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..."
 | 
				
			||||||
 | 
					    # Use the Python interpreter from the virtual environment
 | 
				
			||||||
 | 
					    \${INSTALL_PATH}/venv/bin/python \${INSTALL_PATH}/scripts/ingest_processor.py "\$filepath"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# auto-zipper
 | 
				
			||||||
 | 
					cat <<EOF >"$SCRIPTS_DIR"/auto_zipper_wrapper.sh
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Source virtual environment
 | 
				
			||||||
 | 
					source ${INSTALL_DIR}/venv/bin/activate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WAKEUP="23:59"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while true; do
 | 
				
			||||||
 | 
					    # Replace expr with modern Bash arithmetic (safer and less prone to parsing issues)
 | 
				
			||||||
 | 
					    # fix: expr: non-integer argument and sleep: missing operand
 | 
				
			||||||
 | 
					    SECS=\$(( \$(date -d "\$WAKEUP" +%s) - \$(date -d "now" +%s) ))
 | 
				
			||||||
 | 
					    if [[ \$SECS -lt 0 ]]; then
 | 
				
			||||||
 | 
					        SECS=\$(( \$(date -d "tomorrow \$WAKEUP" +%s) - \$(date -d "now" +%s) ))
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    echo "[acw-auto-zipper] Next run in \$SECS seconds."
 | 
				
			||||||
 | 
					    sleep \$SECS &
 | 
				
			||||||
 | 
					    wait \$!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Use virtual environment python
 | 
				
			||||||
 | 
					    python ${SCRIPTS_DIR}/auto_zip.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if [[ \$? == 1 ]]; then
 | 
				
			||||||
 | 
					    echo "[acw-auto-zipper] Error occurred during script initialisation."
 | 
				
			||||||
 | 
					    elif [[ \$? == 2 ]]; then
 | 
				
			||||||
 | 
					    echo "[acw-auto-zipper] Error occurred while zipping today's files."
 | 
				
			||||||
 | 
					    elif [[ \$? == 3 ]]; then
 | 
				
			||||||
 | 
					    echo "[acw-auto-zipper] Error occurred while trying to remove zipped files."
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sleep 60
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# metadata change detector
 | 
				
			||||||
 | 
					cat <<EOF >"$SCRIPTS_DIR"/metadata_change_detector_wrapper.sh
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					# metadata_change_detector_wrapper.sh - Wrapper for periodic metadata enforcement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Source virtual environment
 | 
				
			||||||
 | 
					source ${INSTALL_DIR}/venv/bin/activate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configuration
 | 
				
			||||||
 | 
					CHECK_INTERVAL=300  # Check every 5 minutes (300 seconds)
 | 
				
			||||||
 | 
					METADATA_LOGS_DIR="${INSTALL_DIR}/metadata_change_logs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "[metadata-change-detector] Starting metadata change detector service..."
 | 
				
			||||||
 | 
					echo "[metadata-change-detector] Checking for changes every \$CHECK_INTERVAL seconds"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while true; do
 | 
				
			||||||
 | 
					    # Check if there are any log files to process
 | 
				
			||||||
 | 
					    if [ -d "\$METADATA_LOGS_DIR" ] && [ "\$(ls -A \$METADATA_LOGS_DIR 2>/dev/null)" ]; then
 | 
				
			||||||
 | 
					        echo "[metadata-change-detector] Found metadata change logs, processing..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Process each log file
 | 
				
			||||||
 | 
					        for log_file in "\$METADATA_LOGS_DIR"/*.json; do
 | 
				
			||||||
 | 
					            if [ -f "\$log_file" ]; then
 | 
				
			||||||
 | 
					                log_name=\$(basename "\$log_file")
 | 
				
			||||||
 | 
					                echo "[metadata-change-detector] Processing log: \$log_name"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Call cover_enforcer.py with the log file
 | 
				
			||||||
 | 
					                 ${INSTALL_DIR}/venv/bin/python  ${SCRIPTS_DIR}/cover_enforcer.py --log "\$log_name"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if [ \$? -eq 0 ]; then
 | 
				
			||||||
 | 
					                    echo "[metadata-change-detector] Successfully processed \$log_name"
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    echo "[metadata-change-detector] Error processing \$log_name"
 | 
				
			||||||
 | 
					                fi
 | 
				
			||||||
 | 
					            fi
 | 
				
			||||||
 | 
					        done
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        echo "[metadata-change-detector] No metadata changes detected"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    echo "[metadata-change-detector] Sleeping for \$CHECK_INTERVAL seconds..."
 | 
				
			||||||
 | 
					    sleep \$CHECK_INTERVAL
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					chmod +x "$SCRIPTS_DIR"/{ingest_watcher.sh,auto_zipper_wrapper.sh,metadata_change_detector_wrapper.sh}
 | 
				
			||||||
 | 
					chown -R "$SERVICE_USER":"$SERVICE_GROUP" {"$INSTALL_DIR","$CONFIG_DIR","$INGEST_DIR","$CALIBRE_LIB_DIR"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cat <<EOF >/etc/systemd/system/autocaliweb.service
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Autocaliweb
 | 
				
			||||||
 | 
					After=network.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					Type=simple
 | 
				
			||||||
 | 
					User=$SERVICE_USER
 | 
				
			||||||
 | 
					Group=$SERVICE_GROUP
 | 
				
			||||||
 | 
					WorkingDirectory=$INSTALL_DIR
 | 
				
			||||||
 | 
					Environment=PATH=$INSTALL_DIR/venv/bin:/usr/bin:/bin
 | 
				
			||||||
 | 
					Environment=PYTHONPATH=$SCRIPTS_DIR:$INSTALL_DIR
 | 
				
			||||||
 | 
					Environment=PYTHONDONTWRITEBYTECODE=1
 | 
				
			||||||
 | 
					Environment=PYTHONUNBUFFERED=1
 | 
				
			||||||
 | 
					Environment=CALIBRE_DBPATH=$CONFIG_DIR
 | 
				
			||||||
 | 
					EnvironmentFile=$INSTALL_DIR/.env
 | 
				
			||||||
 | 
					ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/cps.py -p $CONFIG_DIR/app.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					RestartSec=10
 | 
				
			||||||
 | 
					StandardOutput=journal
 | 
				
			||||||
 | 
					StandardError=journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cat <<EOF >/etc/systemd/system/acw-ingest-service.service
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Autocaliweb Ingest Processor Service
 | 
				
			||||||
 | 
					After=autocaliweb.service
 | 
				
			||||||
 | 
					Requires=autocaliweb.service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					User=${SERVICE_USER}
 | 
				
			||||||
 | 
					Group=${SERVICE_GROUP}
 | 
				
			||||||
 | 
					WorkingDirectory=${INSTALL_DIR}
 | 
				
			||||||
 | 
					Environment=CALIBRE_DBPATH=${CONFIG_DIR}
 | 
				
			||||||
 | 
					Environment=HOME=${CONFIG_DIR}
 | 
				
			||||||
 | 
					ExecStart=/bin/bash ${SCRIPTS_DIR}/ingest_watcher.sh
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					StandardOutput=journal
 | 
				
			||||||
 | 
					StandardError=journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cat <<EOF >/etc/systemd/system/acw-auto-zipper.service
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Autocaliweb Auto Zipper Service
 | 
				
			||||||
 | 
					After=network.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					User=${SERVICE_USER}
 | 
				
			||||||
 | 
					Group=${SERVICE_GROUP}
 | 
				
			||||||
 | 
					WorkingDirectory=${INSTALL_DIR}
 | 
				
			||||||
 | 
					Environment=CALIBRE_DBPATH=${CONFIG_DIR}
 | 
				
			||||||
 | 
					ExecStart=${SCRIPTS_DIR}/auto_zipper_wrapper.sh
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					StandardOutput=journal
 | 
				
			||||||
 | 
					StandardError=journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cat <<EOF >/etc/systemd/system/metadata-change-detector.service
 | 
				
			||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Autocaliweb Metadata Change Detector
 | 
				
			||||||
 | 
					After=network.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					User=${SERVICE_USER}
 | 
				
			||||||
 | 
					Group=${SERVICE_GROUP}
 | 
				
			||||||
 | 
					WorkingDirectory=${INSTALL_DIR}
 | 
				
			||||||
 | 
					ExecStart=/bin/bash ${SCRIPTS_DIR}/metadata_change_detector_wrapper.sh
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					StandardOutput=journal
 | 
				
			||||||
 | 
					StandardError=journal
 | 
				
			||||||
 | 
					Environment=CALIBRE_DBPATH=${CONFIG_DIR}
 | 
				
			||||||
 | 
					Environment=HOME=${CONFIG_DIR}
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					systemctl -q enable --now autocaliweb acw-ingest-service acw-auto-zipper metadata-change-detector
 | 
				
			||||||
 | 
					msg_ok "Created scripts and service files"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					motd_ssh
 | 
				
			||||||
 | 
					customize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Cleaning up"
 | 
				
			||||||
 | 
					$STD apt-get -y autoremove
 | 
				
			||||||
 | 
					$STD apt-get -y autoclean
 | 
				
			||||||
 | 
					msg_ok "Cleaned"
 | 
				
			||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
# Copyright (c) 2021-2025 tteck
 | 
					# Copyright (c) 2021-2025 tteck
 | 
				
			||||||
# Author: tteck (tteckster)
 | 
					# Author: tteck (tteckster)
 | 
				
			||||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
# Source: https://0xerr0r.github.io/blocky/latest/
 | 
					# Source: https://0xerr0r.github.io/blocky
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
 | 
					source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
 | 
				
			||||||
color
 | 
					color
 | 
				
			||||||
@@ -13,14 +13,12 @@ setting_up_container
 | 
				
			|||||||
network_check
 | 
					network_check
 | 
				
			||||||
update_os
 | 
					update_os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Installing Blocky"
 | 
					fetch_and_deploy_gh_release "blocky" "0xERR0R/blocky" "prebuild" "latest" "/opt/blocky" "blocky_*_linux_x86_64.tar.gz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Configuring Blocky"
 | 
				
			||||||
if systemctl is-active systemd-resolved >/dev/null 2>&1; then
 | 
					if systemctl is-active systemd-resolved >/dev/null 2>&1; then
 | 
				
			||||||
  systemctl disable -q --now systemd-resolved
 | 
					  systemctl disable -q --now systemd-resolved
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
mkdir /opt/blocky
 | 
					 | 
				
			||||||
RELEASE=$(curl -fsSL https://api.github.com/repos/0xERR0R/blocky/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
					 | 
				
			||||||
curl -fsSL "https://github.com/0xERR0R/blocky/releases/download/v${RELEASE}/blocky_v${RELEASE}_Linux_x86_64.tar.gz" | tar -xzf - -C /opt/blocky/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cat <<EOF >/opt/blocky/config.yml
 | 
					cat <<EOF >/opt/blocky/config.yml
 | 
				
			||||||
# configuration documentation: https://0xerr0r.github.io/blocky/latest/configuration/
 | 
					# configuration documentation: https://0xerr0r.github.io/blocky/latest/configuration/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,7 +61,7 @@ log:
 | 
				
			|||||||
  # optional: Log level (one from trace, debug, info, warn, error). Default: info
 | 
					  # optional: Log level (one from trace, debug, info, warn, error). Default: info
 | 
				
			||||||
  level: info
 | 
					  level: info
 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
msg_ok "Installed Blocky"
 | 
					msg_ok "Configured Blocky"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Creating Service"
 | 
					msg_info "Creating Service"
 | 
				
			||||||
cat <<EOF >/etc/systemd/system/blocky.service
 | 
					cat <<EOF >/etc/systemd/system/blocky.service
 | 
				
			||||||
@@ -77,7 +75,7 @@ ExecStart=/opt/blocky/./blocky --config config.yml
 | 
				
			|||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
$STD systemctl enable --now blocky
 | 
					$STD systemctl enable -q --now blocky
 | 
				
			||||||
msg_ok "Created Service"
 | 
					msg_ok "Created Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
motd_ssh
 | 
					motd_ssh
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,7 @@ $STD npm run build --configuration=production
 | 
				
			|||||||
msg_ok "Built Frontend"
 | 
					msg_ok "Built Frontend"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Creating Environment"
 | 
					msg_info "Creating Environment"
 | 
				
			||||||
mkdir -p /opt/booklore_storage{/data,/books}
 | 
					mkdir -p /opt/booklore_storage{/data,/books,/bookdrop}
 | 
				
			||||||
cat <<EOF >/opt/booklore_storage/.env
 | 
					cat <<EOF >/opt/booklore_storage/.env
 | 
				
			||||||
DATABASE_URL=jdbc:mariadb://localhost:3306/$DB_NAME
 | 
					DATABASE_URL=jdbc:mariadb://localhost:3306/$DB_NAME
 | 
				
			||||||
DATABASE_USERNAME=$DB_USER
 | 
					DATABASE_USERNAME=$DB_USER
 | 
				
			||||||
@@ -55,6 +55,7 @@ BOOKLORE_PORT=6060
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
BOOKLORE_DATA_PATH=/opt/booklore_storage/data
 | 
					BOOKLORE_DATA_PATH=/opt/booklore_storage/data
 | 
				
			||||||
BOOKLORE_BOOKS_PATH=/opt/booklore_storage/books
 | 
					BOOKLORE_BOOKS_PATH=/opt/booklore_storage/books
 | 
				
			||||||
 | 
					BOOKLORE_BOOKDROP_PATH=/opt/booklore_storage/bookdrop
 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
msg_ok "Created Environment"
 | 
					msg_ok "Created Environment"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,17 @@ $STD apt-get install -y lsb-release
 | 
				
			|||||||
msg_ok "Installed Dependencies"
 | 
					msg_ok "Installed Dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RELEASE=$(curl -fsSL https://api.github.com/repos/bunkerity/bunkerweb/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
					RELEASE=$(curl -fsSL https://api.github.com/repos/bunkerity/bunkerweb/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }')
 | 
				
			||||||
msg_info "Installing BunkerWeb v${RELEASE} (Patience)"
 | 
					msg_warn "WARNING: This script will run an external installer from a third-party source (install-bunkerweb.sh)."
 | 
				
			||||||
 | 
					msg_warn "The following code is NOT maintained or audited by our repository."
 | 
				
			||||||
 | 
					msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:"
 | 
				
			||||||
 | 
					msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→  https://github.com/bunkerity/bunkerweb/raw/v${RELEASE}/misc/install-bunkerweb.sh"
 | 
				
			||||||
 | 
					echo
 | 
				
			||||||
 | 
					read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM
 | 
				
			||||||
 | 
					if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then
 | 
				
			||||||
 | 
					  msg_error "Aborted by user. No changes have been made."
 | 
				
			||||||
 | 
					  exit 10
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					msg_info "Installing BunkerWeb (Patience)"
 | 
				
			||||||
curl -fsSL -o install-bunkerweb.sh https://github.com/bunkerity/bunkerweb/raw/v${RELEASE}/misc/install-bunkerweb.sh
 | 
					curl -fsSL -o install-bunkerweb.sh https://github.com/bunkerity/bunkerweb/raw/v${RELEASE}/misc/install-bunkerweb.sh
 | 
				
			||||||
chmod +x install-bunkerweb.sh
 | 
					chmod +x install-bunkerweb.sh
 | 
				
			||||||
$STD ./install-bunkerweb.sh --yes
 | 
					$STD ./install-bunkerweb.sh --yes
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user