观影照片支持视频:转 H.264 + 抽首帧封面 (#84)
用户:确认一下观影照片是不是允许上传视频,如果不允许需要修改, 要求能上传视频、视频需要转码,参考旅程中的行程做法。 事实核查:原来不允许 —— POST /movies/<id>/photos 走 Upload::image(), 那条管线只收 JPG/PNG/GIF/WebP(要裁剪缩转运 JPEG),传视频直接拒。 旅程走 Attachment::save(),内部对 video/* 调 Video::toH264()。 Schema VERSION 35→36: - event_photos 加 kind / thumb_key / mime 三列; kind 默认 image —— 老数据全是照片,默认值即正确,不需要回填 UPDATE; - 建表语句同步改(新库直接是新结构)。 Video.php: - 新增 poster() 抽首帧。取第 1 秒(手机视频开头常是黑场/对焦), 宽 480 足够列表缩略;失败返回空 path 且**不阻断上传**。 ApiHandler.php 上传分支按 MIME 分流: - 视频 → toH264 转码 + poster 抽封面 + Storage 落盘 videos/ 与 thumbs/; 转码失败不阻断(存原视频,至少能下载后用本地播放器看); - 图片 → 仍走 Upload::image(),裁剪压缩行为完全不变。 - 返回值加了 kind / thumb / mime,端上靠它画播放按钮。 Movie.php: - photos() 带出 kind/thumb_key/mime; - addPhotos() 兼容裸键数组与结构化数组,只有一条 INSERT 路径; - deletePhoto() / destroy() 补删 thumb_key,否则删视频留孤儿封面。 验证(服务器实测):造 H.265 视频 → needsTranscode=true,toH264 成功 转成 h264/yuv420p,poster 抽出 13KB 封面;完整上传后接口返回 5 条 (4 image + 1 video 带 thumb)。迁移已跑(35→36),字段与数据核实无误, 测试视频及封面已清理,videos/ 与 thumbs/ 目录确认为空。
This commit is contained in:
+31
-2
@@ -188,7 +188,7 @@ final class Schema
|
||||
* ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。
|
||||
* 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。
|
||||
*/
|
||||
public const VERSION = '35';
|
||||
public const VERSION = '36';
|
||||
|
||||
/**
|
||||
* 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。
|
||||
@@ -539,12 +539,15 @@ CREATE TABLE IF NOT EXISTS `event_photos` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`event_id` INT UNSIGNED NOT NULL,
|
||||
`key` VARCHAR(255) NOT NULL COMMENT '照片(存储相对键)',
|
||||
`kind` ENUM('image','video') NOT NULL DEFAULT 'image' COMMENT '图片还是视频(老数据全是图片,默认值即正确)',
|
||||
`thumb_key` VARCHAR(255) DEFAULT NULL COMMENT '视频首帧封面(存储相对键;图片为空)',
|
||||
`mime` VARCHAR(100) DEFAULT NULL COMMENT 'MIME(视频转码后是 video/mp4;图片可空)',
|
||||
`position` INT NOT NULL DEFAULT 100 COMMENT '展示先后',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_ep_event` (`event_id`, `position`),
|
||||
CONSTRAINT `fk_ep_event` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='事件的现场照片(可多张)'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='事件的现场照片/视频(可多张)'
|
||||
SQL,
|
||||
// ---- 统一附件(v12)----
|
||||
// 旅程照片 / 资源附件 / 任务附件 / 评论附件,全都走这一张表:
|
||||
@@ -1858,6 +1861,32 @@ SQL);
|
||||
self::addColumn($db, 'trip_details', 'is_public',
|
||||
"ADD COLUMN `is_public` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否公开显示(用户 2026-10-03 第 4 条):0=不显示在旅程列表与日历' AFTER `summary`");
|
||||
|
||||
// ---- 观影照片支持视频(版本 36,2026-11-06)----
|
||||
/* 🔴 用户:「确认一下观影照片是不是允许上传视频,如果不允许,需要修改,
|
||||
要求能够上传视频,视频需要转码,参考旅程中的行程做法。」
|
||||
|
||||
事实核查:**原来不允许**。`POST /movies/<id>/photos` 走的是
|
||||
`Upload::image()`,那条管线只收 JPG/PNG/GIF/WebP(要裁剪缩放转 JPEG),
|
||||
传视频会被直接拒("只支持 JPG / PNG / GIF / WebP 图片")。
|
||||
旅程那边走的是 `Attachment::save()`,它内部对视频调 `Video::toH264()`
|
||||
转成 H.264 再存 —— 所以观影要支持视频,得把照片也改走那条路。
|
||||
|
||||
改走 Attachment 后有三件事要落到 `event_photos` 上:
|
||||
· `kind` —— 是图片还是视频。端上要靠它决定"显示图"还是"显示播放按钮";
|
||||
(不靠扩展名猜:`.mp4` 也可能是图片的误命名,MIME 才靠谱)
|
||||
· `thumb_key` —— 视频首帧封面。列表里不可能为每个视频解一次码
|
||||
(一屏 9 个视频 × 解码 = 卡死),必须服务端抽一帧存成图;
|
||||
· `mime` —— 冗余存一份,免得每次都要回查 Storage 或猜。
|
||||
|
||||
⚠️ 三列都给默认值:`kind` 默认 `'image'`,老数据(全是照片)自动正确,
|
||||
不需要回填 UPDATE —— 这是最安全的一种迁移默认值。 */
|
||||
self::addColumn($db, 'event_photos', 'kind',
|
||||
"ADD COLUMN `kind` ENUM('image','video') NOT NULL DEFAULT 'image' COMMENT '图片还是视频(老数据全是图片,默认值即正确)' AFTER `key`");
|
||||
self::addColumn($db, 'event_photos', 'thumb_key',
|
||||
"ADD COLUMN `thumb_key` VARCHAR(255) DEFAULT NULL COMMENT '视频首帧封面(存储相对键;图片为空)' AFTER `kind`");
|
||||
self::addColumn($db, 'event_photos', 'mime',
|
||||
"ADD COLUMN `mime` VARCHAR(100) DEFAULT NULL COMMENT 'MIME(视频转码后是 video/mp4;图片可空)' AFTER `thumb_key`");
|
||||
|
||||
// ---- 纪念日(版本 13,用户第 13 条)----
|
||||
// ⚠️ 名册里每个人的生日**不入这张表** —— 它们是实时从 persons 派生的
|
||||
// (见 Anniversary::birthdays),改了生日纪念日就跟着变。
|
||||
|
||||
Reference in New Issue
Block a user