0

For a personal use, I needed to chapter a video online and I found this : http://html5videoguide.net/demos/google_io/3_navigation/#videoBox

That's perfect because that's exactly what I needed. It works perfectly on a desktop device, but not on mobile (the exact link and the way I use it too).

After debugging, the issue seems to come from the navigation.js file which use a XMLHttpRequest to parse the .vtt file to read the different chapters (I have the alert 'Unable to retrieve file.' on my iPhone). XMLHttpRequest seems to be deprecated but essential to use in my case.

I am getting

"[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience." on this line :

xhr = new XMLHttpRequest(); if (xhr != null) { xhr.open("GET", url, false /* sync */);

I only have a few skills in JS, I understand the purpose of the script and it's functions, but I don't have enough knowledge to make the script compatible on mobile. I think that only the retrieve function needs to be rewrite, not sure...

Any help ?

Thanks a lot !

Full navigation.js script :

var video, track;
var cues = [];
var xhr;

// get video and track elements
video = document.getElementsByTagName('video')[0];
track = video.querySelectorAll('track')[0];

// display chapters once video is loaded
video.addEventListener("loadedmetadata", init, false);
if (video.readyState >= video.HAVE_METADATA) {
    init.apply(video); // missed the event
}

// display chapters
function init(evt) {
    // grab chapters out of <track>
    retrieve(track.getAttribute('src'));
    // display chapters in list and transport bar
    //displayChapters();
}

// seek chapters
function seekChapter(chapter) {
    if (chapter) {
        video.currentTime = parseFloat(cues[chapter].start);
    }       
}

// retrieve chapters via xhr and process them
function retrieve(url) {
    xhr = new XMLHttpRequest();
    if (xhr != null) {
        xhr.open("GET", url, true /* sync */);
        xhr.setRequestHeader('Content-Type', 'text/text; charset=utf-8');
        
        xhr.onreadystatechange = function() {
          if (xhr.readyState == 4 /* complete */) {
            if (xhr.status != 200) {
                alert('Unable to retrieve file.');
            } else {
                cues = parseWebVTT(xhr.responseText);
            }
          }
        }
        xhr.send();
    } else {
        alert('Error retrieving file.');
    }
}

// Function to parse webvtt file
function parseWebVTT(data) {
    var srt;
    // check WEBVTT identifier
    if (data.substring(0,6) != "WEBVTT") {
        alert("Missing WEBVTT header: Not a WebVTT file - trying SRT.");
        srt = data;
    } else {
        // remove WEBVTT identifier line
        srt = data.split('\n').slice(1).join('\n');
    }

    // clean up string a bit
    srt = srt.replace(/\r+/g, ''); // remove dos newlines
    srt = srt.replace(/^\s+|\s+$/g, ''); // trim white space start and end

    //    srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, ''); // remove all html tags for security reasons

    // parse cues
    var cuelist = srt.split('\n\n');
    for (i = 0; i < cuelist.length; i++) {
        var cue = cuelist[i];
        var content = "", start, end, id = "";
        var s = cue.split(/\n/);
        var t = 0;
        // is there a cue identifier present?
        if (!s[t].match(/(\d+):(\d+):(\d+)/)) {
            // cue identifier present
            id = s[0];
            t = 1;
        }
        // is the next line the time string
        if (!s[t].match(/(\d+):(\d+):(\d+)/)) {
            // file format error: next cue
            continue;
        }
        // parse time string
        var m = s[t].match(/(\d+):(\d+):(\d+)(?:.(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:.(\d+))?/);
        if (m) {
            start =
            (parseInt(m[1], 10) * 60 * 60) +
            (parseInt(m[2], 10) * 60) +
            (parseInt(m[3], 10)) +
            (parseInt(m[4], 10) / 1000);
            end =
            (parseInt(m[5], 10) * 60 * 60) +
            (parseInt(m[6], 10) * 60) +
            (parseInt(m[7], 10)) +
            (parseInt(m[8], 10) / 1000);
        } else {
            // Unrecognized timestring: next cue
            continue;
        }

        // concatenate text lines to html text
        content = s.slice(t+1).join("<br>");

        // add parsed cue
        cues.push({id: id, start: start, end: end, content: content});
    }
    displayChapters();
}

// display chapter list on screen
function displayChapters() {
    // create navigation list on right
    var chapters = document.getElementById('chapters');
    for (i=0; i < cues.length; i++) {
        var item = document.createElement('li');
        item.id = cues[i].id;
        var link = document.createElement('a');
        link.href = '#videoBox';
        link.innerHTML = cues[i].id + ': ' + cues[i].content;
        link.setAttribute('data-chapter', i);
        link.onclick = function () {
            seekChapter(this.getAttribute('data-chapter'));
        }
        item.appendChild(link);
        chapters.appendChild(item);
    }
}```
Greg-J
  • 1
  • 6
  • That's not what the console said... : "[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience." and it send to this line : xhr = new XMLHttpRequest(); if (xhr != null) { xhr.open("GET", url, false /* sync */); – Greg-J Oct 05 '20 at 10:27
  • You can test with the link I sent. It works perfectly on desktop, but no chapters on mobile... – Greg-J Oct 05 '20 at 10:28
  • 1
    `Synchronous` is deprecated - you need `Asynchronous ` so **remove** or change `false` to `true` – mplungjan Oct 05 '20 at 10:40
  • I still have the issue with true on mobile, and it doesn't work on desktop anymore :( – Greg-J Oct 05 '20 at 11:00
  • 1
    You need to move `displayChapters();`and `paintChapterbar();` from `function init(evt) { // display chapters in list and transport bar displayChapters(); paintChapterbar(); }` to end of parseWebVTT – mplungjan Oct 05 '20 at 11:04
  • It doesn't work on all device. As I don't want the bar for my personal use, my full script modified on top – Greg-J Oct 05 '20 at 11:17
  • 1
    You cannot do `return cues;` before you execute displayChapters(). Just remove `return cues;` – mplungjan Oct 05 '20 at 11:29
  • 1
    It works perfectly ! Thanls a lot @mplungjan !!! – Greg-J Oct 05 '20 at 11:37
  • You are welcome – mplungjan Oct 05 '20 at 11:45
  • There is still an issue :( I don't have the JS error, but the link doesn't work anymore. When I click on a link, it's supposed to go to te exact chapter in the video, but nothing happen :( – Greg-J Oct 05 '20 at 13:31
  • I have this JS error : '''Cannot read property '38' of undefined at seekChapter (navigation.js:26).''' As I don't return cues in parseWebVTT, I think the link doesn't work... – Greg-J Oct 05 '20 at 13:34
  • I would not know what that meant unless I saw line 26 and the file – mplungjan Oct 05 '20 at 13:39
  • It's ok, I just add "return cues;" after displayChapters and it's ok now !! Thanks again ! – Greg-J Oct 05 '20 at 13:39
  • Better is to change `cues = parseWebVTT(xhr.responseText);` to `parseWebVTT(xhr.responseText);` since cues is a global var – mplungjan Oct 05 '20 at 13:40

0 Answers0