はじめに
-
Elixirを楽しんでいますか
- この記事はPhoenix LiveViewで、
.m3u8
形式の動画を再生してみます - 動画の再生にはJavaScriptの動画再生ライブラリであるDPlayerを使います
- 公式の解説は、https://hexdocs.pm/phoenix_live_view/js-interop.html#client-hooks あたりが該当します
- 2021/7/31(土) 00:00〜2021/8/2(月) 23:59開催のElixirの純粋なもくもく会autoracex #39の成果です
-
Elixirは
1.11.4-otp-23
、Eralngは23.0.1
を使いました
Setup
$ mix phx.new autorace_phoenix --live
$ cd autorace_phoenix
Why autorace_phoenix?
- なぜ
autorace_phoenix
というプロジェクト名にしたかというと、Vue.jsで、オートレースの過去動画を連続再生するアプリを最近つくりました -
https://torifukukaiou.github.io/autorace-app/#/
-
GitHub Pagesでイゴいています
- ソースコード: https://github.com/TORIFUKUKaiou/autorace-app
-
- なんとなくJavaScriptに自信がついてきたのでElixirと組み合わせることをやってみたくなりました
JavaScriptのライブラリをインストールする
$ cd assets
$ npm install dplayer --save
$ npm install --save hls.js
$ cd ..
ソースコードを書く
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
に全部書いてもいいとおもいます - The Pragmatic Studio Phoenix LiveView Courseという動画学習サイトで学んだことを使ってみました
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ボタンを押すと動画の再生が開始します
ソースコード
コミット
Wrapping Up 




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