react + youtube iframe apiで、youtube視聴時間を測定
youtube iframe api(ブラウザ上でyoutube再生をjs操作できるライブラリ)で視聴時間をカウント(倍速なら倍)。視聴完了も検知する。
src/YouTubePlayer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
import React, { useEffect, useRef, useState } from "react"; // YouTube プレイヤーコンポーネント const YouTubePlayer = ({ videoId }) => { // YouTube プレイヤーのインスタンスを管理するための参照 const playerRef = useRef(null); // requestAnimationFrame の ID を管理するための参照 const animationFrameRef = useRef(null); // 最後に計測した時間のタイムスタンプを管理する参照 const lastTimestampRef = useRef(null); // 視聴時間(秒単位)を管理するステート const [watchTime, setWatchTime] = useState(0); // 視聴完了を管理するステート const [isCompleted, setIsCompleted] = useState(false); useEffect(() => { // YouTube API をロードする関数 const loadYouTubeAPI = () => { // window.YT は YouTube IFrame API によって定義されるオブジェクトで、YouTube API のスクリプトをロードしないと window.YT は存在しない ため、適切に読み込む必要がある。 if (!window.YT) { // すでに script タグが追加されていないかチェック(重複ロード防止) if (!document.querySelector("script[src='https://www.youtube.com/iframe_api']")) { // <script> タグを作成し、YouTube API を動的に読み込む const tag = document.createElement("script"); tag.src = "https://www.youtube.com/iframe_api"; tag.async = true; document.body.appendChild(tag); } // YouTube API の準備が完了したら `createPlayer` を実行 window.onYouTubeIframeAPIReady = () => { createPlayer(); }; } else { // すでにロード済みなら、プレイヤーを作成 createPlayer(); } }; // YouTube プレイヤーを作成する関数 const createPlayer = () => { // API が完全にロードされていなければ何もしない if (!window.YT || !window.YT.Player) return; // YouTube IFrame API を使用してプレイヤーを作成 playerRef.current = new window.YT.Player("youtube-player", { videoId, // 指定された YouTube 動画の ID events: { onStateChange: onPlayerStateChange, // プレイヤーの状態変化を監視 }, }); }; // プレイヤーの状態が変化した際の処理 const onPlayerStateChange = (event) => { // 動画が再生中(PLAYING)の場合 if (event.data === window.YT.PlayerState.PLAYING) { startTracking(); // 視聴時間の計測を開始 setIsCompleted(false); // 新しく再生された場合、視聴完了フラグをリセット } // 動画が終了(ENDED)した場合 else if (event.data === window.YT.PlayerState.ENDED) { stopTracking(); // 計測を停止 setIsCompleted(true); // 視聴完了フラグをセット } // 一時停止やバッファリング中(PAUSED、BUFFERING)などの場合 else { stopTracking(); // 計測を停止 } }; // 視聴時間を追跡する const trackTime = (timestamp) => { // 初回呼び出し時は lastTimestamp を設定 if (!lastTimestampRef.current) { lastTimestampRef.current = timestamp; } // 前回の計測からの経過時間(秒単位)を計算 const deltaTime = (timestamp - lastTimestampRef.current) / 1000; lastTimestampRef.current = timestamp; // YouTube プレイヤーが存在する場合 if (playerRef.current) { // 現在の再生速度を取得 const playbackRate = playerRef.current.getPlaybackRate(); // 視聴時間を増加(再生速度を考慮) setWatchTime((prev) => prev + deltaTime * playbackRate); } // 次のフレームで `trackTime` を再度呼び出し animationFrameRef.current = requestAnimationFrame(trackTime); }; // 視聴時間の計測を開始する関数 const startTracking = () => { // すでに tracking が開始されていなければ実行 if (!animationFrameRef.current) { lastTimestampRef.current = null; // 初回呼び出しのタイムスタンプをリセット animationFrameRef.current = requestAnimationFrame(trackTime); } }; // 視聴時間の計測を停止する関数 const stopTracking = () => { // `requestAnimationFrame` をキャンセル if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); animationFrameRef.current = null; } }; // YouTube API をロード loadYouTubeAPI(); // コンポーネントのアンマウント時にクリーンアップ return () => { stopTracking(); // 計測を停止 if (playerRef.current) { playerRef.current.destroy(); // YouTube プレイヤーを破棄 } }; }, [videoId]); // 視聴時間を "時:分:秒" 形式に変換する関数 const formatTime = (seconds) => { const h = Math.floor(seconds / 3600); // 時 const m = Math.floor((seconds % 3600) / 60); // 分 const s = Math.floor(seconds % 60); // 秒 // 2桁のゼロ埋めでフォーマット return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; }; return ( <div> {/* YouTube プレイヤーを埋め込むための div 要素 */} <div id="youtube-player" style={{ width: "560px", height: "315px" }}></div> {/* 視聴時間を "時:分:秒" 形式で表示 */} <p>視聴時間: {formatTime(watchTime)}</p> {/* 視聴完了のメッセージを表示 */} {isCompleted && <p style={{ color: "green", fontWeight: "bold" }}> 視聴完了! </p>} </div> ); }; // コンポーネントをエクスポート export default YouTubePlayer; |
src/App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React from "react"; import YouTubePlayer from "./YouTubePlayer"; function App() { return ( <div style={{ textAlign: "center", marginTop: "20px" }}> <h1>YouTube 視聴時間カウンター</h1> {/* 任意の YouTube 動画 ID を設定 */} <YouTubePlayer videoId="dQw4w9WgXcQ" /> </div> ); } export default App; |