From 0fa9c8599f189534017001cf69bf7806f1015b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=93=D0=BB=D0=B0=D0=B7=D1=83?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Tue, 20 Jan 2026 21:11:01 +0300 Subject: [PATCH] fixes --- src/content/content.ts | 171 +++++++++++++++++++++++++++++++++-------- 1 file changed, 139 insertions(+), 32 deletions(-) diff --git a/src/content/content.ts b/src/content/content.ts index 8882cb7..e8661c1 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -16,14 +16,18 @@ class ReelsMaster { container: null }; private observer: MutationObserver | null = null; - private storedVolume: number | null = null; + private storedVolume: number = 0.5; private storedMuted: boolean = false; + private videoVolumeListeners: WeakMap void> = new WeakMap(); constructor() { this.init(); } private init(): void { + // Сразу начинаем следить за всеми видео для мгновенного применения громкости + this.setupGlobalVideoInterceptor(); + if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.start()); } else { @@ -31,6 +35,78 @@ class ReelsMaster { } } + // Перехватываем все видео сразу при их появлении и применяем сохраненную громкость + private setupGlobalVideoInterceptor(): void { + // Применяем к уже существующим видео + this.applyVolumeToAllVideos(); + + // Следим за новыми видео через MutationObserver + const videoObserver = new MutationObserver((mutations) => { + let hasNewVideo = false; + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node instanceof HTMLVideoElement) { + hasNewVideo = true; + this.applyVolumeToVideo(node); + } else if (node instanceof HTMLElement) { + const videos = node.querySelectorAll('video'); + if (videos.length > 0) { + hasNewVideo = true; + videos.forEach(video => this.applyVolumeToVideo(video)); + } + } + } + } + }); + + videoObserver.observe(document.documentElement, { + childList: true, + subtree: true + }); + } + + private applyVolumeToAllVideos(): void { + document.querySelectorAll('video').forEach(video => { + this.applyVolumeToVideo(video); + }); + } + + private applyVolumeToVideo(video: HTMLVideoElement): void { + if (!window.location.pathname.includes('/reels/')) return; + + // Применяем сохраненную громкость + video.volume = this.storedVolume; + video.muted = this.storedMuted; + + // Добавляем слушатель на случай если Instagram перезапишет громкость + if (!this.videoVolumeListeners.has(video)) { + const listener = () => { + // Если громкость изменилась не нами, восстанавливаем + if (Math.abs(video.volume - this.storedVolume) > 0.01 || video.muted !== this.storedMuted) { + video.volume = this.storedVolume; + video.muted = this.storedMuted; + } + }; + + // Слушаем первые несколько изменений громкости для борьбы с Instagram + let volumeChangeCount = 0; + const tempListener = () => { + volumeChangeCount++; + if (volumeChangeCount <= 5) { + video.volume = this.storedVolume; + video.muted = this.storedMuted; + } else { + video.removeEventListener('volumechange', tempListener); + } + }; + + video.addEventListener('volumechange', tempListener); + video.addEventListener('loadedmetadata', listener); + video.addEventListener('play', listener); + this.videoVolumeListeners.set(video, listener); + } + } + private start(): void { console.log('Reels Master: Starting...'); this.checkForReels(); @@ -48,7 +124,9 @@ class ReelsMaster { if (currentUrl !== lastUrl) { lastUrl = currentUrl; console.log('Reels Master: URL changed to', currentUrl); - setTimeout(() => this.checkForReels(), 500); + // Сразу применяем громкость ко всем видео + this.applyVolumeToAllVideos(); + setTimeout(() => this.checkForReels(), 300); } }).observe(document.querySelector('body')!, { subtree: true, @@ -80,6 +158,10 @@ class ReelsMaster { console.log('Reels Master: Found new video element'); this.currentVideo = video; + + // Убедимся что громкость применена к текущему видео + this.applyVolumeToVideo(video); + this.injectControls(); } @@ -110,14 +192,23 @@ class ReelsMaster { private injectControls(): void { if (!this.currentVideo) return; - const actionButtons = this.findActionButtonsContainer(); + // Ищем контейнер кнопок рядом с ТЕКУЩИМ видео, а не глобально + const actionButtons = this.findActionButtonsContainer(this.currentVideo); if (!actionButtons) { console.log('Reels Master: Action buttons container not found'); - setTimeout(() => this.injectControls(), 1000); + setTimeout(() => this.injectControls(), 500); return; } - if (this.controls.container) { + // Проверяем, есть ли уже наши контролы в этом контейнере + const existingControls = actionButtons.querySelector('.reels-master-controls'); + if (existingControls) { + console.log('Reels Master: Controls already exist in this container'); + return; + } + + // Удаляем старые контролы из предыдущего контейнера + if (this.controls.container && this.controls.container.parentElement !== actionButtons) { this.controls.container.remove(); } @@ -128,21 +219,40 @@ class ReelsMaster { this.controls.container.appendChild(this.controls.downloadButton); actionButtons.insertBefore(this.controls.container, actionButtons.firstChild); - if (this.storedVolume !== null && this.currentVideo) { - this.currentVideo.volume = this.storedVolume; - } - if (this.currentVideo) { - this.currentVideo.muted = this.storedMuted; + // Синхронизируем слайдер с текущей громкостью + if (this.controls.volumeSlider) { + this.controls.volumeSlider.value = String(this.storedVolume * 100); } console.log('Reels Master: Controls injected'); } - private findActionButtonsContainer(): HTMLElement | null { - const likeButton = document.querySelector('svg[aria-label="Like"]'); + // Ищем контейнер кнопок относительно конкретного видео + private findActionButtonsContainer(video: HTMLVideoElement): HTMLElement | null { + // Ищем родительский контейнер рила для данного видео + let reelContainer = video.closest('article') || video.closest('[role="presentation"]'); + + if (!reelContainer) { + // Пробуем найти родителя вверх по дереву + let parent = video.parentElement; + for (let i = 0; i < 10 && parent; i++) { + if (parent.querySelector('svg[aria-label="Like"]')) { + reelContainer = parent; + break; + } + parent = parent.parentElement; + } + } + + if (!reelContainer) { + reelContainer = document.body; + } + + // Ищем кнопку лайка внутри контейнера текущего рила + const likeButton = reelContainer.querySelector('svg[aria-label="Like"]'); if (likeButton) { let parent = likeButton.parentElement; - while (parent) { + while (parent && parent !== reelContainer) { const childDivs = parent.querySelectorAll(':scope > div'); if (childDivs.length >= 3) { const hasLike = parent.querySelector('svg[aria-label="Like"]'); @@ -186,11 +296,13 @@ class ReelsMaster { volumeButton.onclick = () => { if (this.currentVideo) { - this.currentVideo.muted = !this.currentVideo.muted; - this.storedMuted = this.currentVideo.muted; + this.storedMuted = !this.storedMuted; + + // Применяем ко всем видео + this.applyVolumeToAllVideos(); if (this.controls.volumeSlider) { - this.controls.volumeSlider.value = this.currentVideo.muted ? '0' : String(this.currentVideo.volume * 100); + this.controls.volumeSlider.value = this.storedMuted ? '0' : String(this.storedVolume * 100); } this.updateVolumeIcon(volumeButton); } @@ -208,26 +320,23 @@ class ReelsMaster { slider.min = '0'; slider.max = '100'; - let initialValue = '50'; - if (this.currentVideo) { - if (this.currentVideo.muted) { - initialValue = '0'; - } else { - initialValue = String(this.currentVideo.volume * 100); - } - } - slider.value = initialValue; + // Используем сохраненную громкость + slider.value = String(this.storedVolume * 100); slider.className = 'reels-master-volume-slider'; slider.oninput = (e) => { if (this.currentVideo) { const value = parseInt((e.target as HTMLInputElement).value); - this.currentVideo.volume = value / 100; - this.currentVideo.muted = value === 0; + const newVolume = value / 100; + const newMuted = value === 0; - this.storedVolume = this.currentVideo.volume; - this.storedMuted = this.currentVideo.muted; + // Сохраняем настройки + this.storedVolume = newVolume; + this.storedMuted = newMuted; + + // Применяем ко всем видео сразу + this.applyVolumeToAllVideos(); const volumeControl = slider.closest('.reels-master-volume'); const volumeButton = volumeControl?.querySelector('.reels-master-volume-button') as HTMLButtonElement; @@ -241,9 +350,7 @@ class ReelsMaster { } private updateVolumeIcon(button: HTMLButtonElement): void { - if (!this.currentVideo) return; - - const volume = this.currentVideo.muted ? 0 : this.currentVideo.volume; + const volume = this.storedMuted ? 0 : this.storedVolume; let icon = ''; if (volume === 0) {