v10: 修影院编辑回填布尔化 bug;评分改 10 星制;观影人改弹窗+拼音首字母联想;豆瓣自动补充迁移(movie_details.douban_meta,schema v10)
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Birthday;
|
||||
use App\Cinema;
|
||||
use App\Crypto;
|
||||
use App\Database;
|
||||
use App\Douban;
|
||||
use App\Family;
|
||||
use App\Kinship;
|
||||
use App\Movie;
|
||||
@@ -1437,6 +1438,26 @@ switch ($r) {
|
||||
// 不能凭它把别人家的成员挂进自己家的观影记录里(会顺着详情页漏人名)。
|
||||
'attendees' => array_map('intval', (array)($_POST['attendees'] ?? [])),
|
||||
];
|
||||
|
||||
// 豆瓣元数据(v10):表单隐藏字段带 JSON,解码后只认白名单键、逐字段限长,
|
||||
// 防手搓请求往库里塞垃圾。整体一个数组交给模型存 douban_meta。
|
||||
$doubanMeta = [];
|
||||
$metaRaw = trim((string)($_POST['douban_meta'] ?? ''));
|
||||
if ($metaRaw !== '') {
|
||||
$metaDecoded = json_decode($metaRaw, true);
|
||||
if (is_array($metaDecoded)) {
|
||||
foreach (['douban_id', 'douban_url', 'year', 'genre', 'runtime', 'director',
|
||||
'actors', 'plot', 'rating', 'country', 'alias'] as $mk) {
|
||||
if (isset($metaDecoded[$mk]) && is_scalar($metaDecoded[$mk])) {
|
||||
$mv = trim((string)$metaDecoded[$mk]);
|
||||
if ($mv !== '') {
|
||||
$doubanMeta[$mk] = mb_substr($mv, 0, 2000, 'UTF-8');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$d['douban_meta'] = $doubanMeta;
|
||||
$_familyPeople = Person::map($familyId);
|
||||
$d['attendees'] = array_values(array_filter($d['attendees'],
|
||||
static fn($pid) => isset($_familyPeople[$pid])));
|
||||
@@ -1467,6 +1488,22 @@ switch ($r) {
|
||||
$d[$slot === 'poster' ? 'poster_key' : 'ticket_key'] = (string)($m[$slot === 'poster' ? 'poster_key' : 'ticket_key'] ?? '');
|
||||
}
|
||||
}
|
||||
// 豆瓣抓取的海报(v10):前端抓取成功时把存储键放进了隐藏 poster_key。
|
||||
// 用户**自己选的文件优先**($_FILES['poster'] 真有文件就不覆盖);
|
||||
// 键必须是 posters/ 下的本站存储键,编辑时换用新键就顺带删掉旧对象。
|
||||
$dbPosterKey = trim((string)($_POST['poster_key'] ?? ''));
|
||||
$posterFileGiven = isset($_FILES['poster']['error']) && !is_array($_FILES['poster']['error'])
|
||||
&& (int)$_FILES['poster']['error'] === UPLOAD_ERR_OK;
|
||||
if (!$posterFileGiven && $dbPosterKey !== ''
|
||||
&& preg_match('#^posters/[A-Za-z0-9._-]+$#', $dbPosterKey)) {
|
||||
if (($d['poster_key'] ?? '') !== $dbPosterKey) {
|
||||
$oldPosterKey = $isEdit ? (string)($m['poster_key'] ?? '') : '';
|
||||
$d['poster_key'] = $dbPosterKey;
|
||||
if ($oldPosterKey !== '' && $oldPosterKey !== $dbPosterKey) {
|
||||
\App\Storage\Storage::delete($familyId, $oldPosterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 现场照片(多选,仅新增表单提供;编辑页在详情页里逐张补/删)
|
||||
if (!$isEdit) {
|
||||
$photoFiles = [];
|
||||
@@ -1570,6 +1607,102 @@ switch ($r) {
|
||||
redirect('movies');
|
||||
break;
|
||||
|
||||
// ---------------- 豆瓣自动补充(v10,MovieLog fetch_movie 同源迁移) ----------------
|
||||
|
||||
/** 片名联想 / 缩略图代理 / 抓取详情+海报入库。全部回 JSON(thumb 回图片字节)。 */
|
||||
case 'movies/douban':
|
||||
$familyId = (int)Auth::familyId();
|
||||
$act = (string)($_GET['action'] ?? '');
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['error' => '未登录或登录已过期,请重新登录后再试'], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
if ($act === 'suggest') {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$res = Douban::suggest((string)($_GET['q'] ?? ''));
|
||||
echo json_encode($res, JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
if ($act === 'thumb') {
|
||||
// 浏览器不能直连豆瓣图片(防盗链),由服务端代取;只读、不落盘
|
||||
$img = Douban::thumb((string)($_GET['u'] ?? ''));
|
||||
if ($img === null) {
|
||||
http_response_code(404);
|
||||
break;
|
||||
}
|
||||
header('Content-Type: ' . $img['mime']);
|
||||
header('Content-Length: ' . strlen($img['body']));
|
||||
header('Cache-Control: public, max-age=86400');
|
||||
echo $img['body'];
|
||||
break;
|
||||
}
|
||||
if ($act !== '') {
|
||||
http_response_code(400);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['error' => '不支持的 action'], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
// 默认:抓取详情 + 海报入库(写存储,访客不行)。JSON 错误而非 302,
|
||||
// 前端才能在会话过期/无权限时给出明确提示。
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
if (!Auth::canWrite()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => '访客账号没有写权限'], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
$idDb = trim((string)($_GET['id'] ?? ''));
|
||||
$qDb = trim((string)($_GET['q'] ?? ''));
|
||||
if ($idDb !== '') {
|
||||
$resDb = Douban::fetchById($idDb);
|
||||
} elseif ($qDb !== '') {
|
||||
$resDb = Douban::fetchByTitle($qDb);
|
||||
} else {
|
||||
$resDb = ['ok' => false, 'error' => '请输入电影名称'];
|
||||
}
|
||||
if (!$resDb['ok']) {
|
||||
echo json_encode(['error' => $resDb['error']], JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
}
|
||||
// 海报:下载字节 → poster 槽位管线(裁 2:3、缩放、转 JPEG、Storage 统一出口)
|
||||
$posterKey = '';
|
||||
$posterShow = '';
|
||||
if (($resDb['poster_url'] ?? '') !== '') {
|
||||
$bytes = Douban::downloadPoster((string)$resDb['poster_url']);
|
||||
if ($bytes !== '') {
|
||||
$tmp = tempnam(sys_get_temp_dir(), 'fbdb');
|
||||
if ($tmp !== false) {
|
||||
file_put_contents($tmp, $bytes);
|
||||
$up = Upload::image($familyId, ['error' => UPLOAD_ERR_OK, 'tmp_name' => $tmp], 'poster');
|
||||
@unlink($tmp);
|
||||
if ($up['ok']) {
|
||||
$posterKey = (string)$up['file'];
|
||||
$posterShow = \App\Storage\Storage::url($familyId, $posterKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 清掉上一次抓取但没被采用的海报(每次「自动获取」都下一张,不清就堆孤儿)
|
||||
$oldPoster = trim((string)($_GET['old_poster'] ?? ''));
|
||||
if ($oldPoster !== '' && $oldPoster !== $posterKey
|
||||
&& preg_match('#^posters/[A-Za-z0-9._-]+$#', $oldPoster)) {
|
||||
$stRef = Database::get()->prepare('SELECT COUNT(*) FROM movie_details WHERE poster_key = ?');
|
||||
$stRef->execute([$oldPoster]);
|
||||
if ((int)$stRef->fetchColumn() === 0) {
|
||||
\App\Storage\Storage::delete($familyId, $oldPoster);
|
||||
}
|
||||
}
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'source' => 'douban',
|
||||
'title' => (string)($resDb['title'] ?? ''),
|
||||
'meta' => $resDb['meta'],
|
||||
'poster_key' => $posterKey,
|
||||
'poster' => $posterShow,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
break;
|
||||
|
||||
// ---------------- 影院管理(v9,MovieLog 同款) ----------------
|
||||
|
||||
/** 影院管理单页:上面加/编表单,下面列表。GET 带参数时进入编辑态。 */
|
||||
|
||||
Reference in New Issue
Block a user