5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Phoenix LiveViewでDPlayerを使って.m3u8形式の動画を再生する (Elixir)

Last updated at Posted at 2021-07-31

はじめに

Setup

$ mix phx.new autorace_phoenix --live
$ cd autorace_phoenix

Why autorace_phoenix?

JavaScriptのライブラリをインストールする

$ cd assets
$ npm install dplayer --save
$ npm install --save hls.js
$ cd ..
  • DPlayer
  • hls.js
  • --saveの位置が異なるのはそれぞれの公式ページに書いてあったnpm installの指示に従いました
    • どっちの順番でもいいとおもっています

ソースコードを書く

Elixir

ルートを追加

lib/autorace_phoenix_web/router.ex
  scope "/", AutoracePhoenixWeb do
    pipe_through :browser

    live "/", PageLive, :index
    live "/player", PlayerLive # Add
  end

DPlayerで動画を再生するコンポーネント

  • phx-hook="Player"は、あとででてくるJavaScriptのHooks.PlayerのようにPlayerを一致させておきます
lib/autorace_phoenix_web/live/components/player_component.ex
defmodule AutoracePhoenixWeb.PlayerComponent do
  use AutoracePhoenixWeb, :live_component

  def render(assigns) do
    ~L"""
    <div id="dplayer"
         phx-hook="Player"
         data-url="<%= @url %>">
    </div>
    """
  end
end

Player

  • Playボタンを押したら動画を再生します
  • 動画は2つ用意していて、一個目の再生が終わったら、handle_event("load-more", _, socket)がコールバックされて、2つ目の動画を再生します
  • 二個目の再生が終わったら、同じようにhandle_event("load-more", _, socket)がコールバックされて、{:noreply, assign(socket, url: nil, index: 2)}となりまして、いい感じに連続再生の動作は停止します
  • 動画のURLは適当に見つけたものでして、2021/07/31 現在、再生可能でした
lib/autorace_phoenix_web/live/player_live.ex
defmodule AutoracePhoenixWeb.PlayerLive do
  use AutoracePhoenixWeb, :live_view

  @urls [
    "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8",
    "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
  ]

  def mount(_params, _session, socket) do
    {:ok, assign(socket, url: nil, index: -1)}
  end

  def render(assigns) do
    ~L"""
    <button phx-click="play">
      Play
    </button>

    <%= if @url do %>
    <%= live_component @socket, AutoracePhoenixWeb.PlayerComponent, url: @url %>
    <% end %>
    """
  end

  def handle_event("play", _, socket) do
    index = socket.assigns.index + 1
    {:noreply, assign(socket, url: Enum.at(@urls, index), index: index)}
  end

  def handle_event("load-more", _, socket) do
    index = socket.assigns.index + 1
    {:noreply, assign(socket, url: Enum.at(@urls, index), index: index)}
  end
end

JavaScript

assets/js/app.js
import Hooks from "./hooks"; // add

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}}) 
// mod `hooks: Hooks, `を追加
assets/js/hooks.js
import Player from "./player"

let Hooks = {};

Hooks.Player = {
  url() { return this.el.dataset.url; },
  play() {
    const player = new Player(this.url(), function() {
      this.pushEvent("load-more");
    }.bind(this));

    player.fullScreen();
    player.play();
  },
  mounted() {
    this.play()
  },
  updated() {
    console.log("updated", this.url());
    this.play();
  },
}

export default Hooks;
assets/js/player.js
import DPlayer from "dplayer";
import Hls from "hls.js";

class Player {
  constructor(url, ended_handler) {
    this.dp = new DPlayer({
      container: document.getElementById("dplayer"),
      screenshot: true,
      video: {
        url: url,
        type: "customHls",
        customType: {
          customHls: function (video) {
            const hls = new Hls();
            hls.loadSource(video.src);
            hls.attachMedia(video);
          },
        },
      },
    });

    this.dp.on("ended", function() { 
      console.log("ended");
      ended_handler();
    });
    this.dp.on("abort", function() { console.log("abort"); });
    this.dp.on("error", function() { console.log("error"); });
    this.dp.on("canplay", function() { console.log("canplay"); });
    this.dp.on("playing", function() { console.log("playing"); });
  };

  fullScreen() {
    console.log("fullScreen");
    this.dp.fullScreen.request("web");
  }

  play() {
    console.log("play");
    this.dp.play();
  }
}

export default Player;

Run

$ mix phx.server
  • Visit: http://localhost:4000/player
  • このスクリーンショットだとちっともおもしろくありませんね
  • Playボタンを押すと動画の再生が開始します :tada::tada::tada:

スクリーンショット 2021-07-31 21.52.07.png

ソースコード

コミット

Wrapping Up :lgtm::lgtm::lgtm::lgtm::lgtm::lgtm:

  • Enjoy Elixir !!!
  • Surface UIを取り入れて完成を目指したいとおもいます
5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?