首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 實作網路點播系統:目錄掃描、播放清單生成與前端播放器整合

     編輯:PHP 相關知識

系統架構設計

  • 媒體庫掃描:遞迴掃描目錄,建立藝人/專輯/曲目三級結構。
  • 中繼資料提取:讀取 ID3 標籤 (MP3)、Vorbis Comment (OGG/FLAC)、MP4 atoms。
  • 播放清單生成:M3U/PLS/JSON 格式,支援相對/絕對路徑、HTTP 範圍請求。
  • 前端播放器:HTML5 `
  • 串流輸出:PHP `readfile` / `fread` + `Range` 標頭支援斷點續傳、快轉。

後端核心:媒體庫掃描與索引

<?php
// scan_library.php
$mediaRoot = __DIR__ . '/media';
$indexFile = __DIR__ . '/cache/library_index.json';

function scanMediaLibrary(string $root): array {
    $library = [];
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $file) {
        if (!$file->isFile()) continue;
        $ext = strtolower($file->getExtension());
        if (!in_array($ext, ['mp3', 'ogg', 'flac', 'm4a', 'wav'])) continue;

        $relPath = substr($file->getPathname(), strlen($root) + 1);
        $pathParts = explode(DIRECTORY_SEPARATOR, $relPath);
        
        // 假設結構:藝人/專輯/曲目.mp3
        $artist = $pathParts[0] ?? 'Unknown Artist';
        $album = $pathParts[1] ?? 'Unknown Album';
        $title = basename($file->getFilename(), '.' . $ext);
        $track = count($pathParts) > 2 ? (int)preg_replace('/\D/', '', $pathParts[2]) : 0;

        // 讀取 ID3 標籤 (需安裝 getid3 庫)
        $metadata = [];
        if (class_exists('getID3')) {
            $getID3 = new getID3();
            $fileInfo = $getID3->analyze($file->getPathname());
            $metadata = $fileInfo['tags']['id3v2'] ?? $fileInfo['tags']['id3v1'] ?? [];
            $title = $metadata['title'][0] ?? $title;
            $artist = $metadata['artist'][0] ?? $artist;
            $album = $metadata['album'][0] ?? $album;
            $track = $metadata['track'][0] ?? $track;
        }

        $library[$artist][$album][] = [
            'title' => $title,
            'artist' => $artist,
            'album' => $album,
            'track' => $track,
            'file' => $relPath,
            'url' => '/stream.php?file=' . urlencode($relPath),
            'duration' => $fileInfo['playtime_seconds'] ?? 0,
            'size' => $file->getSize(),
            'mime' => $fileInfo['mime_type'] ?? 'audio/mpeg',
        ];
    }

    // 排序
    foreach ($library as &$artistAlbums) {
        ksort($artistAlbums);
        foreach ($artistAlbums as &$tracks) {
            usort($tracks, fn($a, $b) => $a['track'] <=> $b['track']);
        }
    }
    return $library;
}

$library = scanMediaLibrary($mediaRoot);
file_put_contents($indexFile, json_encode($library, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
echo "媒體庫索引完成,共 " . count($library) . " 位藝人\n";
?>

串流輸出:支援 Range 請求 (斷點續傳/快轉關鍵)

<?php
// stream.php
$mediaRoot = __DIR__ . '/media';
$fileParam = $_GET['file'] ?? '';

if (!$fileParam) {
    http_response_code(400);
    exit('Missing file parameter');
}

// 安全:防止目錄遍歷
$requestedPath = realpath($mediaRoot . '/' . $fileParam);
$mediaRootReal = realpath($mediaRoot);
if ($requestedPath === false || strpos($requestedPath, $mediaRootReal) !== 0) {
    http_response_code(403);
    exit('Access denied');
}

if (!is_file($requestedPath) || !is_readable($requestedPath)) {
    http_response_code(404);
    exit('File not found');
}

$fileSize = filesize($requestedPath);
$mimeType = mime_content_type($requestedPath);

// 處理 Range 標頭
$rangeHeader = $_SERVER['HTTP_RANGE'] ?? '';
$start = 0;
$end = $fileSize - 1;
$statusCode = 200;

if ($rangeHeader) {
    // Range: bytes=start-end
    if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
        $start = (int)$matches[1];
        $end = $matches[2] !== '' ? (int)$matches[2] : $fileSize - 1;
        if ($start >= $fileSize || $end >= $fileSize || $start > $end) {
            header('Content-Range: bytes */' . $fileSize);
            http_response_code(416); // Range Not Satisfiable
            exit;
        }
        $statusCode = 206; // Partial Content
    }
}

$contentLength = $end - $start + 1;

header('Content-Type: ' . $mimeType);
header('Content-Length: ' . $contentLength);
header('Accept-Ranges: bytes');
header('Cache-Control: public, max-age=3600');
header('Content-Disposition: inline; filename="' . basename($requestedPath) . '"');

if ($statusCode === 206) {
    header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
    http_response_code(206);
}

// 輸出檔案片段
$fp = fopen($requestedPath, 'rb');
fseek($fp, $start);
$bufferSize = 8192;
while (!feof($fp) && $contentLength > 0) {
    $read = min($bufferSize, $contentLength);
    echo fread($fp, $read);
    $contentLength -= $read;
    flush(); // 即時輸出
}
fclose($fp);
?>

前端播放器 (Vue 3 + HTML5 Audio 範例)

<!-- player.vue -->



API 端點:媒體庫索引

<?php
// api/library_index.php
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: public, max-age=300');
readfile(__DIR__ . '/../cache/library_index.json');
?>

進階功能擴展

功能技術要點
歌詞同步 (LRC)解析 .lrc 檔,前端依據 currentTime 顯示對應行
音頻視覺化Web Audio API AnalyserNode + Canvas 繪製頻譜/波形
多音質切換預轉碼多版本 (128k/320k/FLAC),前端按頻寬選擇
播放歷史/收藏localStorage 或後端資料庫記錄 user_id + track_id
離線快取Service Worker + Cache API 快取音頻檔案
Chromecast/AirPlayCast SDK / WebKit 播放目標 API

部署建議

  • 靜態檔案直接由 Nginx 服務:`location /media/ { alias /var/www/media/; }` 避免 PHP 處理大檔案傳輸。
  • PHP 僅處理動態邏輯:索引建立、權限驗證、播放清單生成、API 回應。
  • 啟用 sendfile:Nginx `sendfile on;` `aio on;` 最大化靜態傳輸效能。
  • CDN 加速:媒體檔案推至 CDN,來源站僅存原始檔。

總結

網路點播系統核心在於媒體庫索引、Range 串流支援、前端播放器狀態管理三大支柱。PHP 負責索引建立與權限控制,靜態傳輸交給 Nginx/CDN,前端採用現代框架 (Vue/React/Svelte) 配合 HTML5 Audio/Web Audio API,即可構建體驗流暢、擴展性強的音樂播放平台。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved