初始化版本

This commit is contained in:
LL
2025-11-18 14:18:28 +08:00
commit 3c348195b7
23 changed files with 2837 additions and 0 deletions

131
upload.php Normal file
View File

@@ -0,0 +1,131 @@
<?php
// 投稿页面:允许用户上传视频,上传内容进入待审核目录 videos_pending
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>投稿视频</title>
<link rel="stylesheet" href="assets/style.css" />
</head>
<body>
<div class="page">
<header class="menu">
<div class="menu__logo">投稿</div>
<nav aria-label="主菜单">
<ul class="menu__nav">
<li><a class="menu__link" href="index.php">返回首页</a></li>
</ul>
</nav>
</header>
<main class="upload-container" aria-label="投稿表单">
<h1>上传你想分享的视频</h1>
<form class="upload-grid video" action="api/upload_video.php" method="post" enctype="multipart/form-data">
<div class="field">
<label>选择视频文件(支持 mp4 / webm / mov</label>
<div id="dropzone" class="dropzone" role="button" aria-label="拖拽或点击选择文件">
拖拽文件到此处,或点击选择文件
</div>
<input id="videoFile" class="hidden-input" type="file" name="video" accept="video/mp4,video/webm,video/quicktime" required />
</div>
<div class="field">
<label>标题(可选)</label>
<input id="titleInput" type="text" name="title" placeholder="给你的投稿起个标题" />
<div class="actions">
<button class="btn" type="submit">提交投稿</button>
<a href="review.php" style="font-size:12px;color:#666;">管理员审核入口</a>
</div>
</div>
<aside class="card">
<video id="preview" class="video-preview" controls style="display:none"></video>
<div id="meta" class="meta"></div>
<div id="errors" class="errors"></div>
</aside>
<p class="tips">说明投稿后文件会进入“待审核”队列管理员审核通过后才会出现在播放列表中。单个视频大小限制50MB。</p>
</form>
</main>
</div>
<script>
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('videoFile');
const preview = document.getElementById('preview');
const meta = document.getElementById('meta');
const errors = document.getElementById('errors');
const titleInput = document.getElementById('titleInput');
const allowedExt = ['mp4','webm','mov'];
const SIZE_LIMIT = 50 * 1024 * 1024; // 50MB
function formatSize(bytes){
if (bytes >= 1024*1024*1024) return (bytes/1024/1024/1024).toFixed(2) + ' GB';
if (bytes >= 1024*1024) return (bytes/1024/1024).toFixed(2) + ' MB';
if (bytes >= 1024) return (bytes/1024).toFixed(2) + ' KB';
return bytes + ' B';
}
function setFile(file){
if (!file) return;
// 填充到 input
const dt = new DataTransfer();
dt.items.add(file);
fileInput.files = dt.files;
// 校验扩展名
const name = file.name || '';
const ext = name.split('.').pop().toLowerCase();
errors.textContent = '';
if (!allowedExt.includes(ext)) {
errors.textContent = '仅支持 mp4 / webm / mov 格式文件';
fileInput.value = '';
preview.style.display = 'none';
preview.removeAttribute('src');
meta.textContent = '';
return;
}
// 校验大小
if (file.size > SIZE_LIMIT) {
errors.textContent = '文件过大:超过 50MB 限制';
fileInput.value = '';
preview.style.display = 'none';
preview.removeAttribute('src');
meta.textContent = '';
return;
}
// 自动填充标题
if (!titleInput.value) {
const base = name.replace(/\.[^.]+$/, '');
titleInput.value = base;
}
// 预览视频
const url = URL.createObjectURL(file);
preview.src = url;
preview.style.display = 'block';
preview.load();
meta.textContent = '文件:' + name + ',大小:' + formatSize(file.size) + (file.type ? ',类型:' + file.type : '');
preview.onloadedmetadata = () => {
const d = preview.duration;
if (isFinite(d) && d > 0) {
meta.textContent += ',时长:' + Math.round(d) + ' 秒';
}
};
}
dropzone.addEventListener('click', () => fileInput.click());
dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('dragging'); });
dropzone.addEventListener('dragleave', () => dropzone.classList.remove('dragging'));
dropzone.addEventListener('drop', (e) => {
e.preventDefault(); dropzone.classList.remove('dragging');
const file = e.dataTransfer.files?.[0];
setFile(file);
});
fileInput.addEventListener('change', () => {
const file = fileInput.files?.[0];
setFile(file);
});
</script>
</body>
</html>