perf: throttle timeupdate handler to 250ms restricting expensive DOM queries on main thread
perf: optimize DOM querying with smart video caching and selector improvements
-Implement smart caching for querySelectorAll('video') to reduce DOM scans
Optimize querySelector in VideoTypeHandler by combining multiple queries
Reduce cleanup timeouts from 1000ms to 200ms for faster memory release
Add early exit in export to avoid unnecessary processing
perf: Reduce polling interval from 1000ms to 500ms for improved responsiveness
-Optimize storage usage interval from 30s to 60s to reduce API calls
perf: Implement pre-computed color cache and optimized interpolation in getProgressColor()
perf: deduplicate getYouTubePageType, closest, and URLSearchParams calls in hot paths
fix: Block homepage auto-play ads from being saved as regular videos
Prioritize YouTube API sources (YTHelper or ytInitialPlayerResponse) over DOM parsing to avoid "N/A" values caused by delayed page rendering.
refactor: centralize ad selectors and cache ad DOM checks
- Reuse AdSelectors classes in ad monitor checks
- Added TTL-cached, scoped anchor-in-ad guard for Home/Search/Channel inline preview checks (avoids global
document.querySelectorAll scans per tick).
- Cached Shorts
#shorts-player lookup in the ad monitor (reduces repeated DOM queries during rapid checks).
refactor: centralize player DOM refs with best-effort cache
perf: Remove unneeded setTimeout and setInterval polling loops to reduce CPU footprint.
Extended explanation for my own sanity:
rebindInterval (in processVideo)
- What it did: It ran every 500ms checking if the active video element had changed or if the URL had changed without triggering a navigation event. If it did, it would clear events and trigger handleNavigation. It would also manually try to attach event listeners to #movie_player or #shorts-player.
- Why it was remove: YouTube's navigation events (yt-navigate-finish, yt-helper-api-ready) are extremely reliable now, especially with our recent fixes to the helper. We also use a robust timeupdate handler that verifies isValidNewId(newId) natively. Polling every half-second just to check if the video element changed was causing immense CPU overhead, and the event-driven system already catches context changes efficiently.
progressPollIntervalId (in processVideo)
-What it did: It ran every 500ms and manually called updateStatus() using setInterval, acting as a backup in case the timeupdate event failed to fire. It would check Date.now() - lastTick <= 2500 and manually save progress if the video was playing.
- Why it was remove: The timeupdate event natively fires ~4 times per second whenever a video plays. Because our timeupdate handler (processVideo.handler) is now heavily optimized and correctly throttled to 250ms, it is virtually impossible for a video to be playing without triggering timeupdate. The fallback interval was just running redundant checks in the background doing nothing 99% of the time.
secondaryProgressPollIntervalId (in processVideo)
- What it did: It ran every 500ms specifically when watching Shorts. It explicitly polled for the existence of both #movie_player (a background regular video, like the miniplayer) and #shorts-player simultaneously, and tried to manually save progress for both of them at the same time.
- Why it was remove: The createPlayerContextManager architecture we implemented earlier correctly isolates the timeupdate contexts! The miniplayer and the Shorts player now maintain their own dedicated, isolated timeupdate handlers. We no longer need a global loop manually checking both players; they update themselves independently via their own events.
checkInterval (in applySeek)
- What it did: It ran every 200ms after trying to restore a saved timestamp, manually comparing videoEl.currentTime to the target time. If they matched, it manually declared the seek successful.
- Why it was remove: HTML5 videos natively fire the seeked event the exact moment a seek finishes buffering. We already attach an addEventListener('seeked'). The polling interval was completely redundant and just wasting CPU cycles comparing math while waiting for the native event to fire anyway.
periodicCheck (in adStateMonitor)
- What it did: Ran every 15000ms (15s) and simply printed a console.log saying "Monitor activo (watch), anuncio aún presente".
- Why it was remove: It was purely diagnostic code left over from development.
refactor: Implement event delegation and template literals for video modal.
feat: Centralize video context and refactor Settings UI with template literals
fix: relax playlist matching for miniplayer to maintain association on Shorts page
Added
- Created
resolveVideoContext() as a pure source of truth for all YouTube DOM inspections, providing a standardized metadata object (type, videoElement, videoId, playerObject, isAd).
Refactored
- Centralized Context: Consolidated video context logic across
getActiveVideoElement(), applySeek(), and notifySeekOrProgress(). Eliminates "Fallback Hell" and improves cross-page stability.
- Declarative UI: Refactored
showSettingsUI() to use declarative Template Literals and modular rendering helpers (renderLanguageSection, etc.).
- Simplified
getActiveVideoElement() to be a pure wrapper of resolveVideoContext().
Implemented unified input reading by name attribute in the Settings UI, removing manual element references.
Fixed critical bug where miniplayer progress was lost during Shorts navigation (Coexistence Support).
Implemented container-aware progress saving to allow concurrent tracking for multiple video contexts.
Improved context detection to correctly identify miniplayer state on Shorts pages.
Updated cleanupAll to always preserve the movie player observer if a video is active.
Refactored processVideo to use container-isolated handlers, preventing Shorts from removing the miniplayer's progress observer.
Fixed timeupdate handler "suicide" bug: the change detection logic now ignores URL changes when the miniplayer coexists with Shorts, preventing the handler from incorrectly unbinding.
Enhanced resolveVideoContext and getPlayerDomRefs to detect the miniplayer even while on a Shorts page.
Security
- Integrated
escapeHTML() and setInnerHTML() in the Settings UI refactor to ensure safe rendering of translated labels and user data.
refactor: Restructure miniplayer data extraction into MiniplayerDataExtractor module
tweak: centralize selectors & add miniplayer UI
BIG CHANGE
Ref: moving to observers per player
Update youtube-playback-plox.user.js
ref: refine observer and added more ad selectors
perf: optimize video metadata extraction by caching session context to avoid redundant API queries during progress saving
fix: resolve CSP and Trusted Types violations in Helper API fallback using GM_addElement and TT policies
ref: getCascadedVideoInfo to extract and consolidate playlistTitle directly within the video object.
Every saved video now carries its playlist context independently of legacy metadata keys.
perf: optimize video metadata extraction by caching session context to avoid redundant API queries during progress saving
fix: precisely detect shorts previews using internal /shorts/ href links
fix: ensure playback bar icon is visible by default even if saving is disabled
fix: enforce video type settings within saveStatus to respect user preferences
fix: Resolve WeakMap TypeError in VideoObserverManager during navigation
fix: resolve CSP and Trusted Types violations in Helper API fallback using GM_addElement and TT policies
feat: implement dedicated saveMiniplayer and dynamic context detection for transitions
feat: Add dynamic metadata refresh for viewCount in saveStatus
feat: Consolidate playlistTitle in video objects and optimize modal with parallel batching.
tweak: remove internal 'thumb' property from JSON export
refactor: remove 'thumb' from storage, build thumbnail URL dynamically in UI
refactor: Centralize metadata extraction in getCascadedVideoInfo and simplify observers
refactor: Ultra-simplify saving flow by removing all redundant parameters in saveStatus and observers
refactor: Align getCascadedVideoInfo with FreeTube schema and fix syntax error
refactor: Make savePreview explicit and remove redundant spread/fallbacks
refactor: simplify display initialization by removing redundant player fallbacks and DOM queries
perf: replace DOM querySelector seek detection with direct boolean parameter
ref: optimize playlist selection UI and reduce DOM queries
feat: Activate updateProgressBarGradient feature in saveStatus loop
Refactor: multiple high-frequency functions now use DOMHelpers
fix: resolved statistics discrepancy in saved videos modal
fix: storage usage display in video modal
Resolved a race condition where the used/total space indicator would sometimes fail to appear. Added cache invalidation to ensure the statistics are always refreshed when the video list is rendered.
fix: reinitialize VideoObserverManager during SPA navigation to ensure players are tracked
fix: implement async retry loop for playlist extraction to mitigate SPA auto-advance race conditions
refactor(shorts): replace DOM parsing with youtubei API for accurate video views
well... DOM parsing still there but only as backup
docs: optimize JSDoc for getCascadedVideoInfo waterfall strategy
refactor: Simplified the Waterfall strategy by leveraging the new global getVideoId function, removing redundant ID validations and improving "Self-Healing" resilience during SPA transitions.
fix: Implemented a "Kill Switch" for polling sessions and integrated a global stopAllSessions() cleanup into the navigation flow. Added a Navigation Token mechanism to prevent race conditions where sessions started just before a page transition could bypass the cleanup.
fix: Shorts metapanel selection returning null due to invalid selectors
refactor: debug wording to avoid conflict with variables
also remove redundant video ID check in saveStatus and duplicated duration extraction in PlaybackController.resume
perf: Propagate cached video duration to PlaybackController.resume to prevent redundant API polling
fix: Language Selection Persistence
fix: Robustize data migration and FreeTube integration (for v0.23.15 Beta)
refactor: Clean up redundant save logic using non-null spread pattern
for future me: https://github.com/FreeTubeApp/FreeTube/blob/fa842985/src/renderer/components/DataSettings/DataSettings.vue#L832-L839
fix: Allow playlist context reset by refining metadata merge filter
fix: persistent message logic and fixed-time pin indicator
bump to YouTube Helper API 0.9.7.6
fix: Prevent TypeError when modal is closed during async data loading
change: remove debug status, add script version to files during import/export tasks
feat: add Innertube /next fetch fallback for playlistTitle in getCascadedVideoInfo
fix: remove orphaned miniplayer time display span on transition back to watch player
feat: allow auto-closing of persistent toasts when explicit duration is provided
feat: add shrinking life progress bar to floating toasts
fix: reset playlist selection state on modal close
Merge pull request #19 from Alplox/0.0.9
0.0.9
Added
- Playlist Title Fetch Fallback: Added a last-resort fetch via Innertube
/next endpoint to retrieve playlistTitle if DOM and cache methods fail. Only triggers on watch/miniplayer contexts when a playlistId is known but title is still missing.
- Allow auto-closing of persistent toasts when explicit duration is provided and added shrinking life progress bar to floating toasts
- Integrated
escapeHTML() in the Settings UI refactor to ensure safe rendering of translated labels and user data.
- _id Persistence: Standardized all save paths and the FreeTube export format to preserve the optional
_id field. Enforced normalizeVideoType on every write to IndexedDB, preventing format drift and data loss.
- DOM Helpers Optimization: Created centralized DOM helper functions to eliminate repetitive element lookups throughout the codebase, improving maintainability and consistency.
- Extended Context Isolation: Added specific helper contexts to eliminate repetitive queries for shorts and miniplayer elements.
- Centralized AdSelectors: Updated AdSelectors to use centralized constants, eliminating string literals and improving maintainability across ad detection logic.
Changed
- Performance Impact: Reduction in CPU usage during active YouTube navigation with improved memory management, enhanced robustness, and better responsiveness.
- Separated blocking operations from non-critical background tasks.
- Moved critical UI initialization to the beginning of the initialization sequence.
- Implemented immediate button creation for watch/embed pages.
- Added temporal caching for settings to enable early UI initialization.
- Centralized ad selectors/detector: Consolidated repeated ad-related selectors into a single
AdSelectors map with a small TTL-based DOM query cache. This reduces duplicated selector strings, improves maintainability when YouTube changes DOM, and avoids repeated querySelector calls across modules. Included homepage masthead selectors to prevent saving masthead ads.
- Specialized Observers: Replaced monolithic video processing with context-specific observers for Watch, Shorts, Miniplayer, and Previews. This eliminates cross-player data contamination and prevents blocking playback during background processing.
- Session Management: Implemented mapping to correlate video elements with their specific processing intervals, enabling precise cleanup and preventing duplicate save loops.
- Decentralized Navigation: Refactored navigation handlers to trigger the new observer system, ensuring consistent behavior across YouTube's complex navigation events.
- Improved Resume Reliability: Enhanced the resume function to fetch video duration directly from fast internal YouTube APIs rather than waiting on the slower DOM element. This prevents edge cases where the script stalls unexpectedly on
isReady.
- Enhanced Metadata Extraction: Refactored information cascading to extract and consolidate
playlistTitle directly within the video object. Every saved video now carries its playlist context independently of legacy metadata keys.
- Improved Modal Performance: Refactored the Saved Videos modal to utilize parallel batch loading for all entries, eliminating a sequential storage bottleneck.
- Video Metadata Caching: Optimized save workflows to fetch complete video metadata only ONCE per session. This prevents redundant YouTube internal API queries every second while tracking playtime progress.
- CSP & Trusted Types Fix: Refactored the YouTube Helper API fallback mechanism. This resolves the
Sink type mismatch error caused by strict CSP policies.
- In
/watch pages, miniplayer detection is now skipped entirely during navigation, eliminating unnecessary overhead.
- Replaced generic DOM scans on unknown contexts with explicit container lookups, reducing DOM traversal cost.
- Refactored seek handling to be container-isolated with AbortController cancellation, preventing cross-player seek contamination during coexistence.
- Configurable Save Intervals: Replaced the hardcoded save interval with the user-configurable settings.
- Math Operations Optimization: Implemented pre-computed caches to reduce Math and RGB calculations during progress bar updates.
- Regex Cache Optimization: Added compiled regex cache for view parsing.
- Performance: Throttled the main time update video event handlers to reduce CPU usage and UI lag.
- Parallel background tasks: Storage initialization, storage normalization, and data migration now run in parallel using
Promise.all().
- Major initialization improvement: Reduced button initialization delay by optimizing the sequence.
- Theme Detection Optimization: Replaced simple class-based theme detection with robust integration.
- DOM Query Optimization: Implemented intelligent caching for video element querying with a strict duration.
- Timeout Optimization: Reduced cleanup timeouts for quicker memory and resource release.
- Video Modal Event Delegation: Completely refactored the saved videos modal to use Event Delegation and explicit Template formatting to improve response times for large lists. Supported underlying virtualized rendering for seamless integration.
Fixed
- Modal Async Race Condition: Fixed a critical race condition that threw a
TypeError (can't access property appendChild, listContainer is null) if the saved videos modal was closed while loading data from storage.
- Correct meta-key filtering logic to ensure metadata is stripped properly during storage initialization.
- Data Migration & FreeTube Integration: Resolved legacy fields leaking into IndexedDB causing format inconsistencies.
- Metadata Extraction (Shorts/Previews): Resolved an issue where view counts were overwritten incorrectly due to cascade priority logic.
- Playlist Selection State Reset: Resolved a bug where the video selection UI persisted when closing and reopening the modal during playlist creation mode. Now,
isSelectionMode and selectedVideos are properly reset when the modal is closed.
- ProgressBar Gradient: Activated the custom color gradient feature for the playback progress bar.
- Language Selection Persistence: Resolved an issue where saving the custom language in the settings modal was ignored upon reload.
- Discrepancy in video statistics: Resolved the "Videos" count and "Renderizados" mismatch.
- Shorts Panel Detection: Resolved an issue where panels were not detected due to outdated DOM selectors in the Shorts interface.
- Stale Visit Count on Shorts: Fixed a delay where the view count displayed stale data from the previous Short.
- Incorrect Ad Detection for Grid Ads: Fixed a logic error where grid-based ads were processed incorrectly.
- Miniplayer Context Race Condition: Resolved a massive SPA race condition where videos played via miniplayer auto-advance loaded too fast for YouTube APIs to populate IDs.
- Storage usage display: Resolved a race condition where the used/total space indicator would fail to appear.
- Shorts Resume Stalling: Fixed a bug where Shorts videos failed to resume saved playback points.
- Homepage/Grid Ads Saved as Regular Videos: Fixed a bug where auto-played masthead ads were inaccurately saved as legitimate.
- Coexistence Ad Blocking Bug: Fixed critical race condition where ads in Shorts would prevent miniplayer from saving, and vice versa.
- Fixed zombie loop after navigation: Prevented an infinite seek retry loop when navigating from watch to home.
- Theme Detection: Fixed
ReferenceError: isLightTheme is not defined.
Removed
- Removed Dead Code: Deleted the legacy
processVideo and determineVideoContext functions, significantly reducing script complexity (~5000 lines).
- Removed Dead Navigation Handlers: Removed unused preparation functions and dead variables.
- Deprecated Legacy Metadata Logic: Removed obsolete playlist metadata polling paths.
- Refining Polling Logic: Eliminated hundreds of lines of redundant DOM polling intervals, moving purely to event-driven updates.