🐥

動画サイト的なもの作ってみた

2024/04/03に公開

動画サイト的なもの作ってみた

環境

  • OS : Ubuntu 22.04
  • WebServer : Apache 2.4.58
  • CGI : PHP 8.2.12
  • HTML : HTML Living Standard

0.設定

/etc/apache2/apache2.conf

/etc/apache2/apache2.conf
<Directory /var/www/>
        Options Indexes FollowSymLinks
        # AllowOverride None
        AllowOverride All
        Require all granted
</Directory>

.htaccess

AddType application/x-httpd-php .php .html
AddHandler application/x-httpd-php .php .html

php_value memory_limit 1000M
php_value post_max_size 1000M
php_value upload_max_filesize 1200M

1.HTML(UI)

とりあえず外側のHTMLを書いていきます。
index.html

index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <link href="style.css" rel="stylesheet" />
        <title>どーがさいと</title>
    </head>
    <body>
        <h1>どーがさいと</h1>
        <hr>
        <div style="text-align: center;">
            <h2>投稿</h2>
            <form action="cgi-bin/videoPost.php" method="post" enctype="multipart/form-data">
                動画タイトル (14文字まで): <input type="text" name="uploadTitle" maxlength="20"><br>
                動画ファイル : <input type="file" name="upload_video"></br>
                概要欄 (最大5行)</br>
                <textarea name="uploadDescription" rows="8" cols="40" value="" maxlength="70"></textarea></br>
                <input type="submit" value="投稿"></br>
            </form>
            <p>*Uploading may take some time for processing.</p>
            <h2>動画リスト</h2>
        </div>
        <ul class="videoList">
            <!-- ここに動画リスト -->
        </ul>
    </body>
</html>

watch.php

watch.php
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <link href="style.css" rel="stylesheet" />
        <title>どーがさいと - <!-- 動画タイトル ここに設置 --></title>
    </head>
    <body>
        <h1><!-- 動画タイトル ここに設置 --></h1>
        <h3>概要欄</h3>
        <!-- 概要欄 ここに設置 -->
        <hr>
            <!-- 動画 ここに設置 -->
        <hr>
    </body>
</html>

style.css

style.css
video{
    width: 50%;
    @media screen and (max-width: 480px) {
        width: 100%;
    }
}

pre{
    white-space: pre-wrap;
    font-size: x-large;
}

ul{
    display: flex;
    flex-wrap: wrap;
}

2.プログラム

cgi-binフォルダの中に書きます
cgi-bin/videoPost.php 動画投稿プログラム

cgi-bin/videoPost.php
<?php
    function redirectBackPage(){
        if (isset($_SERVER['HTTP_REFERER'])){
            header("Location: ".$_SERVER['HTTP_REFERER']);
        }
        exit;
    }
    ob_start();
    // getの引数がなかったりしたら前のページに戻る
    if(!(!empty($_FILES) && isset($_POST["upload_title"]) && isset($_POST["upload_description"]))){redirectBackPage();}
    // タイトルと概要欄の文字列を変数に代入する(htmlspecialcharsでxss対策)
    $title = htmlspecialchars($_POST["upload_title"]);
    $description = htmlspecialchars($_POST["upload_description"], ENT_QUOTES, 'UTF-8');
    // 文字数 行数制限
    if (strlen($description) > 70){redirectBackPage();}
    else if (strlen($title) > 14) {redirectBackPage();}
    $description = str_replace("<br>", "", $description);
    else if (count(explode("/",$description)) > 5){return;}
    $filename = "";
    $videoid = "";
    // 動画IDをランダム生成
    while (!file_exists($filename)){
        $videoid = uniqid(rand(), true);
        $filename = "../videos/".$videoid.".mp4";
        if (!file_exists($filename)){
            break;
        }
        continue;
    }
    // 動画リストファイルに[動画ID,タイトル,投稿時間,IPのハッシュ値(ユーザーID的な?)]を書き込み
    $fp = fopen("../videos/videos.csv", 'ab');
    if ($fp){
        if (flock($fp, LOCK_EX)){
            if (fputcsv($fp, [$videoid, str_replace("'", "\"", $title), date("Y/m/d H:i"), hash("fnv1a32", $_SERVER['REMOTE_ADDR']), $description]) === FALSE){
                echo '<script>alert("File write failed.");</script>';
                exit;
            }
            flock($fp, LOCK_UN);
        }else{
            echo '<script>alert("File lock failed.");</script>';
            exit;
        }
    } else {exit;}
    // 動画ファイル書き込み
    $uploaded_path = realpath("../videos/mp4/")."/".$videoid.".mp4";
    $result = move_uploaded_file($_FILES['upload_video']['tmp_name'],$uploaded_path);
    if(!$result){
        echo "Upload Error : ".$_FILES['upload_video']['error'];
        exit;
    }
    // ffmpegでhls形式に変換しよう!(最後に&をつけることによってバックグラウンド実行)
    exec("ffmpeg -i ".$uploaded_path." -b:v 10M -c:a copy -f hls -hls_playlist_type vod -hls_time 10 -g 24 -hls_segment_filename \"".realpath("../videos/hls/")."/".$videoid."%3d.ts\" ".realpath("../videos/hls/")."/".$videoid.".m3u8 2> ".realpath("../logs")."/".$videoid.".log &");
    // 前のページに戻る
    redirectBackPage();

