mirror of
https://github.com/yusufipk/YTChatHub.git
synced 2026-09-11 10:56:17 +00:00
feat: implement intelligent message trimming to preserve superchats and memberships while limiting regular messages
This commit is contained in:
+68
-10
@@ -6,6 +6,7 @@ import { bootstrapInnertube, type IngestionContext } from './ingestion/youtubei'
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const MAX_MESSAGES = 500;
|
const MAX_MESSAGES = 500;
|
||||||
|
const MAX_REGULAR_MESSAGES = 200; // Keep fewer regular messages
|
||||||
|
|
||||||
// Simple in-memory cache for images
|
// Simple in-memory cache for images
|
||||||
const imageCache = new Map<string, { buffer: Buffer; contentType: string; timestamp: number }>();
|
const imageCache = new Map<string, { buffer: Buffer; contentType: string; timestamp: number }>();
|
||||||
@@ -45,9 +46,9 @@ export async function startBackend() {
|
|||||||
console.log(`[Backend] ✓ YouTube chat connected successfully`);
|
console.log(`[Backend] ✓ YouTube chat connected successfully`);
|
||||||
ingestion.emitter.on('message', (message) => {
|
ingestion.emitter.on('message', (message) => {
|
||||||
store.push(message);
|
store.push(message);
|
||||||
if (store.length > MAX_MESSAGES) {
|
// Trim regularly to keep regular messages under control
|
||||||
store.splice(0, store.length - MAX_MESSAGES);
|
// This ensures we don't wait until hitting MAX_MESSAGES
|
||||||
}
|
trimMessages(store);
|
||||||
});
|
});
|
||||||
ingestion.emitter.on('error', (error) => {
|
ingestion.emitter.on('error', (error) => {
|
||||||
console.error('[Backend] Innertube ingestion error:', error);
|
console.error('[Backend] Innertube ingestion error:', error);
|
||||||
@@ -112,9 +113,9 @@ export async function startBackend() {
|
|||||||
|
|
||||||
ingestion.emitter.on('message', (message) => {
|
ingestion.emitter.on('message', (message) => {
|
||||||
store.push(message);
|
store.push(message);
|
||||||
if (store.length > MAX_MESSAGES) {
|
// Trim regularly to keep regular messages under control
|
||||||
store.splice(0, store.length - MAX_MESSAGES);
|
// This ensures we don't wait until hitting MAX_MESSAGES
|
||||||
}
|
trimMessages(store);
|
||||||
});
|
});
|
||||||
|
|
||||||
ingestion.emitter.on('error', (error) => {
|
ingestion.emitter.on('error', (error) => {
|
||||||
@@ -337,6 +338,63 @@ function extractLiveId(input: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intelligently trim messages while preserving superchats and memberships
|
||||||
|
* Regular messages are limited to MAX_REGULAR_MESSAGES
|
||||||
|
* Superchats and memberships are preserved for the entire session
|
||||||
|
*/
|
||||||
|
function trimMessages(store: ChatMessage[]): void {
|
||||||
|
// Count messages by type
|
||||||
|
let regularCount = 0;
|
||||||
|
const specialIndices: number[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < store.length; i++) {
|
||||||
|
const message = store[i];
|
||||||
|
const isSpecial = message.superChat || message.membershipGift ||
|
||||||
|
message.membershipGiftPurchase || message.isMember;
|
||||||
|
if (isSpecial) {
|
||||||
|
specialIndices.push(i);
|
||||||
|
} else {
|
||||||
|
regularCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only trim if we have too many regular messages
|
||||||
|
if (regularCount > MAX_REGULAR_MESSAGES) {
|
||||||
|
const toRemove = regularCount - MAX_REGULAR_MESSAGES;
|
||||||
|
const specialSet = new Set(specialIndices);
|
||||||
|
|
||||||
|
// Remove oldest regular messages (keep special messages)
|
||||||
|
let removed = 0;
|
||||||
|
const newStore: ChatMessage[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < store.length; i++) {
|
||||||
|
const isSpecial = specialSet.has(i);
|
||||||
|
|
||||||
|
if (isSpecial) {
|
||||||
|
// Always keep special messages
|
||||||
|
newStore.push(store[i]);
|
||||||
|
} else {
|
||||||
|
// Keep regular messages if we haven't removed enough yet
|
||||||
|
if (removed < toRemove) {
|
||||||
|
removed++;
|
||||||
|
// Skip this message (delete it)
|
||||||
|
} else {
|
||||||
|
newStore.push(store[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace store contents
|
||||||
|
store.length = 0;
|
||||||
|
store.push(...newStore);
|
||||||
|
|
||||||
|
const newRegularCount = regularCount - toRemove;
|
||||||
|
const specialCount = specialIndices.length;
|
||||||
|
console.log(`[Backend] Trimmed ${toRemove} regular messages. Now: ${newRegularCount} regular + ${specialCount} special = ${store.length} total`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function seedMockMessages(
|
function seedMockMessages(
|
||||||
store: ChatMessage[],
|
store: ChatMessage[],
|
||||||
overlayEmitter: EventEmitter<{ update: (message: ChatMessage | null) => void }>
|
overlayEmitter: EventEmitter<{ update: (message: ChatMessage | null) => void }>
|
||||||
@@ -351,9 +409,8 @@ function seedMockMessages(
|
|||||||
publishedAt: new Date().toISOString()
|
publishedAt: new Date().toISOString()
|
||||||
};
|
};
|
||||||
store.push(message);
|
store.push(message);
|
||||||
if (store.length > MAX_MESSAGES) {
|
// Trim regularly to keep regular messages under control
|
||||||
store.splice(0, store.length - MAX_MESSAGES);
|
trimMessages(store);
|
||||||
}
|
|
||||||
if (counter % 5 === 0) {
|
if (counter % 5 === 0) {
|
||||||
overlayEmitter.emit('update', message);
|
overlayEmitter.emit('update', message);
|
||||||
}
|
}
|
||||||
@@ -361,7 +418,8 @@ function seedMockMessages(
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
// Only run if this is the main module
|
||||||
|
if (require.main === module) {
|
||||||
startBackend().catch((error) => {
|
startBackend().catch((error) => {
|
||||||
console.error('Failed to start backend', error);
|
console.error('Failed to start backend', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -19,13 +19,16 @@
|
|||||||
- **UI Layout Fixed**: Adjusted the dashboard grid to eliminate unnecessary space and properly align content. The Super Chat card is now left-aligned for better visual consistency.
|
- **UI Layout Fixed**: Adjusted the dashboard grid to eliminate unnecessary space and properly align content. The Super Chat card is now left-aligned for better visual consistency.
|
||||||
- **Currency Parsing**: Improved the backend logic to correctly parse Super Chat amounts and currencies, including for non-standard formats like TRY. The hardcoded 'USD' fallback has been removed.
|
- **Currency Parsing**: Improved the backend logic to correctly parse Super Chat amounts and currencies, including for non-standard formats like TRY. The hardcoded 'USD' fallback has been removed.
|
||||||
- **Conditional Auto-Scroll**: Implemented intelligent auto-scrolling that only activates when the user is at the bottom of the chat, preventing interruptions when reading older messages.
|
- **Conditional Auto-Scroll**: Implemented intelligent auto-scrolling that only activates when the user is at the bottom of the chat, preventing interruptions when reading older messages.
|
||||||
- **Overlay Redesign**: The overlay has been completely restyled to match the dashboard's compact, dark theme. It now displays new member announcements.
|
- **Overlay Redesign**: The overlay has been completely restyled to match the dashboard's compact, dark theme with optimized spacing (30-40% more compact).
|
||||||
|
- **Overlay Timestamps Removed**: Removed timestamps from overlay for cleaner OBS display while keeping them in dashboard.
|
||||||
- **Gifted Memberships**: The 'Memberships & Milestones' panel now correctly displays the user who purchased the gift, not the recipient.
|
- **Gifted Memberships**: The 'Memberships & Milestones' panel now correctly displays the user who purchased the gift, not the recipient.
|
||||||
- **Timestamp Resolution Fixed**: Resolved critical issue where timestamps showed 1970 epoch time. Now uses `timestamp_usec` (microseconds) from YouTube data and converts properly to user's local timezone.
|
- **Timestamp Resolution Fixed**: Resolved critical issue where timestamps showed 1970 epoch time. Now uses `timestamp_usec` (microseconds) from YouTube data and converts properly to user's local timezone.
|
||||||
- **Timezone Support**: Implemented browser timezone detection and proper timestamp formatting across dashboard and overlay using `Intl.DateTimeFormat` with GMT+3 fallback.
|
- **Timezone Support**: Implemented browser timezone detection and proper timestamp formatting across dashboard using `Intl.DateTimeFormat` with GMT+3 fallback.
|
||||||
- **Visual Selection States**: Added three-tier visual feedback system - active (selected), normal, and previously-selected (dimmed) states for better user experience.
|
- **Visual Selection States**: Added three-tier visual feedback system - active (selected), normal, and previously-selected (dimmed) states for better user experience.
|
||||||
- **UI Polish**: Fixed pulse animations on initial load, hidden N/A messages, and improved overlay timestamp display.
|
- **UI Polish**: Fixed pulse animations on initial load, hidden N/A messages.
|
||||||
- **Image Proxy**: Added `/proxy/image` endpoint in backend with in-memory caching (24hr TTL, max 1000 images) to prevent YouTube CDN 429 rate limit errors. All avatars, badges, and emojis now route through proxy with stale-on-error fallback.
|
- **Image Proxy**: Added `/proxy/image` endpoint in backend with in-memory caching (24hr TTL, max 1000 images) to prevent YouTube CDN 429 rate limit errors. All avatars, badges, and emojis now route through proxy with stale-on-error fallback.
|
||||||
|
- **Message Switching Animation**: Added smooth fade-out/fade-in transitions when switching between selected messages in overlay (300ms duration).
|
||||||
|
- **Smart Message Preservation**: Implemented intelligent message trimming - regular chat messages limited to 200, but superchats and memberships preserved for entire session. Trimming happens on every message to prevent loss of special messages.
|
||||||
|
|
||||||
## Immediate Next Steps
|
## Immediate Next Steps
|
||||||
1. Test with live YouTube stream to verify badge parsing and superchat detection
|
1. Test with live YouTube stream to verify badge parsing and superchat detection
|
||||||
|
|||||||
@@ -29,12 +29,14 @@
|
|||||||
- [x] Style overlay with modern design including avatars, badges, and superchat displays.
|
- [x] Style overlay with modern design including avatars, badges, and superchat displays.
|
||||||
- [x] Ensure transparent background for OBS browser source.
|
- [x] Ensure transparent background for OBS browser source.
|
||||||
- [x] Display new member announcements on overlay.
|
- [x] Display new member announcements on overlay.
|
||||||
- [x] Timezone-aware timestamp display on overlay.
|
- [x] Optimize overlay spacing for efficient use (30-40% more compact).
|
||||||
- [x] Add entrance/exit animations for message transitions.
|
- [x] Remove timestamps from overlay for cleaner display.
|
||||||
|
- [x] Add smooth fade transitions when switching between messages.
|
||||||
- [ ] Add theme controls and customization options.
|
- [ ] Add theme controls and customization options.
|
||||||
|
|
||||||
## Phase 4 – Reliability & Polish
|
## Phase 4 – Reliability & Polish
|
||||||
- [x] Implement image proxy with caching to prevent YouTube CDN 429 errors.
|
- [x] Implement image proxy with caching to prevent YouTube CDN 429 errors.
|
||||||
|
- [x] Implement smart message storage that preserves superchats/memberships.
|
||||||
- [ ] Refactor the code
|
- [ ] Refactor the code
|
||||||
- [ ] Add log-in and unique stream id for each logged in user
|
- [ ] Add log-in and unique stream id for each logged in user
|
||||||
- [ ] Ensure the deployment.
|
- [ ] Ensure the deployment.
|
||||||
@@ -46,10 +48,12 @@
|
|||||||
- **✅ Modern UI Complete**: Dashboard features centered layout, gradient backgrounds, badges, and superchat displays
|
- **✅ Modern UI Complete**: Dashboard features centered layout, gradient backgrounds, badges, and superchat displays
|
||||||
- **✅ CORS Fixed**: Overlay SSE stream works cross-origin for OBS integration
|
- **✅ CORS Fixed**: Overlay SSE stream works cross-origin for OBS integration
|
||||||
- **✅ Rich Parsing**: Moderators, members, verified users, superchats, and membership gifts all detected and displayed. Currency parsing is now more robust.
|
- **✅ Rich Parsing**: Moderators, members, verified users, superchats, and membership gifts all detected and displayed. Currency parsing is now more robust.
|
||||||
- **✅ UI Polished**: The dashboard and overlay layouts have been refined for better spacing and alignment. The overlay now matches the dashboard's aesthetic.
|
- **✅ UI Polished**: The dashboard and overlay layouts have been refined for better spacing and alignment. Overlay is 30-40% more compact.
|
||||||
- **✅ Auto-Scroll Implemented**: Chat panels now auto-scroll intelligently.
|
- **✅ Auto-Scroll Implemented**: Chat panels now auto-scroll intelligently.
|
||||||
- **✅ Timestamp Resolution Fixed**: Critical bug resolved - timestamps now display correct current time instead of 1970 epoch
|
- **✅ Timestamp Resolution Fixed**: Critical bug resolved - timestamps now display correct current time instead of 1970 epoch
|
||||||
- **✅ Timezone Support**: All timestamps display in user's local timezone with browser detection and GMT+3 fallback
|
- **✅ Timezone Support**: Dashboard timestamps display in user's local timezone with browser detection and GMT+3 fallback
|
||||||
- **✅ Visual Selection States**: Three-tier feedback system (active/normal/previously-selected) for better UX
|
- **✅ Visual Selection States**: Three-tier feedback system (active/normal/previously-selected) for better UX
|
||||||
- **✅ Image Proxy Implemented**: Backend now proxies all YouTube CDN images (avatars, badges, emojis) with 24hr in-memory cache to prevent 429 rate limit errors
|
- **✅ Image Proxy Implemented**: Backend now proxies all YouTube CDN images (avatars, badges, emojis) with 24hr in-memory cache to prevent 429 rate limit errors
|
||||||
|
- **✅ Overlay Animations**: Smooth fade-out/in transitions when switching between messages
|
||||||
|
- **✅ Smart Storage**: Regular messages limited to 200, superchats and memberships preserved for entire session
|
||||||
- **Next**: Add search/filter, error recovery, and polish UX details
|
- **Next**: Add search/filter, error recovery, and polish UX details
|
||||||
|
|||||||
@@ -19,3 +19,5 @@
|
|||||||
- **Selection State Management**: Three-tier visual state system (active/selected, normal, previously-selected) with CSS class composition for clear user feedback.
|
- **Selection State Management**: Three-tier visual state system (active/selected, normal, previously-selected) with CSS class composition for clear user feedback.
|
||||||
- **Timestamp Resolution**: Backend uses `timestamp_usec` (microseconds) from YouTube data, converts to milliseconds, and frontend formats in user's local timezone.
|
- **Timestamp Resolution**: Backend uses `timestamp_usec` (microseconds) from YouTube data, converts to milliseconds, and frontend formats in user's local timezone.
|
||||||
- **Image Proxy Pattern**: Backend `/proxy/image` endpoint caches YouTube CDN images (avatars, badges, emojis) with MD5-hashed keys, 24hr TTL, and 1000-image LRU eviction. Returns stale cache on 429 errors or network failures. Frontend `proxyImageUrl()` helper transparently rewrites YouTube CDN URLs to use proxy.
|
- **Image Proxy Pattern**: Backend `/proxy/image` endpoint caches YouTube CDN images (avatars, badges, emojis) with MD5-hashed keys, 24hr TTL, and 1000-image LRU eviction. Returns stale cache on 429 errors or network failures. Frontend `proxyImageUrl()` helper transparently rewrites YouTube CDN URLs to use proxy.
|
||||||
|
- **Smart Message Storage**: Intelligent trimming system preserves all superchats and memberships for entire session while limiting regular messages to 200 most recent. Trimming occurs on every message addition to prevent special message loss. Uses order-preserving algorithm that identifies special messages by index and removes only oldest regular messages.
|
||||||
|
- **Overlay Animation System**: Two-state approach with `message` (backend data) and `displayMessage` (UI state) enables smooth fade transitions when switching messages. 300ms fade-out followed by content swap and automatic fade-in.
|
||||||
|
|||||||
Reference in New Issue
Block a user