Skip to content

Instantly share code, notes, and snippets.

@EstebanFuentealba
Last active March 19, 2022 17:57
Show Gist options
  • Save EstebanFuentealba/10115391d1533476c456aed976124639 to your computer and use it in GitHub Desktop.
Save EstebanFuentealba/10115391d1533476c456aed976124639 to your computer and use it in GitHub Desktop.

Intro

Script para descargar y desencriptar videos de la plataforma Platzi

Requerimientos

  • Node.js
  • ffmpeg

Descargar Segmentos

Entrar por el navegador a un curso, abre las "Herramienats de desarrollador", pega el siguiente código y te descargará un zip con todos los segmentos del video

let s0 = document.createElement("script");
s0.onload = () => {
    console.log('[Status]: Inject fflate')
  let url = document.location.href.split("/");
  let filename = `${url[url.length - 2]}.zip`;
  let course = url[url.length - 3];
  var toZip = {
    [course]: {
        [filename]: {}
    },
  };
  console.log({ course, filename, toZip });

  let s1 = document.createElement("script");
  s1.onload = () => {
    console.log('[Status]: Inject m3u8 parser')
    const { zipSync, strToU8 } = fflate;

    console.log('[Status]: Starting...')
    fetch(location.href)
      .then((response) => response.text())
      .then((response) => {
        let m3u8Url = response.match(/"hls":"(.[^}]+)"}/)[1];
        console.log('[Status]: Download m3u8...')
        fetch(m3u8Url)
          .then((response) => response.text())
          .then((EXTM3U) => {
            let splited = EXTM3U.split("\n");
            let bestQualityUrl = splited[splited.length - 1];
            console.log('[Status]: Download best quality...')
            fetch(bestQualityUrl)
              .then((response) => response.text())
              .then((m3u8) => {
                var parser = new m3u8Parser.Parser();
                parser.push(m3u8);
                parser.end();

                console.log('[Status]: Download key encription...')
                fetch(parser.manifest.segments[0].key.uri)
                  .then((response) => response.arrayBuffer())
                  .then(async (key) => {
                    try {
                      
                      toZip[course][filename]["enc.key"] = new Uint8Array(await key);
                      console.log('[Status]: Download segments...')

                      await parser.manifest.segments.reduce(
                        (acc, segment, index) => {
                          return acc.then(() => {
                            console.log(`[Status]: Downloding ${index+1} of ${parser.manifest.segments.length}`)
                            return fetch(segment.uri)
                              .then((response) => response.arrayBuffer())
                              .then(async (ab) => {
                                toZip[course][filename][`seg-${index}.ts.enc`] =
                                  new Uint8Array(await ab);
                                return ab;
                              });
                          });
                        },
                        Promise.resolve()
                      );
                      console.log(`[Status]: Creating new m3u8 file`)
                      let parsedM3u8 = m3u8;
                      parsedM3u8 = parsedM3u8.replace(
                        parser.manifest.segments[0].key.uri,
                        "enc.key"
                      );

                      parser.manifest.segments = parser.manifest.segments.map(
                        (seg, index) => {
                          parsedM3u8 = parsedM3u8.replace(
                            seg.uri,
                            `seg-${index}.ts.enc`
                          );
                          return seg;
                        }
                      );
                      toZip[course][filename][`lista.m3u8`] = strToU8(parsedM3u8);
                      console.log('[Status]: Creating Zip File...')
                      var zipped = zipSync(toZip, {
                        level: 9,
                      });
                     
                      var blob = new Blob([zipped.buffer], {
                        type: "application/zip",
                      });
                      blob.lastModifiedDate = new Date();
                      blob.name = filename;

                      let a = document.createElement("a");
                      a.href = URL.createObjectURL(blob);
                      a.download = filename;
                      a.click();
                      console.log(`[Status]: Finished`)
                    } catch (error) {
                      console.error(error);
                    }
                  });
              });
          });
      });
  };
  s1.src = "https://cdn.jsdelivr.net/npm/m3u8-parser@4.7.0/dist/m3u8-parser.js";
  document.body.appendChild(s1);
};
s0.src = "https://cdn.jsdelivr.net/npm/fflate/umd/index.js";
document.body.appendChild(s0);

Desencriptar segmentos

Una vez decargado el archivo XXXX.zip descomprime y entra el archivo lista.m3u8y los segmentos. Corre un servidor local con el siguiente comando

npx static-server 

por lo general te genera un sitio estatico en http://localhost:9080/

en una terminal ejecutar

ffmpeg -i http://localhost:9080/lista.m3u8 -c copy output.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment