Skip to content

Commit ef6c6a1

Browse files
authored
fix: resolve Twitch embed CSP errors with iframe and request intercep… (#8)
### Fixed Twitch Stream Embedding - **Issue**: Twitch streams were failing to load due to Content Security Policy (CSP) violations and missing parent parameter - **Error Messages**: - "Refused to frame 'https://www.twitch.tv/' because an ancestor violates the following Content Security Policy directive" - "Twitch embed error message:[NoParent] parent query string value was not specified" - **Solution**: Updated Twitch iframe implementation to use proper parent parameter for Electron environment - **Affected File**: `src/renderer/src/components/StreamCard.tsx` ## Technical Details - Implemented request interception in Electron main process to add parent parameter to Twitch embeds - Removes Content-Security-Policy headers from Twitch responses - Added permissive CSP meta tag to HTML for iframe compatibility - Uses direct iframe embedding for Twitch streams with parent=localhost - ReactPlayer continues to handle all other stream types (YouTube, HLS, DASH, etc.) ## Summary by Sourcery Resolve Twitch embed CSP errors in the Electron app by intercepting requests to inject required parameters, stripping restrictive CSP headers, and updating the embed implementation to support direct iframe embedding. Bug Fixes: - Fix Twitch streams failing to load due to missing parent parameter and CSP violations Enhancements: - Intercept Twitch embed requests in the Electron main process to add parent and referrer parameters - Remove Content-Security-Policy headers from Twitch responses to prevent CSP errors - Add a permissive CSP meta tag and load the Twitch embed script in the HTML - Update StreamCard to use a direct Twitch iframe embedding with muted parent parameter Chores: - Bump application version to 1.2.1
2 parents 7567210 + 6a60749 commit ef6c6a1

6 files changed

Lines changed: 256 additions & 5 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,6 @@ cline_docs_backup/*
6060
cline_docs/*
6161
BUILD_INSTRUCTIONS.md
6262
BUILD_SUMMARY.md
63+
RELEASE_NOTES_v1.2.1.md
64+
ISSUE_TWITCH_EMBED_ERROR.md
65+
GIT_COMMIT_MESSAGE_v1.2.1.md

PR.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Pull Request: StreamGrid v1.2.0 - Performance Optimizations & Cross-Platform Build Support
2+
3+
## Title
4+
feat: Major performance optimizations and cross-platform build support for StreamGrid v1.2.0
5+
6+
## Description
7+
8+
This PR introduces significant performance improvements and adds comprehensive cross-platform build support to StreamGrid. The changes focus on optimizing rendering performance, memory management, and enabling builds for Windows, macOS, and Linux platforms.
9+
10+
### 🎯 Key Improvements
11+
12+
1. **Performance Optimizations**
13+
- Removed artificial 3-second loading delay for instant app startup
14+
- Implemented virtual rendering for efficient grid display with large numbers of streams
15+
- Added player pooling system to reduce memory usage and improve stream switching
16+
- Introduced debounced state updates to reduce re-render frequency
17+
- Created web workers for offloading layout calculations
18+
- Added lazy loading for chat components
19+
- Implemented performance monitoring hooks for tracking metrics
20+
21+
2. **Cross-Platform Build Support**
22+
- Configured electron-builder for Windows, macOS, and Linux builds
23+
- Added support for multiple package formats:
24+
- Windows: NSIS installer (.exe)
25+
- macOS: DMG installer (.dmg)
26+
- Linux: AppImage, DEB, and RPM packages
27+
- Fixed TypeScript compilation errors preventing production builds
28+
- Added missing dependencies (terser) for JavaScript minification
29+
30+
3. **Enhanced Features**
31+
- Grid management system with save/load functionality
32+
- Import/export grid configurations
33+
- Local file support for streams (Issue #4)
34+
- Improved error handling and recovery
35+
- Better auto-save behavior (increased delay to 5 seconds)
36+
37+
### 📊 Performance Impact
38+
39+
- **Startup Time**: Reduced from ~3.5s to <0.5s (removed artificial delay)
40+
- **Memory Usage**: Optimized through player pooling and proper cleanup
41+
- **Rendering**: Virtual rendering enables smooth performance with 50+ streams
42+
- **Bundle Size**: Code splitting reduces initial load size
43+
44+
### 🏗️ Technical Changes
45+
46+
#### New Components & Hooks
47+
- `VirtualStreamGrid.tsx` - Optimized grid with viewport-based rendering
48+
- `OptimizedStreamCard.tsx` - Memoized stream card with player pooling
49+
- `LazyChat.tsx` - Lazy-loaded chat component
50+
- `usePerformanceMonitor.ts` - Performance tracking utilities
51+
- `usePlayerPool.ts` - Video player resource management
52+
- `useDebouncedStore.ts` - Debounced state updates
53+
- `useLayoutWorker.ts` - Web worker integration for layout calculations
54+
55+
#### Modified Core Components
56+
- `App.tsx` - Removed loading delay, added performance monitoring
57+
- `StreamGrid.tsx` - Integrated virtual rendering
58+
- `StreamCard.tsx` - Optimized with memoization and player pooling
59+
- `useStreamStore.ts` - Added debouncing and optimized selectors
60+
61+
#### Build Configuration
62+
- Updated `electron-builder.yml` with Linux configuration
63+
- Enhanced `package.json` with platform-specific build scripts
64+
- Fixed TypeScript configuration for production builds
65+
66+
### 📦 Dependencies Added
67+
68+
```json
69+
{
70+
"dependencies": {
71+
"comlink": "^4.4.2",
72+
"react-window": "^1.8.11",
73+
"react-window-infinite-loader": "^1.0.10",
74+
"web-vitals": "^5.1.0"
75+
},
76+
"devDependencies": {
77+
"terser": "^5.43.1",
78+
"@testing-library/react": "^16.3.0",
79+
"@vitest/ui": "^3.2.4",
80+
"vitest": "^3.2.4"
81+
}
82+
}
83+
```
84+
85+
### 🧪 Testing
86+
87+
- Added comprehensive performance tests in `__tests__/performance.test.tsx`
88+
- Validated virtual rendering with 100+ streams
89+
- Tested memory usage patterns over extended periods
90+
- Confirmed smooth 60fps during drag operations
91+
- Successfully built and tested Windows installer (96.3 MB)
92+
93+
### 📝 Documentation
94+
95+
Created comprehensive build documentation:
96+
- `BUILD_INSTRUCTIONS.md` - Detailed platform-specific build guide
97+
- `BUILD_SUMMARY.md` - Quick reference for build status and commands
98+
- Updated README with new features and capabilities
99+
100+
### 🐛 Bug Fixes
101+
102+
- Fixed TypeScript errors in multiple files preventing production builds
103+
- Resolved Twitch streams not starting (added required parent parameter)
104+
- Fixed grid rename functionality
105+
- Corrected unused variable warnings
106+
107+
### 🔄 Breaking Changes
108+
109+
None - All changes are backward compatible.
110+
111+
### 📋 Changelog
112+
113+
#### Added
114+
- Virtual rendering for performance optimization
115+
- Player pooling system for memory management
116+
- Cross-platform build support (Windows, macOS, Linux)
117+
- Performance monitoring and metrics tracking
118+
- Web worker for layout calculations
119+
- Lazy loading for chat components
120+
- Comprehensive build documentation
121+
- Performance test suite
122+
123+
#### Changed
124+
- Removed artificial 3-second loading delay
125+
- Optimized state updates with debouncing
126+
- Increased auto-save delay from 2s to 5s
127+
- Enhanced error boundary handling
128+
- Improved grid management UI
129+
130+
#### Fixed
131+
- TypeScript compilation errors
132+
- Missing terser dependency
133+
- Twitch stream initialization issues
134+
- Grid rename functionality
135+
- Memory leaks from unmounted components
136+
137+
### 🚀 Next Steps
138+
139+
1. Test builds on macOS and Linux platforms
140+
2. Monitor performance metrics in production
141+
3. Consider additional optimizations based on user feedback
142+
4. Implement remaining features from roadmap
143+
144+
### 📸 Screenshots
145+
146+
*Note: The application now starts instantly without the loading screen delay, and supports smooth operation with 50+ simultaneous streams.*
147+
148+
### ✅ Checklist
149+
150+
- [x] Code follows project style guidelines
151+
- [x] Self-review completed
152+
- [x] Comments added for complex code sections
153+
- [x] Documentation updated
154+
- [x] No breaking changes introduced
155+
- [x] Tests added and passing
156+
- [x] Windows build tested successfully
157+
- [ ] macOS build pending test
158+
- [ ] Linux build pending test
159+
160+
### 🔗 Related Issues
161+
162+
- Partially addresses performance optimization goals from project roadmap
163+
- Fixes Issue #4 (Local file support)
164+
- Implements cross-platform build support
165+
166+
### 💬 Additional Notes
167+
168+
This PR represents a major milestone for StreamGrid v1.2.0, bringing significant performance improvements and enabling distribution across all major desktop platforms. The Windows build has been successfully tested and is ready for release. macOS and Linux builds are configured and ready to be built on their respective platforms.
169+
170+
---
171+
172+
**Git Commit Message:**
173+
```
174+
feat: Major performance optimizations and cross-platform build support (v1.2.0)
175+
176+
- Remove artificial loading delay for instant startup
177+
- Implement virtual rendering for efficient grid display
178+
- Add player pooling system for memory optimization
179+
- Configure cross-platform builds (Windows, macOS, Linux)
180+
- Add performance monitoring and web workers
181+
- Fix TypeScript errors and build issues
182+
- Create comprehensive build documentation
183+
184+
BREAKING CHANGES: None

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "streamgrid",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "A high-performance multi-stream viewer application for watching multiple live streams simultaneously in customizable grid layouts",
55
"main": "./out/main/index.js",
66
"author": "LordKnish",

src/main/index.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron'
1+
import { app, shell, BrowserWindow, ipcMain, dialog, protocol } from 'electron'
22
import { join } from 'path'
33
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
44
import { autoUpdater } from 'electron-updater'
@@ -8,6 +8,11 @@ import fs from 'fs/promises'
88
import path from 'path'
99
import type { SavedGrid, GridManifest } from '../shared/types/grid'
1010

11+
// Register custom protocol for Twitch embeds
12+
protocol.registerSchemesAsPrivileged([
13+
{ scheme: 'streamgrid', privileges: { secure: true, standard: true, corsEnabled: true } }
14+
])
15+
1116
// Function to fetch latest GitHub release version
1217
async function getLatestGitHubVersion(): Promise<string> {
1318
return new Promise((resolve, reject) => {
@@ -106,10 +111,57 @@ function createWindow(): void {
106111
preload: join(__dirname, '../preload/index.js'),
107112
sandbox: false,
108113
devTools: true,
109-
webSecurity: false // Disable web security to allow local file access
114+
webSecurity: false, // Disable web security to allow local file access
115+
allowRunningInsecureContent: true
110116
}
111117
})
112118

119+
// Intercept Twitch embed requests to add parent parameter
120+
mainWindow.webContents.session.webRequest.onBeforeRequest(
121+
{
122+
urls: ['https://embed.twitch.tv/*channel=*']
123+
},
124+
(details, callback) => {
125+
let redirectURL = details.url
126+
127+
const params = new URLSearchParams(redirectURL.replace('https://embed.twitch.tv/', ''))
128+
if (params.get('parent') != '') {
129+
callback({})
130+
return
131+
}
132+
params.set('parent', 'localhost')
133+
params.set('referrer', 'https://localhost/')
134+
135+
redirectURL = 'https://embed.twitch.tv/?' + params.toString()
136+
console.log('Adjust Twitch embed URL to:', redirectURL)
137+
138+
callback({
139+
cancel: false,
140+
redirectURL
141+
})
142+
}
143+
)
144+
145+
// Remove CSP headers for Twitch embeds
146+
mainWindow.webContents.session.webRequest.onHeadersReceived(
147+
{
148+
urls: ['https://www.twitch.tv/*', 'https://player.twitch.tv/*', 'https://embed.twitch.tv/*']
149+
},
150+
(details, callback) => {
151+
const responseHeaders = details.responseHeaders || {}
152+
153+
console.log('Removing CSP headers for:', details.url)
154+
155+
delete responseHeaders['Content-Security-Policy']
156+
delete responseHeaders['content-security-policy']
157+
158+
callback({
159+
cancel: false,
160+
responseHeaders
161+
})
162+
}
163+
)
164+
113165
// Remove Content Security Policy since we've disabled web security for local file access
114166
// This allows local files to be loaded without CSP restrictions
115167

src/renderer/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
<html>
33
<head>
44
<meta charset="UTF-8" />
5+
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src *; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';">
56
<title>StreamGrid</title>
67
<link rel="preconnect" href="https://fonts.googleapis.com">
78
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
89
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9-
<!-- CSP removed to allow local file access -->
10+
<!-- Load Twitch embed script -->
11+
<script src="https://embed.twitch.tv/embed/v1.js"></script>
1012
</head>
1113

1214
<body>

src/renderer/src/components/StreamCard.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ import { useStreamStore } from '../store/useStreamStore'
1919
// Lazy load ReactPlayer for better initial load time
2020
const ReactPlayer = lazy(() => import('react-player'))
2121

22+
// Declare Twitch global
23+
declare global {
24+
interface Window {
25+
Twitch: {
26+
Embed: new (elementId: string, options: any) => any
27+
Player: any
28+
}
29+
}
30+
}
31+
2232
// Helper function to detect stream type
2333
const extractYoutubeVideoId = (url: string): string | null => {
2434
try {
@@ -497,7 +507,7 @@ const StreamCard: React.FC<StreamCardProps> = memo(({ stream, onRemove, onEdit,
497507
>
498508
{streamType === 'twitch' && channelName ? (
499509
<iframe
500-
src={`https://player.twitch.tv/?channel=${channelName}&parent=localhost&parent=${window.location.hostname}`}
510+
src={`https://player.twitch.tv/?channel=${channelName}&parent=localhost&muted=true`}
501511
width="100%"
502512
height="100%"
503513
frameBorder="0"

0 commit comments

Comments
 (0)