Гобелен - кликните для возврата на главную
Не прошло и месяца а Фурмановская ЦРБ в очередной раз попала в историю. На этот раз сотрудница клеветала на пациентов, а именно огласку получил случай когда сотрудница назвала пациента алкашём.
После того как в некоторых случаях перестала работать программа goodbydpi появились сложности с просмотром сайта YouTube. В конце мая 2025 года стало доступно расширение позволяющее разблокировать сайт YouTube в браузере Firefox на территории России.
Ровно 3 года назад произошло событие, которое подарило жителям планеты Змеля новый чистый праздник 6 апреля - в этот замечательный день земля забрала гнить негодяя и СПАМера Владимира Вольфовича Жириновского.
Как бы не обстояли дела в области культуры и IT-технологий, Самосвал писал статьи на связанные темы и планирует ещё написать.
На очередном конкурсе танцев, школьница отлично выступила.
Вот та самая информация: программа для продвижения канала YouTube, эта информация выложена уже несколько лет, при этом просмотров не много, а метод который описан в данном видео действительно помогает продвигать видео в YouTube.
Начал разбираться с информацией которая находится в HTTPS клиентском запросе рукопожатия.
Обратите внимание! Объект изображённый на гобилене может отличаться от общепринятого вида описанного объекта. Тут дело в том что художник видит именно так!
В втором вопросе этого чата я написал что будет несколько связанная тема с первым вопросом, как раз подошли к этому. Есть следующая ситуация - страница с тегом video и несколько скриптов обрабатывающих эту страницу большого размера(один из которых даже более 2-х мегабайт). В это видео как то с использованием скриптов добавляются видеоданные(которые приходят по Ajax в зашифрованном виде). Мне нужны эти видеоданные расшифрованные. Насколько я знаю у тега video нет возможности получить данные обратно(если не использовать src, которого в рассматриваемом случаи нет, поскольку данные добавляются через JavaScript как я уже описал). Моя идея следующая. ранее Вы уже описывали что можно добавить обработчик addEventListener который будет "логировать" все обращения. Так можно получить ссылки на события. Моя идея в том чтобы определённые события для тега video вызывались не напрямую а через некоторый "прокси", который поможет отследить какое именно событие передаёт видеоданные и поможет их получить. Эта технология включает в себя несколько вопросов, а именно, какими именно вариантами может получаться ссылка на элемент video(getElementsById, querySelector, возможно какие то иные), возможно есть способ переопределить эти методы, чтобы они возвращали не прямую ссылку на объект а "прокси" на него, который будет следить что именно сделано с объектом. Так же возможно требуется переопределить методы устанавливающие обработчики на объект и так же возвращать прокси. Я поясню - такой вариант рассматривается, поскольку как я уже написал скрипты имеют значительный размер - и поиск места добавления видео контента в коде может быть длительным(*к тому же скрипты зашифрованы), поэтому ищется обходной вариант.
Ваш подход с проксированием DOM-элементов и событий действительно может помочь перехватить видеоданные, даже если они динамически загружаются через зашифрованные Ajax-запросы. Давайте разберём эту стратегию по шагам, включая перехват методов доступа к элементам и событий.
Чтобы "подменить" возвращаемый элемент на прокси-объект, можно переопределить стандартные методы DOM.
Варианты перехвата:
а) Переопределение document.getElementById и других методов
const originalGetElementById = document.getElementById;
document.getElementById = function(id) {
const element = originalGetElementById.call(document, id);
if (id === 'targetVideo') { // Проверяем, что это нужный элемент
return createVideoProxy(element); // Возвращаем прокси
}
return element;
};
б) Перехват querySelector/querySelectorAll
const originalQuerySelector = Element.prototype.querySelector;
Element.prototype.querySelector = function(selector) {
const element = originalQuerySelector.call(this, selector);
if (element?.tagName === 'VIDEO') {
return createVideoProxy(element);
}
return element;
};
в) Перехват getElementsByTagName
const originalGetElementsByTagName = document.getElementsByTagName;
document.getElementsByTagName = function(tagName) {
const elements = originalGetElementsByTagName.call(document, tagName);
if (tagName === 'video') {
return Array.from(elements).map(el => createVideoProxy(el));
}
return elements;
};
Прокси-объект будет перехватывать все обращения к оригинальному элементу, включая:
Пример прокси:
function createVideoProxy(videoElement) {
return new Proxy(videoElement, {
get(target, prop) {
if (prop === 'addEventListener') {
return function(type, listener, options) {
console.log(`Добавлен обработчик события: ${type}`);
// Можно подменить listener на свой
const wrappedListener = function(event) {
console.log(`Событие ${type} вызвано`, event);
if (type === 'progress' || type === 'loadeddata') {
// Попытка перехватить видеоданные
console.log('Возможная передача данных:', target.src || target.srcObject);
}
return listener.call(target, event);
};
return target.addEventListener(type, wrappedListener, options);
};
}
// Перехват изменения src или srcObject
if (prop === 'src' || prop === 'srcObject') {
return target[prop];
}
if (prop === 'setAttribute') {
return function(name, value) {
console.log(`Установлен атрибут: ${name}=${value}`);
if (name === 'src') {
console.log('Обнаружена установка видео-источника:', value);
}
return target.setAttribute(name, value);
};
}
return target[prop];
},
set(target, prop, value) {
if (prop === 'src' || prop === 'srcObject') {
console.log(`Попытка установки ${prop}:`, value);
// Здесь можно перехватить URL или медиа-поток
}
target[prop] = value;
return true;
},
});
}
3. Перехват событий, связанных с загрузкой видео
Видеоданные могут передаваться через:
Пример перехвата данных через srcObject
Если видео передаётся через MediaStream или Blob:
Object.defineProperty(HTMLVideoElement.prototype, 'srcObject', {
set: function(stream) {
console.log('Обнаружен видеопоток:', stream);
// Здесь можно сохранить stream или его данные
this._srcObject = stream;
return this._srcObject;
},
get: function() {
return this._srcObject;
},
});
Если видео загружается через Ajax (например, в ArrayBuffer), можно перехватить XMLHttpRequest или fetch:
а) Переопределение XMLHttpRequest
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
xhr.open = function(method, url) {
if (url.includes('.mp4') || url.includes('video_data')) {
console.log('Обнаружен запрос видео:', url);
}
return originalOpen.apply(xhr, arguments);
};
xhr.addEventListener('load', function() {
if (xhr.responseType === 'arraybuffer' || xhr.responseType === 'blob') {
console.log('Получены видеоданные:', xhr.response);
}
});
return xhr;
};
б) Перехват fetch
const originalFetch = window.fetch;
window.fetch = function(url, options) {
if (typeof url === 'string' && url.includes('video')) {
console.log('Fetch-запрос видеоданных:', url);
}
return originalFetch(url, options).then(response => {
if (response.headers.get('Content-Type')?.includes('video')) {
response.clone().arrayBuffer().then(data => {
console.log('Данные видео через fetch:', data);
});
}
return response;
});
};
Переопределяем методы DOM (getElementById, querySelector и др.), чтобы возвращать прокси.
Проксируем элемент <video>, перехватывая:
Установку src/srcObject.
Добавление обработчиков событий.
Перехватываем Ajax-запросы, чтобы обнаружить загрузку видео.
Анализируем события (progress, loadeddata), чтобы получить момент передачи данных.
6. Ограничения и обходные пути
Если скрипты используют ShadowDOM — перехват усложняется (нужно лезть внутрь shadowRoot).
Минифицированный/обфусцированный код — может мешать анализу, но структура событий обычно сохраняется.
CORS и защита данных — если видео загружается с другого домена, могут быть ограничения.
Ваш подход с проксированием DOM и событий — один из самых надёжных способов перехватить динамически загружаемые видеоданные, особенно когда:
Итоговый алгоритм:
Это даст вам контроль над процессом загрузки видео и доступ к сырым данным до их воспроизведения.
Я на всякий случай уточню, существует ли вариант как то ещё получить ссылку на DOM-элемент кроме как использование querySelector и getElementsByTagName, я например знаю что есть ещё getElementById, хотя в данном случаи его можно не рассматривать так тег video про который идёт речь не имеет атрибута id. Может быть существуют ещё способы получить элемент? Например может быть есть вариант через получение потомков document или другого DOM-элемента а затем перебор их в цикле и поиск в нужного с рекурсией по поиску внутри элементов цикла. Возможно ещё какие то варианты.
Да, существует множество способов получить DOM-элементы, помимо стандартных querySelector, getElementsByTagName и getElementById. Некоторые из них могут быть полезны в вашем случае, особенно если скрипты используют неочевидные методы доступа к <video>. Рассмотрим все возможные варианты.
1. Основные методы доступа к DOM-элементам
1.1. Поиск по ID (getElementById)
document.getElementById('myVideo'); // Если есть id
В вашем случае не подходит, так как <video> не имеет id.
1.2. Поиск по тегу (getElementsByTagName)
document.getElementsByTagName('video'); // Все <video> на странице
1.3. Поиск по CSS-селектору (querySelector, querySelectorAll)
document.querySelector('video'); // Первый <video>
document.querySelectorAll('video'); // Все <video>
2. Альтернативные методы доступа к элементам
2.1. Поиск через document.all (устаревший, но работает)
const allElements = document.all; // Все элементы страницы
const videos = Array.from(allElements).filter(el => el.tagName === 'VIDEO');
Минусы:
2.2. Поиск через document.getElementsByClassName
Если у <video> есть класс:
document.getElementsByClassName('video-player'); // Все элементы с классом
Если классы одинаковые у нескольких <video>, можно отфильтровать.
2.3. Поиск через document.evaluate (XPath)
Позволяет искать элементы с помощью XPath-запросов:
const result = document.evaluate(
'//video', // XPath-запрос (все <video> в DOM)
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
const videos = [];
for (let i = 0; i < result.snapshotLength; i++) {
videos.push(result.snapshotItem(i));
}
Плюсы:
Минусы:
Синтаксис XPath сложнее CSS-селекторов.
Если скрипты используют нестандартные методы, можно вручную перебрать всю DOM-структуру:
3.1. Рекурсивный обход childNodes
function findAllVideos(element) {
const videos = [];
if (element.tagName === 'VIDEO') {
videos.push(element);
}
for (const child of element.childNodes) {
videos.push(...findAllVideos(child));
}
return videos;
}
const allVideos = findAllVideos(document.documentElement);
Плюсы:
Находит даже <video> внутри ShadowDOM (если добавить проверку element.shadowRoot).
Минусы:
Медленнее, чем querySelectorAll.
3.2. Обход через TreeWalker
Более эффективный способ перебора DOM:
const treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
{ acceptNode: (node) => node.tagName === 'VIDEO' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP }
);
const videos = [];
while (treeWalker.nextNode()) {
videos.push(treeWalker.currentNode);
}
Плюсы:
Если <video> находится внутри ShadowDOM (например, в веб-компоненте), стандартные методы не сработают. Нужно лезть внутрь shadowRoot:
4.1. Рекурсивный поиск в ShadowDOM
function findVideosInShadowDOM(root) {
const videos = Array.from(root.querySelectorAll('video'));
for (const element of root.querySelectorAll('*')) {
if (element.shadowRoot) {
videos.push(...findVideosInShadowDOM(element.shadowRoot));
}
}
return videos;
}
const allVideos = findVideosInShadowDOM(document);
4.2. Пример: Поиск <video> внутри YouTube-плеера
Многие плееры (например, YouTube) используют ShadowDOM:
const youtubePlayer = document.querySelector('youtube-player');
if (youtubePlayer?.shadowRoot) {
const video = youtubePlayer.shadowRoot.querySelector('video');
}
Иногда разработчики используют неочевидные способы доступа к элементам:
5.1. Доступ через window.frames (если <video> в <iframe>)
const iframe = window.frames[0].document;
const video = iframe.querySelector('video');
5.2. Поиск через document.adoptNode (редко)
const orphanVideo = document.createElement('video');
document.adoptNode(orphanVideo); // Делает элемент частью DOM
5.3. Доступ через document.currentScript (если <video> создаётся скриптом)
const script = document.currentScript; // Текущий выполняемый скрипт
const video = script.previousElementSibling; // Если <video> перед скриптом
Если видео добавляется через JavaScript после загрузки страницы, можно перехватить методы DOM-модификации:
7.1. Переопределение appendChild
const originalAppend = Node.prototype.appendChild;
Node.prototype.appendChild = function(element) {
if (element.tagName === 'VIDEO') {
console.log('Обнаружен динамически добавленный <video>', element);
}
return originalAppend.call(this, element);
};
7.2. Перехват insertBefore
const originalInsert = Node.prototype.insertBefore;
Node.prototype.insertBefore = function(newNode, refNode) {
if (newNode.tagName === 'VIDEO') {
console.log('Обнаружен <video>, вставленный через insertBefore', newNode);
}
return originalInsert.call(this, newNode, refNode);
};
Если <video> не имеет id, можно использовать:
Перехват динамического добавления (мониторинг appendChild, insertBefore).
Оптимальный вариант:
// Быстрый поиск всех <video> (включая ShadowDOM)
function findAllVideos() {
const videos = Array.from(document.querySelectorAll('video'));
document.querySelectorAll('*').forEach(el => {
if (el.shadowRoot) {
videos.push(...el.shadowRoot.querySelectorAll('video'));
}
});
return videos;
}
const allVideos = findAllVideos();
console.log(allVideos); // Все <video> на странице
Это даст вам максимальный охват, даже если видео спрятано глубоко в DOM.
Вы так же можете прочитать следующие статьи: