fixes lol
All checks were successful
Build and Push / build-all (push) Successful in 1m48s

This commit is contained in:
doomtube 2026-01-10 03:00:41 -05:00
parent 33624d3b02
commit 896a3b77d7
5 changed files with 293 additions and 85 deletions

View file

@ -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 @@
</div>
<div class="calendar-days">
{#each calendarDays as day}
<span class="calendar-day" class:today={isToday(day)} class:empty={!day}>
<span
class="calendar-day"
class:today={isToday(day)}
class:holiday={isHoliday(day)}
class:empty={!day}
title={getHolidayName(day) || ''}
>
{day || ''}
</span>
{/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;

View file

@ -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);

View file

@ -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<number, { name: string, type: string }>} - 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;
}