Script para fazer o download de videoaulas no Estratégia

EDIT: AGORA ELE BAIXA TODOS OS VÍDEOS, MESMO QUE UMA AULA POSSUA MAIS DE UM VÍDEO.

Sigam as mesmas instruções contidas nesse post: Script para fazer o download de PDFs no Estratégia

Mas no 5º passo em vez de colarem o código do post, colem o seguinte código abaixo. Ele fará o download de todas as videoaulas contidas em uma determinada disciplina ( todas as videoaulas de ex. Direito Administrativo). Isso é útil para quem quer assisti-las em um momento que não possui Internet.

(function() {
    // Função para verificar se a aula está aberta
    function checkIfLessonOpened(lessonElement) {
        return lessonElement.classList.contains('isOpened');
    }

    // Função para abrir a aula se ela não estiver aberta
    function openLesson(lessonElement) {
        return new Promise((resolve) => {
            const lessonHeader = lessonElement.querySelector('.Collapse-header');
            if (!lessonHeader) {
                console.log("Erro: Cabeçalho da aula não encontrado.");
                resolve();
                return;
            }

            // Se a aula não estiver aberta, clica para abrir
            if (!checkIfLessonOpened(lessonElement)) {
                lessonHeader.click();
                console.log("Aula aberta.");
            } else {
                console.log("Aula já está aberta.");
            }

            // Aguarda 3 segundos para a aula abrir completamente
            setTimeout(resolve, 3000);
        });
    }

    // Função para processar vídeos de uma aula
    async function processLessonVideos(lessonElement, lessonName) {
        // Seleciona todos os botões de vídeo dentro da aula
        const videoButtonsNodeList = lessonElement.querySelectorAll('.ListVideos-items-video');
        const videoButtons = Array.from(videoButtonsNodeList); // Cria uma cópia estática

        if (videoButtons.length === 0) {
            console.log(`Nenhum vídeo encontrado na aula "${lessonName}".`);
            return [];
        }

        const videoLinks = [];

        // Itera sobre cada botão de vídeo
        for (let index = 0; index < videoButtons.length; index++) {
            const videoButton = videoButtons[index];
            const videoIndexSpan = videoButton.querySelector('.VideoItem-info-index');
            const videoIndexText = videoIndexSpan ? videoIndexSpan.textContent.trim() : `Vídeo ${index + 1}`;

            console.log(`\nProcessando ${videoIndexText} da aula "${lessonName}"...`);

            // Verifica se é o primeiro vídeo
            if (index === 0) {
                // Para o primeiro vídeo, que já está selecionado
                const videoUrl = getVideoSrc(lessonElement);
                if (videoUrl) {
                    videoLinks.push(videoUrl);
                    await downloadVideo(videoUrl, lessonName, videoIndexText);
                } else {
                    console.log(`Erro: URL do ${videoIndexText} não encontrada.`);
                }
                continue; // Passa para o próximo vídeo
            }

            // Para vídeos além do primeiro
            // Verifica se o botão já está selecionado
            const isSelected = videoButton.querySelector('a').classList.contains('isSelected');

            let previousSrc = getVideoSrc(lessonElement);
            if (!previousSrc) {
                console.log(`Erro: Não foi possível obter o src do vídeo atual na aula "${lessonName}".`);
                continue;
            }

            if (!isSelected) {
                // Clica no botão para selecionar o vídeo usando dispatchEvent para simular um clique mais robusto
                const anchor = videoButton.querySelector('a');
                if (anchor) {
                    anchor.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
                    console.log(`Selecionado ${videoIndexText}.`);
                } else {
                    console.log(`Erro: Elemento <a> para ${videoIndexText} não encontrado.`);
                    continue;
                }

                // Aguarda até que o src do vídeo seja atualizado usando polling
                const videoUrl = await waitForVideoSrcChange(lessonElement, previousSrc, 15000, 500);
                if (videoUrl) {
                    videoLinks.push(videoUrl);
                    await downloadVideo(videoUrl, lessonName, videoIndexText);
                } else {
                    console.log(`Erro: URL do ${videoIndexText} não encontrada após a seleção.`);
                }
            } else {
                console.log(`${videoIndexText} já está selecionado.`);

                // Mesmo que já esteja selecionado, obter o src para download
                const videoUrl = getVideoSrc(lessonElement);
                if (videoUrl && videoUrl !== previousSrc) {
                    videoLinks.push(videoUrl);
                    await downloadVideo(videoUrl, lessonName, videoIndexText);
                } else {
                    console.log(`Erro: URL do ${videoIndexText} não encontrada ou não atualizada.`);
                }
            }
        }

        return videoLinks;
    }

    // Função para aguardar até que o src do vídeo seja atualizado usando polling
    function waitForVideoSrcChange(lessonElement, oldSrc, timeout = 15000, interval = 500) {
        return new Promise((resolve) => {
            const startTime = Date.now();

            const checkSrc = () => {
                const currentSrc = getVideoSrc(lessonElement);
                if (currentSrc && currentSrc !== oldSrc) {
                    console.log(`Detectado novo src: ${currentSrc}`);
                    resolve(currentSrc);
                } else {
                    if (Date.now() - startTime >= timeout) {
                        console.log(`Timeout: src do vídeo não mudou após ${timeout}ms.`);
                        resolve(null);
                    } else {
                        setTimeout(checkSrc, interval);
                    }
                }
            };

            checkSrc();
        });
    }

    // Função para obter o elemento de vídeo
    function getVideoElement(lessonElement) {
        return lessonElement.querySelector('video.video-react-video[src^="https://www.estrategiaconcursos.com.br/storage/video/"]');
    }

    // Função para obter o src do vídeo
    function getVideoSrc(lessonElement) {
        const videoElement = getVideoElement(lessonElement);
        return videoElement ? videoElement.src : null;
    }

    // Função para baixar o vídeo
    async function downloadVideo(videoUrl, lessonName, videoIndexText) {
        return new Promise((resolve) => {
            // Cria um link temporário para simular o clique de download
            const link = document.createElement('a');
            link.href = videoUrl;

            // Define o nome sugerido para o download
            const fileName = `${lessonName} - ${videoIndexText}.mp4`;
            link.download = fileName;

            // Configura para abrir em nova aba
            link.target = '_blank';

            // Adiciona o link ao corpo do documento
            document.body.appendChild(link);

            // Simula o clique no link
            link.click();

            // Remove o link após o clique
            document.body.removeChild(link);

            console.log(`${videoIndexText} baixado como: ${fileName}`);
            resolve();
        });
    }

    // Função principal para processar todas as aulas uma por uma
    async function processLessons() {
        const lessons = document.querySelectorAll('.LessonList-item');

        if (lessons.length === 0) {
            console.log("Nenhuma aula encontrada na página.");
            return;
        }

        let lessonsList = [];

        // Itera sobre cada aula
        for (let i = 0; i < lessons.length; i++) {
            const lesson = lessons[i];
            const lessonNameElement = lesson.querySelector('.SectionTitle-no-margin'); // Obtém o nome da aula
            const lessonName = lessonNameElement ? lessonNameElement.textContent.trim() : `Aula ${i}`; // Nome da aula, ex: "Aula 00"

            console.log(`\nProcessando ${lessonName}...`);

            // Abre a aula se necessário
            await openLesson(lesson);

            // Espera a aula abrir completamente antes de acessar os vídeos
            await new Promise(resolve => setTimeout(resolve, 3000)); // Espera 3 segundos para a aula abrir

            // Agora processa os vídeos da aula
            const videoLinks = await processLessonVideos(lesson, lessonName);

            // Adiciona os links dos vídeos à lista de aulas
            lessonsList.push({
                lessonName,
                videoLinks
            });

            // **Removido: Verificação de downloads concluídos**
            // await waitForDownloads(videoLinks);

            // **Apenas uma breve espera antes de passar para a próxima aula**
            await new Promise(resolve => setTimeout(resolve, 1000)); // Espera 1 segundo entre as aulas
        }

        // Exibe a lista de aulas e vídeos identificados
        console.log("\nLista de Aulas e Vídeos Identificados:");
        lessonsList.forEach((lesson, index) => {
            console.log(`${index + 1}. ${lesson.lessonName}:`);
            lesson.videoLinks.forEach((url, i) => {
                console.log(`   ${i + 1}. ${url}`);
            });
        });

        console.log("Processamento das aulas concluído!");
    }

    // Chama a função principal
    processLessons();
})();

Obs. 1: que não sou programador e quem criou o código foi o Chat GPT (tive o trabalho de ir tentando prompts corretos até conseguir o que queria), então provavelmente é imperfeito. Quaisquer erros que encontrarem postem aqui explicando que tentarei corrigir, se possível.

Obs. 2: Nosso caro amigo AffectionateCake4830 já melhorou o código mencionado no início do post, como descrito a seguir: Script para fazer o download de PDFs no Estratégia - versão 2.0 então acessem esse tutorial de preferência para baixarem os PDFs.

Obs. 3: Estou tentando fazer com que o código baixe também vídeos em alta qualidade (720) dependendo da escolha do usuário. Por enquanto o código baixa apenas vídeos em 360. Quando (se) eu conseguir fazer isso atualizo o post. Espalhem para que todos possam estudar melhor, por um país melhor.