0

Meta: This question is a follow-up, or a variant of a similar question about embedded Youtube videos: How to target "hidden" iframes? (application: links to starting positions of a "poster image hidden"-embedded Youtube video)


Definitions:

A "poster" image

A customized image on which you click, to reveal the actual A/V player.

Locally stored

The A/V is stored on your website, as opposed to a streaming service such as Youtube.

Clickable "positioning" hyperlinks

When a certain hyperlink inside an HTML text, a predefined starting (and perhaps ending) position of the A/V is addressed and automatically played.


I have been reading things on the net about function(){something.currentTime=...;});, but I have not been able to implement this.

Community
  • 1
  • 1
O0123
  • 481
  • 9
  • 28
  • I'll answer it as soon as I get some more free time, see you! – undefined Jan 22 '15 at 17:51
  • @Rou - Thanks, that would be very welcome, I could really use your help. As I think could the world in general, since it is hard to even find 1 source clearly stating the procedure. – O0123 Jan 25 '15 at 12:26
  • Sorry, I've been swamped with work, maybe later today! :) – undefined Jan 25 '15 at 20:04
  • @Rou - I have been happily using your solution over the last couple of weeks. Unfortunately, my webhosting doesn't allow to store these "large" `.mp3-files` for a long amount of time... Cf. [Files (±50MB, audio) automatically repetitively deleted (after ± a couple of hours-days?)](http://drupal.stackexchange.com/questions/145932/files-%C2%B150mb-audio-automatically-repetitively-deleted-after-%C2%B1-a-couple-of-hou?noredirect=1#comment176025_145932). Any good streaming hosting service you know of? If not, I think I will have to convert to a video, and stream them from youtube nevertheless... – O0123 Feb 06 '15 at 09:22
  • I can't seem to do it with [`Google Drive`](https://www.google.com/drive/) nor with [`Dropbox`](https://www.dropbox.com/). They don't seem to offer streaming that works with this solution. Perhaps a solution could be to once more use your other code at [How to target "hidden" iframes? (application: links to starting positions of a "poster image hidden"-embedded Youtube video)](http://stackoverflow.com/questions/28013991/how-to-target-hidden-iframes-application-links-to-starting-positions-of-a)? :) Hmmm ... Very unfortunate these web serving limits, since your code was working so fine. – O0123 Feb 06 '15 at 09:26
  • If you are looking for a video host well, youtube is the way to go, they also have an [**api**](https://developers.google.com/youtube/2.0/developers_guide_protocol_uploading_videos) if you want to upload the videos via scripting :) – undefined Feb 06 '15 at 14:49
  • @Rou - Hi! I'm now referring to `.mp3-files` specifically, but I could of course add some black video to them. But then still, I don't know how to get the Youtube video (or more relevant: audio) to stream via the HTML5 audio player you implemented in the project beow. Thanks for the reply. – O0123 Feb 07 '15 at 09:01
  • Have you tried the [**sound cloud api**](https://developers.soundcloud.com/)? I'm not an expert but It may be useful, again this is not my area of expertise :) Good luck! – undefined Feb 07 '15 at 19:33
  • Glad I could help! See you :) – undefined Feb 07 '15 at 21:24
  • @Rou - Edit: Thanks a lot for the tip. It works like a charm. Your code stays perfectly functional with the download link of an audio-file on [`Soundcloud`](http://soundcloud.com/). :) By similar quick tweaking, I haven't found a solution for `Google Drive`. Nor did I found 1 for `Dropbox` (I thought I had, but figured out this only functioned when logged in). Cheers. – O0123 Feb 09 '15 at 07:42

1 Answers1

1

Demo

I'm sorry for the delay, and for it not being exactly the same as the Youtube one, the thing I lack most it's consistency, but here it is.

I've based this example in two songs from Hozier, one in audio and one in video format. (Hope nobody sues me)

As always you can modify the styles later on to fit your design, I just put something quick together to demonstrate.

Below, you will see a basic example of how the code works (for a more in-depth example, please refer to the demo).

HTML

Audio

<div class="mediaAudioFacade"
     id="id"
     data-sources="source1.mp3,source2.wav,source3.ogg"
     data-start="seconds"(optional)
     data-end="seconds"(optional)>
label
</div>

<div class="mediaJumper" data-id="id" data-time="seconds">label</div>

Video

<div class="mediaVideoFacade" (..)>(..)</div>
(..)

The only difference would be the class attribute, this being mediaVideoFacade instead of mediaAudioFacade.

JavaScript

    window.addEventListener("load",function(){
    setUpMediaObjects();
    setUpMediaJumpers();
});
MediaObjects = [];
MediaJumpers = [];

function setUpMediaObjects() {
    var allAudioFacades = document.querySelectorAll(".mediaAudioFacade");
    if (allAudioFacades) {
        for (var i = 0; i < allAudioFacades.length; i++) {
            var facade = allAudioFacades[i];
            var mo = new MediaObject(facade);
            MediaObjects.push(mo);
        }
    }

    var allVideoFacades = document.querySelectorAll(".mediaVideoFacade");
    if (allVideoFacades) {
        for (var i = 0; i < allVideoFacades.length; i++) {
            var facade = allVideoFacades[i];
            var mo = new MediaObject(facade);
            MediaObjects.push(mo);
        }
    }
}

function setUpMediaJumpers(){
    var allMediaJumpers = document.querySelectorAll(".mediaJumper");
    for( var i = 0 ; i < allMediaJumpers.length ; i ++ ){
        var mediaJumper = allMediaJumpers[i];
        var mj = new MediaJumper(mediaJumper);
        MediaJumpers.push(mj);
    }
}


function MediaObject(facade) {
    this.facade = facade;
    this.id = this.facade.id;
    this.sourcesURI = this.facade.dataset.sources.split(",");
    this.sources = this.getSources();
    var isAudio = this.facade.className.match(/mediaAudioFacade/);
    this.type = (isAudio) ? "audio" : "video";
    this.icon = new Image();
        this.icon.src = (isAudio) ? "http://i.imgur.com/HKktAoE.png" : "http://findicons.com/icon/download/566082/video_play/33/png";
        this.setUpFacade();
    this.capType = this.type.substr(0,1).toUpperCase() + this.type.substr(1);
    this.elem = document.createElement(this.type);
    this.elem.controls = "true";
    this.elem.className = "mediaType".replace(/type/i, this.capType);
    this.hasStarted = false;
    this.appendSources();
    this.startTime = this.facade.dataset.start;
    this.endTime = this.facade.dataset.end;
    this.facade.addEventListener("click", this.startUp.bind(this) );
}

MediaObject.prototype.setUpFacade = function () {
    var label = document.createElement("span");
    label.innerHTML = this.facade.innerHTML || "Play audio.";
    this.facade.innerHTML = "";
    this.facade.appendChild(this.icon);
    this.facade.appendChild(label);
}

MediaObject.prototype.getSources = function () {
    var sources = [];
    for (var i = 0; i < this.sourcesURI.length; i++) {
        var sourceURI = this.sourcesURI[i];
        var source = document.createElement("source");
        source.src = sourceURI;
        sources.push(source);
    }
    return sources;
}

MediaObject.prototype.appendSources = function () {
    for (var i = 0; i < this.sources.length; i++) {
        var source = this.sources[i];
        this.elem.appendChild(source);
    }
}

MediaObject.prototype.startUp = function () {
    this.replaceNode(this.facade, this.elem);
    this.hasStarted = true;
    if( this.startTime )
        this.elem.currentTime = this.startTime;
    if( this.endTime )
        this.elem.addEventListener("timeupdate",this.checkForVideoEnd.bind(this));
    this.elem.play();
}

MediaObject.prototype.checkForVideoEnd = function(){
    console.log(this.elem.currentTime);
    if( Math.floor(this.elem.currentTime) == this.endTime )
        this.elem.pause();
}

MediaObject.prototype.replaceNode = function(node1,node2){
    var parent = node1.parentNode;
    var next = node1.nextSibling;
    if( next )
        parent.insertBefore(node2,next);
    else
        parent.appendChild(node2);
    parent.removeChild(node1);
}

function MediaJumper(jumper){
    this.jumper = jumper;
    this.id = this.jumper.dataset.id;
    this.mediaObject = this.getMediaObject();
    this.time = this.jumper.dataset.time;
    this.jumper.addEventListener("click",this.jump.bind(this));
}

MediaJumper.prototype.getMediaObject = function(){
    for( var i = 0 ; i < MediaObjects.length ; i ++ ){
        var mediaObj = MediaObjects[i];
        if( mediaObj.id == this.id )
            return mediaObj;
    }
    return null;
}

MediaJumper.prototype.jump = function(){
    if( this.mediaObject ){
        if( !this.mediaObject.hasStarted )
            this.mediaObject.startUp();
        this.mediaObject.elem.currentTime = this.time;
        this.mediaObject.elem.play();
    }
}

Feel free to ask any question's about the code, or report anything that is not working, good luck and hope it helps! :)

Updates

  • Added a data-start and data-end, note that data-end will only stop the video if the time specified matches the current time of the video floored. Technically:

    if( Math.Floor(MediaCurrentTime) == DataEndTime ) stop();

This means if a jumper is called it will continue to play normally until it hits that number again, and if the jumper calls for a time beyond the data-end, then the video will be played normally.

undefined
  • 3,949
  • 4
  • 26
  • 38
  • Wow! My first question would be: where did you learn this? --- Your Fiddle works great, thank you very much! No delay at all. --- Your help is very much appreciated indeed, I will use this for science. An exquisitely helpful and excellent coder, you are. --- Thank you for your effort in sharing your knowledge in an open way. – O0123 Jan 26 '15 at 10:13
  • Thanks! You can learn more about the HTML5 audio and video elements [**here**](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_HTML5_audio_and_video), I was wondering though didn't you need a `data-start` and `data-end` in the facades? Or was that only for the youtube one? :) – undefined Jan 26 '15 at 16:09
  • You are right, something like `data-end` might come in handy. I was just enthusiastically fiddling with your result, that I forgot about that. --- A small improvement may be to change the `div`s to `span`s, since I couldn't get the `mediaJumper`s inline when they were `div`s. – O0123 Jan 26 '15 at 17:12
  • To get them as inline you could add `display:inline-block` in the CSS, or change it to a span as you did (span is displayed as an inline-block by default). I'll do when I come home from work, see you! – undefined Jan 26 '15 at 17:42
  • Yes, you are right, normally display: `inline-block` works just fine, as well as in your Fiddle. However, on my Drupal article, the `span` was the only solution ... couldn't figure out why. --- Now, for my current implementation, I am trying to make the `mediaAudioFacade` have a position: fixed at the bottom of the document, but it is very difficult to get the desired `100%` (minus `40 px` on both `left` and `right`), across all major browsers; because *Chrome* seems to work with a smaller HTML5 audio player, it seems to almost exactly (±`3px`) double the "margins". – O0123 Jan 26 '15 at 19:36
  • For *Chrome*, it seems very hard to get the horizontal *CSS* margins, described in the comment above, right. Even with extra *Chrome*-specific *CSS* in between `@media screen and (-webkit-min-device-pixel-ratio:0){`...`}`, as described in [Is there a Google Chrome-only CSS hack?](http://stackoverflow.com/questions/10812093/is-there-a-google-chrome-only-css-hack), but I'm not sure if *Safari*'s *CSS* will get changed along. --- It might be because I am not an experienced coder, but I find *Chrome* having a lot of room for improvements. E.g. it can also not handle RSS feeds very well. – O0123 Jan 26 '15 at 20:10
  • It seems like the hack is webkit, so it should work in other webkit browsers like Safari and Opera :) – undefined Jan 27 '15 at 00:22
  • 1
    I've updated the answer and the fiddle to support `data-start` and `data-end` attributes, hope it helps! :) – undefined Jan 27 '15 at 04:49
  • Nice improvement, it works fine! --- I am not in the need for this, but it could be handy if every `
    ` could specify its own `data-start` and a `data-end`, like in your [Fiddle](http://jsfiddle.net/yhmo9j1a/12/) for embedded Youtube videos. --- In your current code for locally stored A/V, it seems `data-time` can already be replaced by `data-start`, however `data-end` has no defined function in the `
    `'s there yet.
    – O0123 Jan 27 '15 at 09:03
  • **I have found a fix for the *Chrome* issue.** The discussion that offered a solution is this one: [Remove max-width on audio player in Chrome](http://stackoverflow.com/questions/26221047/remove-max-width-on-audio-player-in-chrome?rq=1). --- One should just add `.mediaAudio::-webkit-media-controls-enclosure {max-width: inherit}` in the *CSS*. – O0123 Jan 27 '15 at 10:41
  • Hey @undefined. You are the one who kick-started my projects with your enormous help. How are you doing? ||| I would like to ask you the following: I now use: ` – O0123 Apr 18 '15 at 15:29