arte stream and download

get arte stream url with just one click (or none at all) or download seperate audio/video mp4

安裝腳本?
作者推薦腳本

您可能也會喜歡 arte stop nagging me

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         arte stream and download
// @namespace    http://tampermonkey.net/
// @version      3.0.1
// @description  get arte stream url with just one click (or none at all) or download seperate audio/video mp4
// @author       mihau
// @match        https://www.arte.tv/de/videos/*
// @match        https://www.arte.tv/fr/videos/*
// @match        https://www.arte.tv/*/videos/*
// @match        https://www.arte.tv/*/live*
// @license MIT
// @supportURL   https://greasyfork.org/en/scripts/533451-arte-stream-and-download
// ==/UserScript==

// if you want to get the url in the very moment the page loads, you may change this from 0 to 1:
var loadonload = 0;
// best not edit below this line

// from https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists
function waitforit(selector) {
  return new Promise(resolve => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }

    var observer = new MutationObserver(mutations => {
      if (document.querySelector(selector)) {
        observer.disconnect();
        resolve(document.querySelector(selector));
      }
    });

    // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
    observer.observe(document.body, { childList: true, subtree: true });
  });
}

waitforit('video').then((elm) => {

    $   = function(_) {return document.getElementById(_)}
    $tn = function(_) {return document.getElementsByTagName(_)}
    $cn = function(_) {return document.getElementsByClassName(_)}
    $qa = function(_) {return document.querySelectorAll(_)}

  var pathpart = window.location.pathname.split("/"),
    lang = pathpart[1],
    id = pathpart[3],
    name = pathpart[4],
    jsoncontainer,
    nix = "",
    url = "",
    filmid = "",
    cuescriptid = 0,
    liveid = "",
    archid = "",
    mode = "",
    kind = "",
    livelabel = "",
    chapterformat = "",
    trailer = "",
    loc_stream = "stream url",
    loc_video = "video",
    loc_audio = "audio",
    loc_subs = "subtitles",
    loc_set = "setlist",
    loc_cue = "cue file",
    loc_txt = "plain text",
    loc_dl = "download",
    loc_nodl = "(download unvailable)";

  var trash = ["- Regarder le documentaire complet", " - Die ganze Doku", " - Komplette Sendung", " - Programm in voller Länge", " - Film in voller Länge", " - Regarder le film complet", " - Regarder l’émission complète"];

  if (($qa('[data-testid="AUSSCHNITT"]')[0]) || ($qa('[data-testid="EXTRAIT"]')[0])) {
    trailer = "TRAILER";
    var desc = '[data-testid="pc_desc-Heading"]';
    var warn = $qa(desc)[0].innerText;
    var nraw = '<span style="color: red">' + trailer + ':</span> ';

    $qa(desc)[0].innerHTML = nraw + warn;
    var newtitle = document.title;
    trash.forEach((item) => {
      newtitle = newtitle.replace(item, "")
    });
    document.title = trailer + ": " + newtitle;
  }


  if (/ARTE Concert/.test(document.title)) {
    kind = "concert";
  }

  if (lang == "fr") {
    loc_subs = "sous-titres";
    loc_dl = "téléchargement";
    loc_nodl = "(téléchargement indisponible)";
  }
  if (lang == "de") {
    loc_stream = "Stream URL";
    loc_video = "Video";
    loc_audio = "Audio";
    loc_subs = "Untertitel";
    loc_set = "Setliste";
    loc_cue = "cue-Datei";
    loc_txt = "Textdatei";
    loc_dl = "Download";
    loc_nodl = "(Download nicht verfügbar)";
  }

  function streamurl() {
    if (mode == "live") {
      var test = prompt(livelabel + loc_stream, url);
    } else {
      var test = prompt(loc_stream + " (click 'OK' for ffmpeg command)", url);
      if (test !== null) {
        prompt("ffmpeg command ('OK' for ffmpeg AUDIO-ONLY command)", 'ffmpeg -referer "' + location.href + '" -user_agent "' + window.navigator.userAgent + '" -i "' + url + '" -c copy -bsf:a aac_adtstoasc "' + filmtitle + '.mp4"');
        if (test !== null) {
          prompt("ffmpeg AUDIO-ONLY command ('OK' for yt-dlp command)", 'ffmpeg -referer "' + location.href + '" -user_agent "' + window.navigator.userAgent + '" -i "' + url + '" -vn -c:a copy "' + filmtitle + '-audio.m4a"');
          if (test !== null) {
            prompt("yt-dlp command", "yt-dlp '" + url + "'");
          }
        }
      }
    }
  }

  function parsescripts(nextfid) {

    if (nextfid != -1) {
      nix = self.__next_f[nextfid].toString();
    }

    if (mode == "live") {

      var mynewregex = new RegExp("m3u8.*", "gi");
      url = nix.match("https://artesimulcast\.akamaized\.net.*m3u8")[0];
      url = url.replace(mynewregex, "m3u8")

      if (lang == "de") {
        url = url.replace("artelive_fr", "artelive_de")
      } else {
        url = url.replace("artelive_de", "artelive_fr")
      }

    } else {

      var myregex = /Generate\/.*?\/\//;
      var match = nix.match(myregex);
      var mynewregex = new RegExp("\/.*", "gi")
      filmid = match[0].replace("Generate/", "").replace(mynewregex, "");
      url = "https://manifest-arte.akamaized.net/api/manifest/v1/Generate/" + filmid + "/" + lang + "/XQ+KS+CHEV1/" + id + ".m3u8";

    }

    return url;

  }

  function setlist() {

    var cue = "REM edit all fields to your liking, especially the filename (FILE ...)\n\n",
      txt = "",
      cuefilename = "",
      cuescript = "",
      schemaObj = "";

    if (chapterformat == "chapter") {
      cuescript = self.__next_f[cuescriptid].toString().slice(22).slice(0, -2);
      schemaObj = JSON.parse(cuescript);
      var artistcreds = new Array();
      artistcreds[0] = schemaObj.apiPlayerConfig.attributes.metadata.title;
      artistcreds[1] = schemaObj.apiPlayerConfig.attributes.metadata.subtitle;
    } else {
      cuescript = self.__next_f[cuescriptid].toString().slice(2);
      schemaObj = JSON.parse(cuescript);
      if (/ - /.test(schemaObj.name)) {
        var artistcreds = schemaObj.name.split(" - ");
      } else {
        var artistcreds = new Array();
        artistcreds[0] = schemaObj.name;
        artistcreds[1] = schemaObj.name;
      }

    }

    cuefilename = artistcreds[0] + "-" + artistcreds[1];
    cuefilename = cuefilename.replace(/[^a-z0-9-.]/gi, '_').toLowerCase();

    cue += 'PERFORMER "' + artistcreds[0] + '"' + "\n";
    cue += 'TITLE "' + artistcreds[1] + '"' + "\n";
    cue += 'FILE "' + cuefilename + '.m4a" M4A' + "\n\n  TRACK 01 AUDIO\n";
    var cueperf = '    PERFORMER "' + artistcreds[0] + '"' + "\n";
    cue += cueperf;
    cue += '    TITLE "Intro/Prologue"' + "\n";
    cue += "    INDEX 01 00:00:00\n";

    txt += "00:00 Intro/Prologue\n";
    var setlistlength = 0;

    if (chapterformat == "chapter") {
      setlistlength = schemaObj.apiPlayerConfig.attributes.chapters.elements.length;
    } else {
      setlistlength = schemaObj.hasPart.length;
    }

    for (var j = 0, k = setlistlength; j < k; ++j) {

      var songtitle = "";
      var timestamp = "";
      if (chapterformat == "chapter") {
        songtitle = schemaObj.apiPlayerConfig.attributes.chapters.elements[j].title;
        timestamp = schemaObj.apiPlayerConfig.attributes.chapters.elements[j].startTime;
      } else {
        songtitle = schemaObj.hasPart[j].name;
        timestamp = schemaObj.hasPart[j].startOffset;
      }

      var padm = "";
      if (timestamp < 600) {
        padm = "0";
      }
      var cuetrackid = j + 2;
      var ctidpad = "";
      if (cuetrackid < 10) {
        ctidpad = "0";
      }
      cue += "\n  TRACK " + ctidpad + cuetrackid + " AUDIO \n";
      cue += cueperf;
      cue += "    TITLE " + '"' + songtitle + '"';
      cue += "\n    INDEX 01 " + padm + fmtMSS(timestamp) + ":00\n";

      txt += padm + fmtMSS(timestamp) + " " + songtitle + "\n";

    }

    cue += "\n";
    window.cue = cue;
    window.txt = txt;

    window.cuefilename = cuefilename;

  }

  // https://stackoverflow.com/questions/3733227/javascript-seconds-to-minutes-and-seconds
  function fmtMSS(s) {
    return (s - (s %= 60)) / 60 + (9 < s ? ':' : ':0') + s;
  }

  // ...

  if (self.__next_f) {

    for (var i = 0, l = self.__next_f.length; i < l; ++i) {

      var it = self.__next_f[i].toString();

      if (kind == "concert") {
        if (/startOffset/.test(it)) {
          cuescriptid = i;
          chapterformat = "offset";
        }
        if (/chapterId/.test(it)) {
          cuescriptid = i;
          chapterformat = "chapter";

        }
      }

      if (/m3u8/.test(it)) {
        if (/artelive/.test(it)) {

          liveid = i;
          mode = "live";
          parsescripts(i);

        } else if (/Generate/.test(it)) {

          archid = i;
          mode = "archive";

          var thestring = self.__next_f[archid].toString().slice(22, -1).slice(0, -1);
          if (/Generate\//.test(thestring)) {
            var schemaObj = JSON.parse(thestring);
            filmid = "notnull";
            url = schemaObj.apiPlayerConfig.attributes.streams[0].url;

          } else {

            parsescripts(i);

          }

        }

      }

    }

  } else {

    for (var i = 0, l = $tn("script").length; i < l; ++i) {

      if ($tn("script")[i].innerText.indexOf("Generate/") != -1) {
        jsoncontainer = i;
        break;
      }

    }

    nix = $tn("script")[jsoncontainer].innerText;
    parsescripts(-1);

  }

  var download_url = "";
  if ((url != "") && (url != null) && (url != "null")) {
    download_url = url;
  } else {
    download_url = "https://api.arte.tv/api/player/v2/config/" + lang + "/" + id;
  }

  var xhr = new XMLHttpRequest();

  if (loadonload != 0) {
    if ((filmid != null) && (filmid != "null") && (filmid != "") && (filmid != "undefined") && (filmid != undefined) && (filmid != NaN)) {
      streamurl();
    }
  }

  var filmtitle = document.querySelector('meta[property="og:title"]').content;
  filmtitle = filmtitle.replace(" | ARTE Concert", "").replace(" | ARTE", "");
  trash.forEach((item) => {
    filmtitle = filmtitle.replace(item, "")
  });
  filmtitle = filmtitle.replace(/ /g, "_").replace(/[^a-z0-9 \.,_-]/gim, "").replace("_-_", "-");

  if (mode == "live") {
    livelabel = "live ";
  }
  if (lang == "de") {
    if (mode == "live") {
      livelabel = "Live ";
    }
  }

  var aclass = "ds-1nr7pfe";
  var spanclass = "ds-92m6hs";

  var topelementstream = document.createElement("a");
  topelementstream.setAttribute('id', 'arteuserjsstream');
  topelementstream.setAttribute('class', aclass);

  var innerelementstream = document.createElement("span");
  innerelementstream.setAttribute('id', 'streamurl');
  innerelementstream.setAttribute('class', spanclass);
  innerelementstream.setAttribute('style', 'color: #FA481C');
  innerelementstream.innerText = livelabel + loc_stream;

  topelementstream.appendChild(innerelementstream);

  if (!($("arteuserjsstream"))) {
    if ($cn('ds-1r0jukn')[0]) {
      $cn('ds-1r0jukn')[0].insertBefore(topelementstream, null);
    } else if ($cn('ds-1rm5mah')[0]) {
      $cn('ds-1rm5mah')[0].insertBefore(topelementstream, null);
    }
  }

  if (mode == "archive") {

    var topelementdl = document.createElement("a");
    topelementdl.setAttribute('id', 'arteuserjsdl');
    topelementdl.setAttribute('class', 'ds-1nr7pfe');

    var innerelementdl = document.createElement("span");
    innerelementdl.setAttribute('id', 'arteuserjsdlinner');
    innerelementdl.setAttribute('class', 'ds-11ckmbs');
    innerelementdl.innerText = "";

    topelementdl.appendChild(innerelementdl);

    if (!($("topelementdl"))) {
      if ($cn('ds-1r0jukn')[0]) {
        $cn('ds-1r0jukn')[0].insertBefore(topelementdl, null);
      } else if ($cn('ds-1rm5mah')[0]) {
        $cn('ds-1rm5mah')[0].insertBefore(topelementdl, null);
      }
    }

    var getJSON = function(url, callback) {

      xhr.open('GET', url, true);
      xhr.responseType = 'json';

      xhr.onload = function() {

        var status = xhr.status;

        if (status == 200) {
          callback(null, xhr.response);
        } else {
          callback(status);
        }
      };

      xhr.send();
    };

    var getM3U = function(url, callback) {

      xhr.open('GET', url, true);
      xhr.responseType = 'text';

      xhr.onload = function() {

        var status = xhr.status;

        if (status == 200) {
          callback(null, xhr.response);
        } else {
          callback(status);
        }
      };

      xhr.send();
    };


    if ((filmid != null) && (filmid != "null") && (filmid != "") && (filmid != "undefined") && (filmid != undefined) && (filmid != NaN)) {
      $("streamurl").onclick = function() {
        streamurl()
      }
    } else {
      getJSON(download_url, function(err, data) {
        if (err != null) {
          console.error(err);
        } else {
          url = data.data.attributes.streams[0].url;
        }
      });

    }

    getM3U(url, function(err, data) {
      if (err != null) {
        console.error(err);
      } else {

        getM3U = function() {};

        var mp4avail = 1,
          fhdavail = 0,
          videosarr = new Array(),
          audiosarr = new Array(),
          subtisarr = new Array(),
          videosarrng = new Array(),
          vid = 0,
          aud = 0,
          sub = 0,
          vidng = 0,
          videolinksng = "",
          result, videolinks, audiolinks, subtilinks = "",
          vregex = new RegExp(".*_v", "gi"),
          videoformats = /-([A-Z])_v/,
          uregex = /URI=(["'])(.*?)\1/,
          nregex = /NAME=(["'])(.*?)\1/;

        var lines = data.split(/[\r\n]/);

        for (var i in lines) {

          var line = lines[i];

          if (!(/iframe/.test(line))) {

            if (!(/h265/.test(line))) {

              if (videoformats.test(line)) {

                if (/aka_me_session/.test(line)) {
                  mp4avail = 0;
                }
                if (/v1080/.test(line)) {
                  fhdavail = 1;
                }

                videosarr[vid] = line;
                vid++;

              }

              if (/TYPE=AUDIO/.test(line)) {

                var audiofile = line.match(uregex)[2];
                var audiolabel = line.match(nregex)[2];

                audiosarr[aud] = audiolabel + "#" + audiofile;
                aud++;

              }

              if (/TYPE=SUBTITLES/.test(line)) {

                var subfile = line.match(uregex)[2];
                var sublabel = line.match(nregex)[2];

                subtisarr[sub] = sublabel + "#" + subfile.replace("m3u8", "vtt");
                sub++;

              }

            } else {
              videosarrng[vidng] = line;
              vidng++
            }

          }

        }

        videosarr = videosarr.sort();
        if (fhdavail == 1) {
          videosarr.push(videosarr.shift());
        }

        videosarr = videosarr.reverse();

        audiosarr = audiosarr.sort();
        subtisarr = subtisarr.sort();

        var bp = "&#x95; ";

        for (var i = 0, l = subtisarr.length; i < l; ++i) {

          var subcom = subtisarr[i].split("#");
          subtilinks += '<option value="' + subcom[1] + '">' + subcom[0].replace("automatische", "erforderlich").replace("Für", "für").replace(" Untertitel", "").replace("forcé", "obligatoire") + '</option>';

        }

        for (var i = 0, l = audiosarr.length; i < l; ++i) {

          var audcom = audiosarr[i].split("#");
          audiolinks += '<option value="' + audcom[1] + '">' + audcom[0].replace(" (Original)", "").replace(" (VO)", "") + '</option>';

        }

        for (var i = 0, l = videosarr.length; i < l; ++i) {

          videolinks += '<option value="' + videosarr[i] + '">' + videosarr[i].replace(".m3u8", "").replace(vregex, "") + 'p</option>';

        }

        if (videosarrng.length > 0) {

          videosarrng = videosarrng.sort();
          videolinks += '<option value="">' + bp + 'h265 (hevc)/mp4 ' + loc_video + ':</option>';

          for (var i = 0, l = videosarrng.length; i < l; ++i) {

            videolinks += '<option value="' + videosarrng[i] + '">' + videosarrng[i].replace(".m3u8", "").replace("_h265", "").replace(vregex, "") + 'p</option>';

          }

        }

        if (mp4avail == 1) {
          trailer = " " + trailer;
          result = '<form name="jump" action=""><select id="dlmenu" style="background-color:black;color:white; width: 40px" class="' + spanclass + '" name="dlmenu" onchange="opennewtab()">';
          result += '<option value="" selected="selected">&#x1f847; ' + loc_dl + trailer + '</option>'; // 🡇
          result += '<option value=""></option>';
          result += '<option value="">' + bp + 'h264 (avc)/mp4 ' + loc_video + ':</option>';
          result += videolinks;
          result += '<option value=""></option>';
          result += '<option value="">' + bp + 'aac/mp4 ' + loc_audio + ':</option>';
          result += audiolinks;
          if (subtisarr.length > 0) {
            result += '<option value=""></option>';
            result += '<option value="">' + bp + 'vtt/txt ' + loc_subs + ':</option>';
            result += subtilinks;
          }
          if ((kind == "concert") && (cuescriptid > 0)) {
            result += '<option value=""></option>';
            result += '<option value="">' + bp + loc_set + ':</option>';
            result += '<option value="cue">' + loc_cue + '</option>';
            result += '<option value="txt">' + loc_txt + '</option>';
          }
          result += '</select></form>';
          result = result.replaceAll(".m3u8", ".mp4").replaceAll("undefined", "");

        } else {
          result = loc_nodl;
        }

        $("arteuserjsdlinner").innerHTML = result;

      }

    });

    var script = document.createElement("script");
    script.innerHTML = `
            function opennewtab() {
              var optionvalue = document.jump.dlmenu.options[document.jump.dlmenu.selectedIndex].value;
                if ((optionvalue == "cue") || (optionvalue == "txt")) {
                  var hiddenElement = document.createElement('a');
                  var format;
                    if (optionvalue == "cue") { format = encodeURIComponent(window.cue) }
                    else { format = encodeURIComponent(window.txt) }
                    hiddenElement.href = 'data:attachment/text,' + format;
                    hiddenElement.download = window.cuefilename + '.' + optionvalue;
                    hiddenElement.click();
                } else if ((document.jump.dlmenu.selectedIndex > 0) && (optionvalue != "") && (optionvalue != "#")) {
                    window.open(optionvalue, "artedltab");
                }

            }
       `;
    document.body.appendChild(script);
  }

  $("streamurl").onclick = function() { streamurl() }
  if ((kind == "concert") && (cuescriptid > 0)) { setlist() }

});