From 896a3b77d7c4339211a904f80ad6d7b46ed739d0 Mon Sep 17 00:00:00 2001 From: doomtube Date: Sat, 10 Jan 2026 03:00:41 -0500 Subject: [PATCH] fixes lol --- .../lib/components/chat/ChatTerminal.svelte | 39 ++++- .../lib/components/watch/YouTubePlayer.svelte | 33 +++- frontend/src/lib/data/holidays.js | 110 ++++++++++++ frontend/src/routes/+page.svelte | 157 +++++++++--------- .../src/routes/chat/terminal/+page.svelte | 39 ++++- 5 files changed, 293 insertions(+), 85 deletions(-) create mode 100644 frontend/src/lib/data/holidays.js diff --git a/frontend/src/lib/components/chat/ChatTerminal.svelte b/frontend/src/lib/components/chat/ChatTerminal.svelte index c9ab076..9cdd83f 100644 --- a/frontend/src/lib/components/chat/ChatTerminal.svelte +++ b/frontend/src/lib/components/chat/ChatTerminal.svelte @@ -3,6 +3,7 @@ import { fly, fade, slide } from 'svelte/transition'; import { browser } from '$app/environment'; import { isAuthenticated } from '$lib/stores/auth'; + import { getHolidaysForMonth } from '$lib/data/holidays.js'; import { connectionStatus } from '$lib/chat/chatStore'; import TerminalTabBar from '$lib/components/terminal/TerminalTabBar.svelte'; import TerminalCore from '$lib/components/terminal/TerminalCore.svelte'; @@ -231,8 +232,20 @@ calendarDate.getFullYear() === today.getFullYear(); } + function isHoliday(day) { + if (!day) return false; + return calendarHolidays.has(day); + } + + function getHolidayName(day) { + if (!day) return null; + const info = calendarHolidays.get(day); + return info ? info.name : null; + } + $: calendarDays = getCalendarDays(calendarDate); $: calendarMonthYear = calendarDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); + $: calendarHolidays = getHolidaysForMonth(calendarDate.getFullYear(), calendarDate.getMonth()); // Timezone definitions const timezones = [ @@ -330,7 +343,13 @@
{#each calendarDays as day} - + {day || ''} {/each} @@ -725,6 +744,24 @@ background: #9eeea1; } + .calendar-day.holiday { + background: #ff9800; + color: #0d1117; + font-weight: 600; + } + + .calendar-day.holiday:not(.today):hover { + background: #ffb74d; + } + + .calendar-day.today.holiday { + background: linear-gradient(135deg, #7ee787 50%, #ff9800 50%); + } + + .calendar-day.today.holiday:hover { + background: linear-gradient(135deg, #9eeea1 50%, #ffb74d 50%); + } + .timezone-row { display: flex; justify-content: space-between; diff --git a/frontend/src/lib/components/watch/YouTubePlayer.svelte b/frontend/src/lib/components/watch/YouTubePlayer.svelte index 5352004..3173ef8 100644 --- a/frontend/src/lib/components/watch/YouTubePlayer.svelte +++ b/frontend/src/lib/components/watch/YouTubePlayer.svelte @@ -22,8 +22,11 @@ let lastSeekTime = 0; // Track last seek time for rate-limiting let leadInEndedAt = 0; // Track when lead-in ended for grace period let wasInLeadIn = false; // Track previous lead-in state + let restartInProgress = false; // Track when video restart is in progress (prevents drift detection) + let restartStartedAt = 0; // When restart started const SYNC_CHECK_INTERVAL = 1000; // Check sync every second (matches server sync interval) + const RESTART_GRACE_PERIOD = 3000; // 3 seconds grace period during video restart const DRIFT_THRESHOLD = 2; // Seek if drift > 2 seconds (CyTube-style tight sync) const MIN_SYNC_INTERVAL = 1000; // Minimum 1 second between sync checks (server pushes every 1s) const CONTROLLER_SEEK_DEBOUNCE = 2000; // Debounce controller seeks by 2 seconds @@ -122,6 +125,9 @@ // YT.PlayerState: UNSTARTED (-1), ENDED (0), PLAYING (1), PAUSED (2), BUFFERING (3), CUED (5) // Always handle ENDED state - crucial for playlist advancement if (state === window.YT.PlayerState.ENDED) { + // Set restart flag to prevent drift detection during transition + restartInProgress = true; + restartStartedAt = Date.now(); dispatch('ended'); // Also notify server directly - this is a fallback for when duration-based detection fails watchSync.videoEnded(); @@ -187,10 +193,29 @@ const shouldBePlaying = storeState.playbackState === 'playing'; const hasVideoEnded = playerState === window.YT.PlayerState.ENDED; + // During restart transition (video ended, waiting for server response), skip drift detection + // This prevents false "Controller seek detected" when video loops/restarts + if (restartInProgress) { + if (now - restartStartedAt >= RESTART_GRACE_PERIOD) { + // Grace period expired, reset flag + restartInProgress = false; + } else { + // Still in grace period - only ensure video starts playing if needed, no sync + if (shouldBePlaying && !isPlayerPlaying && !isBuffering && !hasVideoEnded) { + ignoreStateChange = true; + player.playVideo(); + setTimeout(() => { ignoreStateChange = false; }, 1500); + } + return; + } + } + // During lead-in period, let video buffer without seeking // Server sends leadIn=true for 3 seconds after play starts if (storeState.leadIn) { wasInLeadIn = true; + // Lead-in started, clear restart flag since server has responded + restartInProgress = false; // During lead-in, just ensure video is loading/buffering // Don't seek or sync position - wait for lead-in to complete if (shouldBePlaying && !isPlayerPlaying && !isBuffering && !hasVideoEnded) { @@ -266,6 +291,7 @@ $: if (playerReady && $watchSync.currentVideo?.id !== currentPlaylistItemId) { currentPlaylistItemId = $watchSync.currentVideo?.id; durationReportedForItemId = null; // Reset so we report duration for new video + restartInProgress = false; // Video changed, clear restart flag const newVideoId = $watchSync.currentVideo?.youtubeVideoId; if (newVideoId && player) { videoId = newVideoId; @@ -302,13 +328,18 @@ } else if (action === 'seek' && currentTime !== undefined) { player.seekTo(currentTime, true); } else if (action === 'skip' || action === 'video_changed') { + // Server has responded, clear restart flag + restartInProgress = false; // Video change will be handled by the reactive statement above setTimeout(() => checkAndSync(true), 1000); } else if (action === 'locked_restart' || action === 'repeat') { + // Server has responded with restart, clear the restart flag + restartInProgress = false; // Locked video loop - seek to beginning and play player.seekTo(0, true); player.playVideo(); - setTimeout(() => checkAndSync(true), 1000); + // Delay sync check longer to allow player to stabilize + setTimeout(() => checkAndSync(true), 2000); } setTimeout(() => { ignoreStateChange = false; }, 1000); diff --git a/frontend/src/lib/data/holidays.js b/frontend/src/lib/data/holidays.js new file mode 100644 index 0000000..579e773 --- /dev/null +++ b/frontend/src/lib/data/holidays.js @@ -0,0 +1,110 @@ +/** + * Holiday data for terminal calendar + * Includes both Indian national holidays and international holidays + */ + +/** + * Fixed holidays - same date every year + * Format: { month: 0-11, day: 1-31, name: string, type: 'indian' | 'international' } + */ +export const fixedHolidays = [ + // Indian National Holidays (Fixed) + { month: 0, day: 26, name: 'Republic Day', type: 'indian' }, + { month: 7, day: 15, name: 'Independence Day', type: 'indian' }, + { month: 9, day: 2, name: 'Gandhi Jayanti', type: 'indian' }, + + // International Holidays (Fixed) + { month: 0, day: 1, name: "New Year's Day", type: 'international' }, + { month: 1, day: 14, name: "Valentine's Day", type: 'international' }, + { month: 9, day: 31, name: 'Halloween', type: 'international' }, + { month: 11, day: 25, name: 'Christmas', type: 'international' }, + { month: 11, day: 31, name: "New Year's Eve", type: 'international' }, +]; + +/** + * Variable holidays - different date each year (lunar calendar based) + * Format: { [year]: Array<{ month: 0-11, day: 1-31, name: string, type: string }> } + */ +export const variableHolidays = { + 2024: [ + { month: 2, day: 25, name: 'Holi', type: 'indian' }, + { month: 3, day: 10, name: 'Eid ul-Fitr', type: 'indian' }, + { month: 5, day: 17, name: 'Eid ul-Adha', type: 'indian' }, + { month: 7, day: 19, name: 'Raksha Bandhan', type: 'indian' }, + { month: 7, day: 26, name: 'Janmashtami', type: 'indian' }, + { month: 8, day: 7, name: 'Ganesh Chaturthi', type: 'indian' }, + { month: 9, day: 12, name: 'Dussehra', type: 'indian' }, + { month: 10, day: 1, name: 'Diwali', type: 'indian' }, + ], + 2025: [ + { month: 2, day: 14, name: 'Holi', type: 'indian' }, + { month: 2, day: 31, name: 'Eid ul-Fitr', type: 'indian' }, + { month: 5, day: 7, name: 'Eid ul-Adha', type: 'indian' }, + { month: 7, day: 9, name: 'Raksha Bandhan', type: 'indian' }, + { month: 7, day: 16, name: 'Janmashtami', type: 'indian' }, + { month: 7, day: 27, name: 'Ganesh Chaturthi', type: 'indian' }, + { month: 9, day: 2, name: 'Dussehra', type: 'indian' }, + { month: 9, day: 20, name: 'Diwali', type: 'indian' }, + ], + 2026: [ + { month: 2, day: 4, name: 'Holi', type: 'indian' }, + { month: 2, day: 20, name: 'Eid ul-Fitr', type: 'indian' }, + { month: 4, day: 27, name: 'Eid ul-Adha', type: 'indian' }, + { month: 7, day: 28, name: 'Raksha Bandhan', type: 'indian' }, + { month: 8, day: 4, name: 'Janmashtami', type: 'indian' }, + { month: 8, day: 17, name: 'Ganesh Chaturthi', type: 'indian' }, + { month: 8, day: 20, name: 'Dussehra', type: 'indian' }, + { month: 10, day: 8, name: 'Diwali', type: 'indian' }, + ], + 2027: [ + { month: 2, day: 22, name: 'Holi', type: 'indian' }, + { month: 2, day: 10, name: 'Eid ul-Fitr', type: 'indian' }, + { month: 4, day: 17, name: 'Eid ul-Adha', type: 'indian' }, + { month: 7, day: 17, name: 'Raksha Bandhan', type: 'indian' }, + { month: 7, day: 25, name: 'Janmashtami', type: 'indian' }, + { month: 8, day: 6, name: 'Ganesh Chaturthi', type: 'indian' }, + { month: 9, day: 9, name: 'Dussehra', type: 'indian' }, + { month: 9, day: 28, name: 'Diwali', type: 'indian' }, + ], +}; + +/** + * Get all holidays for a specific month and year + * @param {number} year - Full year (e.g., 2024) + * @param {number} month - Month (0-11) + * @returns {Map} - Map of day -> holiday info + */ +export function getHolidaysForMonth(year, month) { + const holidayMap = new Map(); + + // Add fixed holidays for this month + for (const holiday of fixedHolidays) { + if (holiday.month === month) { + holidayMap.set(holiday.day, { name: holiday.name, type: holiday.type }); + } + } + + // Add variable holidays for this year and month + const yearHolidays = variableHolidays[year]; + if (yearHolidays) { + for (const holiday of yearHolidays) { + if (holiday.month === month) { + holidayMap.set(holiday.day, { name: holiday.name, type: holiday.type }); + } + } + } + + return holidayMap; +} + +/** + * Check if a specific date is a holiday + * @param {number} year - Full year + * @param {number} month - Month (0-11) + * @param {number} day - Day of month (1-31) + * @returns {{ name: string, type: string } | null} + */ +export function getHolidayInfo(year, month, day) { + const monthHolidays = getHolidaysForMonth(year, month); + return monthHolidays.get(day) || null; +} diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 2041544..2f9ac79 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -203,32 +203,30 @@
-
-

Live Streams

-

Watch your favorite streamers live

-
- {#if loading}

Loading streams...

@@ -1036,6 +1029,61 @@
{/if} + + {#if !watchRoomsLoading && watchRooms.filter(r => r.currentVideoTitle).length > 0} + + {/if} + {#if !videosLoading && videos.length > 0}
@@ -1132,61 +1180,6 @@
{/if} - - {#if !watchRoomsLoading && watchRooms.filter(r => r.currentVideoTitle).length > 0} - - {/if} - {#if !ebooksLoading && ebookFiles.length > 0}
diff --git a/frontend/src/routes/chat/terminal/+page.svelte b/frontend/src/routes/chat/terminal/+page.svelte index 7325f55..96e46ab 100644 --- a/frontend/src/routes/chat/terminal/+page.svelte +++ b/frontend/src/routes/chat/terminal/+page.svelte @@ -3,6 +3,7 @@ import { browser } from '$app/environment'; import { connectionStatus } from '$lib/chat/chatStore'; import { auth, isAuthenticated } from '$lib/stores/auth'; + import { getHolidaysForMonth } from '$lib/data/holidays.js'; import { siteSettings } from '$lib/stores/siteSettings'; import TerminalTabBar from '$lib/components/terminal/TerminalTabBar.svelte'; import TerminalCore from '$lib/components/terminal/TerminalCore.svelte'; @@ -129,8 +130,20 @@ calendarDate.getFullYear() === today.getFullYear(); } + function isHoliday(day) { + if (!day) return false; + return calendarHolidays.has(day); + } + + function getHolidayName(day) { + if (!day) return null; + const info = calendarHolidays.get(day); + return info ? info.name : null; + } + $: calendarDays = getCalendarDays(calendarDate); $: calendarMonthYear = calendarDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); + $: calendarHolidays = getHolidaysForMonth(calendarDate.getFullYear(), calendarDate.getMonth()); // Timezone definitions const timezones = [ @@ -224,7 +237,13 @@
{#each calendarDays as day} - + {day || ''} {/each} @@ -534,6 +553,24 @@ font-weight: 600; } + .calendar-day.holiday { + background: #ff9800; + color: #0d1117; + font-weight: 600; + } + + .calendar-day.holiday:not(.today):hover { + background: #ffb74d; + } + + .calendar-day.today.holiday { + background: linear-gradient(135deg, #4caf50 50%, #ff9800 50%); + } + + .calendar-day.today.holiday:hover { + background: linear-gradient(135deg, #66bb6a 50%, #ffb74d 50%); + } + .tab-content { flex: 1 1 0; display: flex;