Sisifos496
Sisifos496

Reputation: 39

HTML Video Tag's Source Doesn't Work With Base64 Code

My project is about user chooses a directory and then the program takes all the videos inside the directory and via help of a video player then shows the content of the video. This photo might explain the project layout: (Right side is where you choose the video and left side is where the video plays)

Project Layout

I have used vanilla javascript so far. here is the code:

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Video Player</title>
</head>
<body>
    <div class="select-folder-div">
        <input type="file" name="file" id="file-selector" class="file-selector" 
webkitdirectory multiple>
    </div>
    <div class="main-div">
        <div class="video-player-div" id="video-player-div">

        </div>
        <div class="video-chooser-div" id="video-chooser-div">
        
        </div>
    </div>
    <script src="index.js"></script>
</body>
</html>

CSS:

body {
    background: rgb(34, 32, 32);
    color: rgb(204, 87, 204);
    font-size: 1.5em;
    font-family:'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans 
Unicode', Geneva, Verdana, sans-serif;
}

.main-div {
    display: flex;
    flex-direction: row;
}

.video-player-div{
    display: flex;
    align-items: center;
    align-self: flex-start;
    border: 5px solid rgb(204, 87, 204);
    width: 70%;
    margin-top: 5%;
    margin-bottom: 10%;
    margin-left: 3%;
    margin-right: 3%;
    padding: 3%;
}

.video-chooser-div {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 20px;
    border: 5px solid rgb(204, 87, 204);
    width: 21%;
    margin-top: 5%;
    margin-bottom: 10%;
    margin-right: 3%;
    padding: 3%;
    overflow: auto;
}

.file-selector {
    margin-left: 45%;
    margin-top: 3%;
}

.video-name:hover {
    cursor: pointer;
    color: blueviolet;
}

.video-name:active {
    color: rgb(99, 14, 178);
}

Javascript:

const directoryInput = document.getElementById("file-selector");
const videoChooser = document.getElementById("video-chooser-div");
const videoPlayer = document.getElementById("video-player-div");

directoryInput.addEventListener("change", (event) => {
    videoChooser.innerHTML = "";

    for (const file of event.target.files) {
        if (file.name.endsWith(".mp4")) {
            let videoContentDiv = document.createElement("div");
            videoContentDiv.className = "video-content";

            let videoName = file.name;
            let videoNameTag = document.createElement("p");
            videoNameTag.className = "video-name";
            videoNameTag.textContent = videoName;

            videoContentDiv.appendChild(videoNameTag);

            videoChooser.appendChild(videoContentDiv);

            videoContentDiv.addEventListener("click", () => {
                videoPlayer.innerHTML = "";

                const video = document.createElement("video");

                const reader = new FileReader();
                reader.onload = (event) => {
                    video.src = event.target.result;
                    console.log(video.src)
                };
                reader.readAsDataURL(file); 
            
                video.width = "100%";
                video.controls = true;

                videoPlayer.appendChild(video);
            });
        }
    }
}, false);

The Problem is I am able to select a directory and show all the mp4 contents inside it however when I click on the text the video tag doesn't show up. When I check what is inside the div there is indeed a video tag with a base64 code as source, but there is nothing on the screen not even an empty video place. I tried to check the base64, and I think I realized there are empty parts inside the base64 as I scrolled down, and I am not sure if it is normal. Even it is I don't know how to solve it.

Upvotes: 1

Views: 149

Answers (1)

herrstrietzel
herrstrietzel

Reputation: 17115

You can simplify your code.
Add a static <video> element to your HTML and hide it via css if src is empty

video[src=""] {
  opacity: 0
}

On upload you create an object URL URL.createObjectURL(file); and save it to the new playlist item.

Then you're adding a click event swapping the current video src attribute with the playlist item's object URL (e.g saved in a data attribute).

const directoryInput = document.getElementById("file-selector");
const videoChooser = document.getElementById("video-chooser-div");
const videoPlayer = document.getElementById("video-player-div");

directoryInput.addEventListener("change", (event) => {
  videoChooser.innerHTML = "";

  for (const file of event.target.files) {

    if (file.name.endsWith(".mp4")) {
      let videoContentDiv = document.createElement("div");
      videoContentDiv.className = "video-content";

      // add playlist items
      let videoName = file.name;
      let videoNameTag = document.createElement("p");
      videoNameTag.className = "video-name";
      videoNameTag.textContent = videoName;

      // create object url and save it in data attribute 
      videoNameTag.dataset.src = URL.createObjectURL(file);

      // play playlist item on click
      videoNameTag.addEventListener('click', e => {
        video.src = e.currentTarget.dataset.src;
        video.play()
      })

      videoContentDiv.appendChild(videoNameTag);
      videoChooser.appendChild(videoContentDiv);

    }
  }
}, false);
body {
  background: rgb(34, 32, 32);
  color: rgb(204, 87, 204);
  font-size: 1.5em;
  font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans 
 Unicode', Geneva, Verdana, sans-serif;

}

.main-div {
  display: flex;
  flex-direction: row;
}

.video-player-div {
  display: flex;
  align-items: center;
  align-self: flex-start;
  border: 5px solid rgb(204, 87, 204);
  width: 70%;
  margin-top: 5%;
  margin-bottom: 10%;
  margin-left: 3%;
  margin-right: 3%;
  padding: 3%;
}

.video-chooser-div {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 20px;
  border: 5px solid rgb(204, 87, 204);
  width: 21%;
  margin-top: 5%;
  margin-bottom: 10%;
  margin-right: 3%;
  padding: 3%;
  overflow: auto;
}

.file-selector {
  margin-left: 45%;
  margin-top: 3%;
}

.video-name:hover {
  cursor: pointer;
  color: blueviolet;
}

.video-name:active {
  color: rgb(99, 14, 178);
}

/* hide empty video elements */
video[src=""] {
  opacity: 0
}
<div class="select-folder-div">
  <input type="file" name="file" id="file-selector" class="file-selector" multiple>
</div>
<div class="main-div">
  <div class="video-player-div" id="video-player-div">

    <video id="video" style="width:100%; height:100%" src="" controls>
    </video>
  </div>
  <div class="video-chooser-div" id="video-chooser-div">
  </div>
</div>

This approach should be more efficient than creating a base64 data URL. Also, you might encounter problems due to URL length limitations when using base64.

Upvotes: 3

Related Questions