cgi-bin/videoList.php 動画リストプログラム

cgi-bin/videoList.php
<?php
    $rows = [];
    $fp = fopen("videos/videos.csv", 'rb');
    if ($fp){
        if (flock($fp, LOCK_SH)){
            while ($row = fgetcsv($fp)) {
                $rows[] = $row;
            }
            $rows = array_reverse($rows);
            if (!empty($rows)){echo "<ul>";}
            foreach ($rows as $row){
                // 動画リスト出力
                $link = "watch?videoid=".$row[0];
                echo "<li>".$row[2]."|".$row[3]."</br><a href=".$link.">".$row[1];
                if(!file_exists("videos/hls/".$row[0].".m3u8") && !file_exists("videos/mp4/".$row[0].".mp4")){echo "(処理中)";}
                echo "</a></li><p style=\"margin-right: 140px;\">";
            }
            echo "</ul>";
            flock($fp, LOCK_UN);
        } else {
            echo '<script>alert("File lock failed.");</script>';
        }
    }
    fclose($fp);

cgi-bin/watch.php 動画視聴プログラム

cgi-bin/watch.php
<?php
    $rows = [];
    $videoid = "";
    $fp = fopen("videos/videos.csv", 'rb');
    if ($fp){
        if (flock($fp, LOCK_SH)){
            while ($row = fgetcsv($fp)) {
                $rows[] = $row;
            }
            foreach ($rows as $row){
                if ($row[0] == $_GET["videoid"]){
                    $videoid = $row[0];
                    break;
                }
            }
            if($videoid == "") {redirectBackPage();}
            flock($fp, LOCK_UN);
        } else {
            echo '<script>alert("File lock failed.");</script>';
        }
    } else {exit;}

3.UIにも少しプログラムを追加

index.html

index.html
<ul class="videoList">
    <!-- ここに動画リスト -->
    <?php include "cgi-bin/videoList.php" ?>
</ul>

watch.php

watch.php
<?php
    function redirectBackPage(){
        if (isset($_SERVER['HTTP_REFERER'])){
            header("Location: ".$_SERVER['HTTP_REFERER']);
        }
        exit;
    }
    date_default_timezone_set('Asia/Tokyo');
    if(!isset($_GET["videoid"])){redirectBackPage();}
    else if (!file_exists("videos/hls/".$_GET["videoid"].".m3u8") && !file_exists("videos/mp4/".$_GET["videoid"].".mp4")){redirectBackPage();}
    include "cgi-bin/watch.php";
?>
<head>
    <meta charset="UTF-8" />
    <link href="style.css" rel="stylesheet" />
    <title>どーがさいと - <?=$row[1]?></title>
</head>
<body>
    <h1><?=$row[1]?></h1>
    <h3>概要欄</h3>
    <pre><?=$row[4]?></pre>
    <hr>
        <!-- 動画 ここに設置 -->
        <?php if(file_exists("videos/hls/".$_GET["videoid"].".m3u8")): ?>
            <video controls id="hlsvideo"></video>
            <script>
                if(Hls.isSupported()) {
                    var video = document.getElementById('hlsvideo');
                    var hls = new Hls();
                    hls.loadSource("videos/hls/<?=$_GET["videoid"]?>.m3u8");
                    hls.attachMedia(video);
                }
            </script>
        <?php elseif(file_exists("videos/mp4/".$_GET["videoid"].".mp4")): ?>
            <video controls id="mp4video" src="videos/mp4/<?=$_GET["videoid"]?>.mp4"></video>
        <?php endif; ?>
</body>

4.セットアップ

setup.sh
mkdir videos videos/mp4 videos/hls logs
touch videos/videos.csv
sudo apt install unzip ffmpeg -y

最後に

とりあえずこれで完成かな?
今回のプログラムは完全練習のプログラムなので、いろいろ冗長化かも

Discussion