momo2026 模板开发全程实录:从零自写 MacCMS 暗色影视门户,到播放链路的连续排障

这篇接在《给 MacCMS 站点做「前台访客一键换肤」》之后。上一回是让站点能「换」模板,这一回是真正从零「写」一套模板——并把模板上线之后连着踩到的三类硬故障一并解决:播放器窗口矮到只有 156px、默认线路排序不符合预期、以及最要命的「视频明明在播、屏幕却一直转圈」。全文按时间顺序还原,每个坑都给出可直接复用的判定方法与修法。

〇、先给结论:这几个数字

262
自写模板文件数
156 → 750
播放窗口高度 px(+380%)
12
记录在案的「隐形坑」
7 / 7
转圈故障回归用例通过率

站点是一套 MacCMS v10 影视门户,跑在一台新加坡节点的服务器上,前端 nginx + PHP 8.1,模板从海螺(conch)起步。自写模板这条线从 9 月 23 日中午开始,到 9 月 24 日中午收尾,约一天时间里完成了「从零写模板 → 站点默认模板切换 → 视觉重塑 → 播放链路连续排障」的完整闭环。

一、缘起:先做事实核查,再决定「不搬运」

需求起点很直接:给了一个演示站,要求「复刻这个主题」。

这一步我没有直接开工,而是先做了一次事实核查——结果改变了整个方案:

  • 演示站跑的是 /template/hl093/,即海螺模板的 2026 新版。
  • 该模板的官方站点是会员制,而其公开模板库最新只到 HLMB-073,没有 093。
  • 本地归档同样止步于 hl073——此前批量下载的 36 个可下载模板里,最新的就是这个版本。

也就是说,hl093 并不存在公开获取途径。我最初口头断言它「在售」,属于推断而非实证,这里必须更正:后来的结论是「没有可下载的官方包」。于是方案改为——按演示站的设计语言,自写一套原创模板。

方法论:接到「照这个做」的需求,第一步不是动手,而是核查「目标物能不能拿到、拿到了合不合规」。这一次核查省下的是整条返工路径——如果先照抄再发现拿不到包,最后还是要回到自写,前面的时间就白花了。
自写的 momo2026 暗色影视门户首页
自写的 momo2026 首页:Hero 大图轮播 → 追剧日历条 → 3D 封面流热播推荐 → 分区网格

二、模板从零搭建:标签体系与 5 个隐形坑

产出是一套名为 momo2026 的暗色影视门户模板,共 262 个文件,装进站点模板目录后由换肤器自动收录。

页面体系:Hero 大图轮播(5 张 + 缩略图 + 进度点)→ 追剧日历条(7 天,今天高亮)→ 3D 封面流热播推荐 → 分区网格 → 页脚;另有分类列表(筛选 / 排序 / 分页)、详情(线路切换 + 选集)、播放、搜索、周表、榜单、专题、留言。

首轮验证:home 72 卡 / week 36 / hot 54 / new 48 / search 36,全部 HTTP 200,无 System Error,页面无残留标签。

写模板的难点不在 HTML,而在 MacCMS 的标签编译规则——它是「先编译成 PHP,再执行」,所以很多写法在源码里看着对,编译完就变了。记录在案的 5 个坑:

坑 现象 正解
标签属性不解析运行时变量 ★ 写 type="$sec.type_id" 被编译成字面量字符串,查询条件失效 先拼成实际值再进标签:type="'.$sec['type_id'].'"
注释语法 ThinkPHP 只认 {/* */},写成 {* *} 会被当正文原样输出 批量改正 23 个文件
level 过滤 首页轮播写 level="1..9",一条都不出 全站 vod_level 都是 0,去掉该条件即可
$__LIST__ 时序 在标签执行前读它判空,永远是 0 判空放到标签之后,改读 $__PAGING__.record_total
搜索页缺 pageurl 搜索永远 0 条 必须写 pageurl="vod/search";关键词在 $param.wd

另有一个语法级陷阱:{:函数($x|过滤器)} 是非法写法,编译直接产出 syntax error。需要兜底取值时用三元表达式替代:

模板标签的正确/错误写法
{* 错误:属性里的运行时变量不会解析,会被编译成字面量 *}
{maccms:vod num="12" type="$sec.type_id"}

{* 正确:先拼成实际值,再进标签 *}
{maccms:vod num="12" type="'.$sec['type_id'].'"}

{* 非法:函数 + 过滤器管道,编译报 syntax error *}
<img src="{:mac_url_img($v.vod_pic|default='')}">

{* 改用三元表达式兜底 *}
<img src="{:mac_url_img($v.vod_pic_slide ?: $v.vod_pic)}">

判定「标签到底编译成了什么」有一个极好用的办法——直接去看编译产物:

终端
# 模板标签最终变成了什么条件,看编译缓存最直接
grep listCacheData runtime/temp/*.php

# 改完模板 PHP 一定要清编译缓存,否则读到的还是旧结果
rm -rf runtime/temp/*.php

三、站点开启 + 访客门禁,两件事要能共存

站点上线要「正式开启」,同时保留访客门禁。看似简单,实际踩了一个结构性的坑。

原实现把密码校验塞在维护模式分支里(site_status==0 的 check_site_status())。问题在于——站点一开启,这个分支就永远不走了,门禁随之失效。

修法是让门禁独立于维护模式:新增 check_site_gate(),挂在控制器构造函数里、label_user() 之后、check_force_login() 之前;并在 site_status==0 时直接 return,与维护分支互斥,避免双重门禁。

放行条件 说明
已登录会员 正常放行
session('site_pwd_verified') 输对通行密码后打上的标记
user@login|oauth|logincallback 登录页自身必须可达,否则死循环
非 index 入口(如 api.php) 给采集/对接留通道

未通过则输出统一提示页。这里有一个容易被忽略但很关键的点——缓存安全:页面缓存在 label_fetch()(action 阶段)读写,而门禁在构造函数里就终止了请求,匿名访客根本碰不到缓存副本,所以不需要额外做防穿透。

验证:匿名访问首页、分类页、详情页,均返回「访问验证」标题;错密码返回 code=0;正确密码返回 code=1;登录页 200;接口入口放行。

四、首页「追剧日历」全是空壳:id 与 include 变量错位

反馈是:首页追剧日历只显示「周几 + 即将更新」的框架,影片卡全是空壳(有格子、没内容);热播推荐 3D 流也不自动切换。

先说结论——查询本身完全正常,问题在模板变量名对不上。

主因:html/index/week.html 里标签写的是 id="wv",但它 include 的子模板取的是 $vo.xxx —— 变量根本不存在,于是循环照跑、卡片照出,只是每个卡片的链接、名字、图片全空。同一个 copy-paste 错误在另外 3 处也存在(详情页与播放页的相关推荐、榜单页 TOP6),共 4 个文件。

修复是统一的:把 id 改回 id="vo" key="vk"。

至于「轮播不切换」——JS 逻辑本身没问题(无头浏览器实测 4.2s 自动轮转正常)。真实原因是浏览器缓存了旧版 app.js(当天改过文件,但引用的版本号没变)。顺手还修了一个真 bug:圆点容器在轮播包裹层之外,而查询用了限定作用域的写法,导致圆点高亮永远不亮——改成全局查询即可,并顺手把版本号推进一位强制刷新。

方法论:「卡片是空的」是模板里最阴的一类 bug。它不像报错那样显眼——循环照常执行、卡片数量完全正确、页面布局毫无异常,只有内容是空的,肉眼扫一眼很容易漏过。批量检测思路:把所有「标签 + include 子模板」的段落抽出来,逐一比对子模板声明的入参变量名是否一致。

五、用户中心整备:42 个 404 与一条断掉的深色链路

反馈是「用户中心界面比较乱」。排查下来是三层问题叠加,逐层拆:

第一层:资源全缺。用户中心目录当初是从官方默认模板整目录拷来补齐的,但只带了 HTML、没带配套资源——页面引用的 42 个 css / js / 图片全部 404,布局样式彻底失效,这就是「乱」的直接来源。修法是按文件粒度建软链,把默认模板的样式资源目录链过来,共 533 个链接,且只补缺失、不覆盖模板自带的样式。404 从 42 降到 1(残留那一个,官方默认模板自己也缺)。

第二层:深色链路断。默认深色是靠 body 上的一个类名驱动的,但在这个模板下,三段链路全没接上:页面头部的主题判定、全局模板颜色配置、以及主题小部件里后执行的默认模式设置——最后执行的赢,于是深色被覆盖。修法是在两处写死默认深色,不动全站共用的配置(那个配置被所有模板共用,改它会连带影响线上正在跑的主题)。

第三层:运行时 body class 被清空。这是「class 明明写了却不生效」的真凶。页面内联脚本先把 body 的类清空,随后另一个脚本又补了一个语言类,结果布局类和主题类全丢。这个用打调用栈的方式才定位到——挂上 classList / setAttribute 的钩子看是谁改的。另外默认模板的样式里还有一处硬编码浅色背景且没有深色变体,在用户中心注入覆盖规则解决。

验收用的是「对比度巡检」——把渲染后文字色与背景色的对比度低于 3 的位置全部找出来(低于 3 基本等于不可读):

页面 修复前 修复后
用户中心 / 登录 / 注册 / 找回密码 18 / 2 / 1 / 0 处低对比度,整片白底 0 处(深色生效,深底 rgb(22,21,27))
充值页 35 处 2 处(绿底白字按钮,属设计本身)
播放记录 35 处 2 处
静态资源 404 42 个 0(用户中心)/ 1(模板自身缺失)
momo2026 影片详情页
详情页:线路切换 + 选集 + 相关推荐(就是这里曾因变量错位导致推荐卡全空)

六、全维度视觉美化:从「能用」到「赏心悦目」

功能齐了之后做了一轮整体视觉重塑。改动集中在四件事:

  • 设计令牌与氛围底色:底色重置为深邃通透的深黛夜空,叠加三层弥散极光雾光与磨砂玻璃质感。
  • 文本对比度:把原本看不清的弱化灰字提亮到 Slate-400 一档,主字提亮到接近纯白,解决暗色下久看疲劳。
  • 卡片质感:全站卡片与封面统一圆角,边框加一道超细微光线,悬浮轻微抬升并配弥散柔光投影;播放遮罩升级为渐变脉冲圆钮。
  • 播放页沉浸光影:为播放器容器注入外溢的环境光,选集胶囊升级为圆角流光高亮。

这一轮里最有价值的是一个移动端重大缺陷的修复:手机端(390px 视口)详情页与播放页横向溢出到 583px,页面能左右拖动。根因是网格项默认的 min-width: auto —— 内容撑破了容器。重置为自适应两列后,scrollWidth 严格等于 390px,零溢出。

momo2026 移动端首页
移动端首页:390px 视口下零横向溢出

七、播放器窗口太小了:先读行业标杆代码,再动手

反馈很直接:「视频播放器的窗口太小了吧,看看最新最热的播放器代码」。

先量了一下——实测播放窗口高度只有 156px,在一台 1440×900 的屏幕上几乎是一条缝。定位到根因:此前那轮视觉美化的样式替换用的是非贪婪正则,匹配区间从播放器容器一路吃到控制栏,把中间定义 16:9 比例和绝对填充的两条规则一起删掉了——容器失去比例约束,高度就塌回到默认行内高度。

接着去读了生态里几套主流模板的播放器实现,对比之下就很容易判断「现代播放器该长什么样」:

模板 比例实现 交互特性
海螺(生态里最流行) Bootstrap 式 embed-responsive-16by9,用 padding-bottom:56.25% 撑高,并由 JS 按 PC/移动端分别注入配置值 9/12 宽度分栏 + 报错 / 刷新 / 上一集 / 下一集快捷条
奈飞(颜值代表) 同样是 embed-responsive-16by9 独立全屏切换按钮 + 右侧浮动推荐 / 二维码面板
官方默认 传统固定纵横比响应式垫片 基础控制条

共性很清楚:都必须有明确的 16:9 比例约束。所以我这里没有照抄 Bootstrap 垫片方案,而是用现代的 aspect-ratio,并加上一层「视口双保险」——下限保证大屏沉浸感,上限保证不超出视口产生纵向滚动:

模板样式表 · 播放器核心视窗
/* 核心视窗:16:9 黄金比例 + 视口双保险 */
.player-box .stage {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  min-height: 480px;                  /* 大屏沉浸感下限 */
  max-height: calc(100vh - 150px);    /* 不超出视口,避免纵向滚动 */
  background: #000;
  overflow: hidden;
}

/* CMS 注入的播放器与 iframe 一律满幅绝对定位 */
.player-box .stage .MacPlayer,
.player-box .stage video,
.player-box .stage iframe {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  border: 0 !important;
}

效果:PC 端播放窗口从 156px 变成 750px(正好吃满「900 − 150」的上限),宽度 1374px。在此基础上补了三个现代播放器该有的能力:

  • 剧场模式:一键把播放器摊成 100vw 巨幕(实测 1440×780),并把这个偏好记到本地存储,翻页后保持。
  • 关灯模式:铺一层 94% 的暗场遮罩,播放器提权到最上层并加一圈聚焦光晕,做出电影院的沉浸感;再点遮罩或按钮即柔和退灯。
  • 播放器单点刷新:第三方解析线路卡顿时,只重载内层 iframe,不刷新整个页面、不丢当前选集。
播放器标准模式,修复后的大画幅
标准模式:播放窗口 1374×750,比修复前的 156px 高出 380%
剧场模式
剧场模式:容器摊到 100vw,高度 780px,偏好写入本地存储
关灯模式
关灯模式:94% 暗场遮罩 + 播放器提权与聚焦光晕

移动端则单独保底:强制重置视口上限,保持自适应比例,横向滚动严格为 0。

八、播放线路默认排序:让 m3u8 排在前

需求是「播放器的顺序默认 m3u8 在前」。这件事看起来只是改个排序值,实际有个陷阱。

先看机制。播放线路列表由 mac_play_list() 生成,它只收录状态为启用的线路;当排序开关打开时,会按播放器配置里的排序值倒序重排。

站内数据有个很规整的规律——几乎都是「普通播放器 + 同名 m3u8」成对出现,例如 liangzi$$$lzm3u8、feifan$$$ffm3u8、dytt$$$dyttm3u8。而原先每一对里,非 m3u8 的排序值反而更高,所以默认落在普通播放器上。

第一层改动很直白:把排序值重排,让所有 m3u8 线路整体优先(顺带修掉一个排版里混进来的异常小值)。

但只改排序是不够的——这是我事先没预料到的地方。因为这条线路列表只在详情页生成,列表页的数据里根本没有这个字段,模板里取到的线路号是空的,于是全站大量入口(首页幻灯片、热门区、列表页的播放按钮、海报点击、旧书签、订阅)都硬编码回落到第 1 条线路。结果就是:切换线路的标签页上 m3u8 排第一,但点「立即播放」进的却是另一条线路。

所以第二层改动才是关键:在 mac_play_list() 排序之后,把线路号重新编号为 1..n,让「优先级最高的线路」恒等于 1 号。这样全站所有指向 1 号的入口一次性对齐,不用去逐个改模板。同时加了「空线路沉底」——没有剧集内容的线路排到最后,避免默认落到一条空线路上。

application/common.php · 排序 + 线路号重编号
// ① 排序键:先按「线路是否有剧集」沉底,再按播放器排序值倒序
$valid = [];
$tmp   = [];
$res_list = array_values($res_list);
foreach ($res_list as $v) {
    $valid[] = empty($v['urls']) ? 0 : 1;   // 空线路 = 0,排到最后
}
if (!empty($sort)) {
    array_multisort($valid, SORT_DESC, SORT_NUMERIC,
                    $sort,  SORT_DESC, SORT_FLAG_CASE,
                    $res_list, SORT_ASC, SORT_REGULAR);
}

// ② 关键一步:重编号线路号,让优先级最高的线路恒为 1
//    全站所有指向 sid=1 的入口(首页/热门/列表/海报/旧书签)自动对齐
$i = 1;
foreach ($res_list as $v) {
    $v['sid'] = $i;
    $tmp[$i] = $v;
    $i++;
}
$res_list = $tmp;

验证覆盖了站内 5 种不同的线路组合,结果全部是 m3u8 排在首位,「立即播放」与所有指向 1 号的入口均落在 m3u8 线路上。

九、视频「一直转圈」:一次三层修复(最硬的一仗)

这是整段里最难的一个,因为第一直觉是错的。

反馈是「视频播放不了一直转圈」。按常规思路应该去查播放器、查线路、查地址。但我先做了一件不太一样的事——量化验证「视频到底有没有在播」:

观测项 实测值 含义
video.readyState 4(HAVE_ENOUGH_DATA) 数据充足,播放器已就绪
视频时长 2776 秒 清单已解析,时长正常
currentTime 推进 3.47 → 8.66 → 16.76 秒 视频其实一直在正常播放

也就是说——播放没有失败,是有一层东西把画面盖住了。于是换个问法:屏幕上那个位置,最上层到底是谁?用 document.elementsFromPoint() 取屏幕中心的层叠链,答案立刻就出来了:

屏幕中心 (720,460) 的层叠链,自顶向下
IFRAME#install  <  IFRAME#buffer  <  IFRAME  <  TD#playleft

#buffer  display=block  z-index=99998  pointer-events=auto
        src = //union.maccms.la/html/prestrain.html   (301 → union.dplayerstatic.com)
视频在播但被第三方加载遮罩盖住
故障现场:画面全黑 + 中央一直转圈,而这层转圈其实来自第三方的加载页 iframe

根因链是这样的:

  1. CMS 原生播放器脚本会在播放器之上再叠两个 iframe(#buffer 与 #install),其中 #buffer 指向一个第三方加载页,就是那张「加载中」转圈图。
  2. 本站用的是自定义播放器壳(按地址形态选壳:直链走播放器页、网页直接内嵌),壳自己设置内容后走的是自定义流程,原生那两个遮罩不会被自动摘除。
  3. 自定义播放器页本来是靠 postMessage 向父页上报「就绪 / 失败」的,而父页从来没有监听这个消息——整条链是断的。
  4. 为什么现在才暴露:原先默认线路是资源站的分享页(普通内嵌),这次按需求改成 m3u8 优先、走自定义播放器壳,才把这个历史缺陷翻了出来。这两件事是同一条时间线上的因果。

修复分三层落地:

层级 改动
配置层 把「加载页 / 缓冲页」两个地址从第三方域名改成同源的本地加载页
缓存层 同步更新前端那份播放器配置缓存,否则旧第三方地址继续生效
页面层 播放页注入「遮罩治理脚本」:立即并持续压制两层遮罩、监听播放器上报(就绪即撤遮罩 / 失败则提示并自动切换下一条线路)、并用观察器常驻自愈

页面层这段脚本是整场排查的技术核心,值得完整贴出来:

播放页 · 遮罩治理脚本(节选)
var OVERLAY_IDS = ['buffer', 'install'];

// 幂等且自愈:以「计算样式」判断,只有确实还露着才重写
// 必须带 !important —— 第三方脚本会用内联样式覆盖并丢掉优先级,
// 而页面 CSS 里另有更高优先级的规则
function killOverlays() {
  for (var i = 0; i < OVERLAY_IDS.length; i++) {
    var el = document.getElementById(OVERLAY_IDS[i]);
    if (!el) continue;
    var cs = window.getComputedStyle(el);        // ← 关键:读计算样式
    if (cs.display !== 'none') {
      el.style.setProperty('display', 'none', 'important');
    }
    if (cs.pointerEvents !== 'none') {
      el.style.setProperty('pointer-events', 'none', 'important');
    }
    if (cs.zIndex !== '1') {
      el.style.setProperty('z-index', '1', 'important');
    }
  }
}

// ① 立即清一次;前 20 秒轮询兜底;观察器拦截第三方脚本的重新显示
killOverlays();
var ticks = 0;
var guard = setInterval(function () {
  killOverlays();
  if (++ticks > 80) clearInterval(guard);
}, 250);

// ② 播放器上报:source=mac-player,type=play_ok / play_fail
window.addEventListener('message', function (ev) {
  var d = ev && ev.data;
  if (!d || d.source !== 'mac-player') return;

  if (d.type === 'play_ok') { killOverlays(); return; }

  if (d.type === 'play_fail') {
    killOverlays();
    // 自动切到下一条线路,而不是让用户干等
    var links  = [].slice.call(document.querySelectorAll('.playlist-box .lines a'));
    var cur    = document.querySelector('.playlist-box .lines a.on');
    var target = links[links.indexOf(cur) + 1];
    if (target) location.href = target.getAttribute('href');
  }
});
本次最费时的坑,也是最有价值的一条经验:判断「遮罩是否已经隐藏」,必须用 getComputedStyle() 读计算样式,不能用元素的内联样式值。因为第三方脚本会把内联的 display:none 重写并丢掉 !important,而 CSS 里另有更高优先级的规则在生效——于是出现「内联读出来是 none、实际计算出来还是 block」的诡异状态,代码误判为「已隐藏」就不再重写。
改用计算样式判断之后,逻辑同时变得幂等,顺带解决了另一个坑:观察器被自己的改动反复触发,形成无限循环。中途出现过一次「加固版反而全量失效」,就是这个原因。

回归验证 7/7 全通过(5 部影片 × 2 条线路):两层遮罩的计算样式均为 none、屏幕中心最上层是真实播放器、影片均处于播放中;并且原有那条分享页线路没有回归。同时覆盖了 iPhone Safari、Android Chrome 与微信内置浏览器三种移动端 UA。

十、复盘:可复用的方法论与坑清单

把这一天的经验压缩成几条可直接套用的规则:

  1. 先核查可获取性,再决定方案。「照这个做」的第一件事是确认目标物能不能拿到、拿了合不合规。
  2. 判断元素真实状态,读计算样式;读内联值会被第三方脚本骗。这是本次最贵的一课。
  3. 排查前台问题前,先确认「生效模板到底是哪个」。站点配置里可能还挂着别的模板,改了半天改在不生效的那套上。
  4. 只改显示顺序往往不够,要顺着「默认值从哪来」一路查到入口。线路排序就是这样:改了标签顺序,全站入口还在用硬编码的 1 号。
  5. 「内容全空但循环正常」是模板最阴的 bug——卡片数、布局都没错,只有内容是空的,必须专门比对变量名。
  6. 每次 CMS / 主题升级之后,都要重跑一次播放层修复。升级会静默还原自定义播放器壳,表现为「突然又不能播了」,而报错日志里什么都没有。

坑清单汇总:

# 坑 判定 / 修法
1 标签属性不解析运行时变量 看编译缓存确认条件;先拼值再进标签
2 注释语法只认一种写法 页面出现原样输出的注释字样即中招
3 等级过滤条件与数据不符 先查库里该字段的实际分布,再决定要不要过滤
4 判空读到的是标签执行前的值 判空放到标签之后,读分页对象里的总数
5 搜索页缺分页地址参数 搜索恒为 0 条时优先查这里
6 标签与子模板变量名错位 卡片全空但数量正常——比对 include 入参
7 运行时 body 类被脚本清空 钩住类名操作打调用栈
8 宽高比规则被样式替换误删 容器高度塌成一条缝时,先找比例约束还在不在
9 网格项默认最小宽度撑破容器 移动端能左右拖 = 查 scrollWidth
10 「在播但被遮罩盖住」被误判为「播放失败」 先量进度有没有推进,再查屏幕中心最上层是谁
11 用内联样式判断隐藏状态 改读计算样式,同时获得幂等性
12 升级静默还原自定义播放层 把修复脚本化、幂等化,升级后固定重跑

附:这次动到的文件与回滚点

所有生产改动一律「先备份、再覆盖、后验证」,备份文件名带改动语义与时间戳,出问题可以按语义精确定位回滚:

改动 涉及文件 备份后缀
自写模板 模板目录下 262 个文件(新增,无覆盖) —
门禁独立化 控制器基类、站点配置 .bak-gateopen-*
变量错位修复 首页周表、详情、播放、榜单 4 个模板 同目录备份
用户中心整备 用户中心 28 个页面、主题判定与颜色配置 .bak-dark*-*
视觉美化 模板样式表 .bak-beautify-*
播放器窗口修复 模板样式表、播放页 .bak-player-*
线路排序 公共函数库、播放器配置 .bak-sidrenum-* / .bak-sort-*
转圈故障修复 站点配置、前端播放器配置缓存、播放页 .bak-overlay-*

说明:文中所有服务器地址、访问口令、数据库凭据均已隐去或不含具体值;涉及线上数据的数字均为实测值。

2026-09-24 增补:AI 搜索前端(仿 kanju.ai)与 L 站登录修复

本日围绕「前端 AI 的使用」对搜索链路做了整备,并顺带修复 L 站 OAuth 自动注册的两个阻断性缺陷。所有改动依旧遵循「先备份、再覆盖、后验证」。

1. 搜索结果页 AI 速答条(仿 kanju.ai 设计语言)

  • 搜索结果页新增 AI 速答条:状态药丸(打开中/已完成/失败)→ 概要行(AI 找到 N 部相关影片)→ AI 文字解读(编号列表可读化排版)→ 横排海报卡(左海报右信息,绿色「查看」按钮)→ 底部「结果不满意?」操作区(站内搜索定位 + 去 AI 对话追问)。
  • 零搜索结果时自动触发一次速答:站内搜不到的片名,AI 会扩展检索词并从片库捞出相关影片。
  • 后端错误翻译为友好降级文案(未登录/需验证/额度用尽/熔断维护),任何失败都不影响原搜索功能。

2. 「问AI」开关三端打通

  • 搜索中心、搜索结果页、站点头部三处共用同一份 localStorage 开关(mac_search_hub_ask_ai),任意一处切换处处同步。
  • 开启后搜索/回车直达 AI 对话页并自动提问;关闭走普通搜索。
  • 修复搜索中心 AI 提示行挤压定高面板导致「问AI/搜索」按钮行被裁掉的问题(提示行移到搜索栏挂载,状态挂 body 类)。

3. 头部搜索框集成 ✦ 开关

  • 独立的「问AI」文字胶囊移除,✦ 开关收进搜索框内部(放大镜左侧),节省头部空间;独立入口在移动端保留为 30px 纯图标兜底(移动端搜索框本就隐藏)。
  • 顺带修复 app.js 的 searchSync 回车处理会覆盖 AI 跳转的问题(两边逻辑统一为 AI 感知)。

4. 搜索中心桌面端升级

  • 修复热榜海报全量破图:懒加载链在本页失效(data-original 有值、src 为空),监听列表渲染直接补真实封面,加载失败给暗色占位。
  • 桌面端容器 696px → 880px,热榜改双列卡片、海报 52×74 → 76×106,深色主题标题提亮。
  • 补上默认模板缺口:bstem 深色模式下主区背景只在 ≥821px 媒体查询覆盖,移动端出现白底浅字(文字隐形)。

5. L 站 OAuth 自动注册修复(三个叠加缺陷)

  • 同 IP 每日注册限额误伤:后台 reg_num=1 对第三方注册同样生效,共享代理出口 IP 的第二个新用户当天必然失败 → 第三方 OAuth 身份已由 L 站平台验证,予以豁免。
  • 账号名不足 6 位:openid 为短数字串时派生用户名不过 min:6 校验(报错「名称最少不能低于6个字符」)→ 优先用 L 站用户名(仅留字母数字)生成,不足 6 位回退 openid,再不足补随机数字。
  • 撞名:派生名与已有账号冲突时自动加随机后缀重试一次。
  • 注册失败时错误信息透出真实原因,不再一律显示「请联系管理员」。
  • 实测:普通注册仍被 reg_open=0 正确拦截;两个同 IP 的模拟 L 站注册连续成功(修复前第二个必挂)。

6. 用户中心管理后台入口

  • 个人中心侧栏(PC + 移动端双导航)新增「管理后台」绿色盾牌入口,指向后台入口文件。
  • 仅 mac_admin 表中 admin_status=1 的账号可见(普通会员实测 0 处渲染);后台自身仍有独立登录校验,隐藏入口只是体验层。
改动范围 备份后缀
搜索结果页 / 搜索中心模板、AI 速答条全套 .bak-aiopt-*
头部搜索框 AI 集成(head.html / ai-entry.css / app.js) .bak-aiint-*
L 站 OAuth 注册(控制器 + 用户模型) .bak-oauth-* / .bak-oauth2-*
用户中心管理后台入口(left_nav) .bak-admin-*

说明:AI 后端沿用站内 ajax/ai_chat(new-api 网关,deepseek-flash),本日改动全部为前端叠加层与少量控制器/模型修复,未动核心框架。

2026-09-25 增补:后台菜单瘦身 + 暗色极光主题

  • 后台菜单瘦身:按数据库实况(相关表 0 行)移除从未使用的模块入口——整组扔掉文章(含漫画)、网址导航、静态生成;子项扔掉专题、友链、评论、聊天室、弹幕、演员、角色、充值卡、金币商城、提现、优惠券、秒杀、AI 注解、Meilisearch、域名绑定、模板市场、URL 推送。菜单分组 15→11,可见功能项 98→74。控制器文件保留未删,随时可从备份恢复。
  • 后台主题:曾短暂切换为暗色极光风,应用站长偏好已恢复默认绿色风格(覆盖层 CSS 保留在服务器备用,未链接)。

2026-09-25 增补(二):功能集成日——AI 搜索落地、友链体系、大屏剧照与 Hermes 自动化

  • L 站注册修复(第二段):错误透出后定位到「账号名不足 6 位」——L 站 openid 为短数字串时派生用户名过不了 min:6 校验。修复:账号名优先取 L 站用户名(仅留字母数字),不足 6 位回退 openid,再不足自动补随机数字;配合此前的撞名重试与额度豁免,L 站新账号注册链路完整打通。
  • 头部搜索框集成 ✦ 开关:独立的「问AI」文字胶囊移除,✦ 开关收进搜索框内部(放大镜左侧),开启后回车直达 AI 对话;移动端保留 30px 图标入口兜底。三处开关(头部/搜索中心/结果页)共用同一状态。顺带修复 app.js searchSync 回车处理覆盖 AI 跳转的问题。
  • 用户中心管理后台入口:个人中心侧栏(PC+移动)新增「管理后台」绿色盾牌入口,仅 mac_admin 表 status=1 的账号可见(后台自身另有独立登录校验)。
  • 友链体系完整化:① 回加后台「友情链接管理」菜单;② 修复一个影响面更大的缺陷——后台基类统一声明 view_new 视图路径。此前 Link/System 等未声明控制器的页面全部渲染空白(旧 view 目录几乎只剩 4 个模板,TplConfig 的主题设置页也是同因空白);③ 博客「momo的存货」与 L站(linux.do) 加入友链;④ 前端 UI 胶囊化:圆角 chip + 悬停青色发光上浮 + 首字母渐变头像(m 青/L 金交替),支持友链 logo 字段优先显示图片。
  • 大屏海报高清化:首页轮播原直接用竖版封面硬裁,观感差。搭建豆瓣剧照管线——条目匹配(标题+年份双校验) → 移动端剧照接口取 1600px 级横版物料 → 下载到本地 upload/vod/slide/ → 写入影片「幻灯图」字段(轮播模板本就优先读取)。当前 5 部轮播片全部换装(1620×1080 级,一瓯春 3375×1080 超宽)。爱奇艺/腾讯搜索页为 JS 壳+接口签名,服务器端直抓不可靠,故选豆瓣官方物料(即各平台分发用的同源图)。
  • Hermes 自动化:管线封装为 Hermes 技能 momo-hero-slides(动态识别首页轮播片,无需维护片单;幂等跳过已有剧照;支持 –name/–force)。注册每日 04:30 定时任务(增量采集之后),抓到新图推送 Telegram,无新图静默。
  • 热榜轮播间隔可配置:后台「momo2026 主题设置」新增「轮播间隔」字段(hot_delay,默认 2000ms,0=只手动切换,悬停暂停),封面流自动播放从写死 4200ms 改为配置下发。
改动范围 备份后缀
头部搜索框 AI 集成(head.html/ai-entry.css/app.js) .bak-aiint-*
L 站注册账号名派生(控制器) .bak-oauth2-*
用户中心管理后台入口(left_nav) .bak-admin-*
友链管理菜单回加 / 快捷菜单(auth.php/quickmenu.php) .bak-trim-* / .bak-quickmenu-*
后台视图路径统一(Base.php) .bak-viewpath-*
热榜轮播间隔(app.js/hot.html/TplConfig/momo.html) .bak-hotdelay-*

说明:大屏剧照管线已封装为 Hermes 技能 momo-hero-slides(每日 04:30 自动运行),轮播片单变化后无需人工补图。

2026-09-25 增补(三):分类页筛选全面修复与补全

  • 根因:分类页的筛选从来没真正生效过——MacCMS 的 mac_url 在生成 vod/type 链接时只保留 id 和 page 两个参数,其余筛选参数(年份/排序等)全部丢弃,点击筛选按钮后 URL 里无参数、列表永不过滤。这是核心 URL 生成层的行为,模板正常调用无法绕过。
  • 修复:在分类页模板内置筛选 URL 构造器——以干净的基础类型 URL 为底、手工拼接筛选查询串(?year=2026&area=大陆&lang=国语&by=hits_month)。列表查询(paging="yes" + mac_param_url)原生支持这些参数,断点只在 URL 生成一环,故无需改核心代码。
  • 维度补全:筛选从 2 维扩到 4 维——分类、年份、地区(新增)、语言(新增)。地区/语言选项不是写死清单,而是按当前顶级分类的实际数据聚合(出现次数降序),有内容的才出现。
  • 联动互持:各维度切换时保留其余已选条件——选「大陆」再选「2026」,地区条件保留。
  • 实测:电影片页 7204 部 → 地区=大陆 1434 部 → 加年份=2026 剩 32 部,URL 参数正确、选中态高亮。

备注:搜索结果页目前未加筛选区,如需要可复用同一套构造器。

2026-09-26 增补(四):首页轮播全是老片——采集刷新与推荐级治理

  • 现象:首页 Hero 轮播清一色 2009–2021 的老片(官能的法则 2014 / 处处有贼 2012 / 澳门风云 2014…),而站点明明每天都有新片入库。
  • 排查:模板 → 标签 → 配置 → 数据库逐层下钻,最后用「短暂切维护模式换会话、真实渲染首页」的方式拿到实测结果。轮播取片规则是 hero_by=time(vod_time 最后更新时间)倒序取前 5,而这些片的 vod_time 恰好全是当天凌晨 01:35——一批刚被采集任务「碰」过的老片。
  • 根因:vod_time 在每次采集命中时都会刷新。7 个资源站的定时任务每小时拉「最近 24 小时更新」,资源站会持续把库内老片重推进更新流,于是「最后被采集器碰过的片」永远是轮播候选,与影片真实年份彻底脱钩。顺带证实:全站 hits 系列全是采集时随机生成的 1–1000 假数据,换「热度」排序同样救不了轮播。
  • 修复:轮播两个 vod 标签加双重过滤——level="9"(推荐级·幻灯,人工可控) + year="2024,2025,2026"(年份兜底)。MacCMS 的 Vod 模型原生支持这两个参数(vod_level IN / vod_year IN),零核心改动。
  • 一个反转:正文第二节记过「轮播写 level 一条都不出,去掉即可」——本质是当时全库 vod_level 全 0、没有数据。这次反过来操作:先批量给 15 部 2026 新片打上推荐 9(vod_level=9),再启用 level 过滤,同一个参数从「弃用」变成「治理入口」。另有一个细节:level 过滤一生效,可见性检查标签也查不到数据,轮播区块会整个隐藏而非渲染空盒——这反而是想要的优雅降级。
  • 实测:渲染结果 5 张幻灯全部变为 2026-09 新片(甲武神 / 未尽之言 / 反恐特警组:放逐者 / 分道扬镳 / 我家那闺女2026),老片清零。运营语义从此清晰:后台把影片推荐级别设为「9-幻灯」即上轮播,改回 0 下架。

备注:追剧日历里的老剧属正常语义(它本就是「今天有什么在播」的日程表);热榜(hot_by=hits_week)还在吃随机假热度,后续可用同一套「推荐级+年份」过滤治理。回滚点:slide.html.bak-level9-20260926。

2026-09-26 增补(五):轮播图很模糊——高清剧照管线从静默故障到 TMDB 双源兜底

  • 现象:增补(四)换上来的新片单,轮播 5 张里 4 张很模糊。原因:这些片的 vod_pic_slide(幻灯图)字段是空的,模板回退用采集来的竖版小海报(约 600px 宽)全屏拉伸,自然糊成一片。
  • 既有管线找到时,它已经静默坏了:服务器上本来就有一套「高清横版剧照自动抓取」——root 侧 Hermes 技能 momo-hero-slides,每日 04:30 运行、结果投递 Telegram。查执行记录:当天 04:30 确实触发了,last_status: error, Script exited with code 1。根因是它的第一步设计为「匿名抓首页 HTML、解析当前轮播片名」,而站点凌晨刚开启强制登录,匿名首页只剩 302 跳转——脚本抓不到内容直接退出。更隐蔽的是包装脚本采用静默模式,只在输出含 ✓/✗ 时才投递,这条不含两者的报错被完全吞掉,Telegram 无感知。教训:依赖前台页面状态的定时任务,会随着站点门禁策略变化而集体静默失效。
  • 脚本改造:选片不再抓页面,直接查库——level/year 过滤条件用正则从 slide.html 里提取,数量与排序读 mctheme_momo.php 的 hero_* 配置,与模板永久同源,对登录态彻底免疫;豆瓣剧照接口取图量从 12 张提到 50 张。
  • TMDB 双源兜底:豆瓣无横版剧照时自动走 TMDB——search/multi 按片名+年份加权匹配,取 w1280 backdrop。api_key 从 maccms.php 配置读取(实测时还发现该配置的父级层级与预想不同,第一版脚本因此读空,修正后才生效——读配置前先 var_export 确认真实路径,别靠记忆)。
  • 密钥生成:TMDB 注册账号 → Developer 计划申请(个人用途认证勾选 + 应用信息表单) → 生成 v3 key 配置入库。全程浏览器自动化完成,注意其表单的电话号码是必填项,且提交按钮的 click 事件需要用页面脚本触发(常规定位点击会超时)。
  • 结果:15 部打标影片 11 部拿到高清横版剧照(豆瓣 8 张 1080p 级 + TMDB 3 张 1280×720);4 部国漫/网剧两边都查无收录,暂撤出轮播。实测渲染:轮播 5 张全部为 1270–1620px 宽的本地剧照,模糊清零。

备注:方法论上这一轮的收获是「定时任务的可观测性」——静默失败比失败更贵,包装层的输出过滤把唯一的报错信号也滤掉了;改造后选片与模板同源、报错逐片可见。脚本最终版已备份进 server-scripts 仓库。同日顺手把热榜从 hot_by=hits_week(随机假热度,老片钉死)改为 time——至此首页全板块(轮播/追剧日历/热榜/分区/专题)统一按最新更新时间排列,配置改动,模板零改动。

2026-09-26 增补(六):年份过滤从轮播推广到首页全板块

  • 问题:分区列表(电影片/连续剧/综艺片…)虽然按 by=time 排序,但排在前面的依旧全是 2014–2021 老片——「最新更新」排在最前,而最新更新恰恰是采集器刚重推过的老板,和轮播当初是同一个病:排序治不了数据源的刷新噪音。
  • 推广治理:把轮播验证过的 year="2024,2025,2026" 过滤推广到全部板块——list.html(分区 rail)、hot.html(热榜,2 处标签)、week.html(追剧日历)。改前用 SQL 预检了 2024+ 影片的星期分布(898–5567 部/天),确认日历 7 天不会空。
  • 结果:渲染实测,6 个分区 + 热榜全部变为 2024+ 新片且按最新更新排列(电影片:黑箱日记/互帮互助/海岛巨蟒…;连续剧:夺宝天团第二季/宛如阿修罗2025…),此前 12 部样本老片在首页零残留。
  • 边界:追剧日历与分区列表里可能出现「年份新但内容旧」的片(如 2025 年上架的经典重映),这是过滤语义的天然边界;若想更严格可在年份之上再叠推荐级,但内容量会骤减,现阶段不建议。

备注:至此 MacCMS 的「时间字段被采集刷新」问题形成完整解法——轮播(推荐级+年份)、板块(年份)、排序(全部 time)、配图(双源高清剧照管线)。模板改动已推送 GitHub。同日稍晚:预检各分类 2026 年库存(连续剧 514/电影片 389/动漫片 321/综艺片 249/体育 72/解说 137)后,把年份过滤统一收紧为 year="2026",首页所有板块只显示当年内容;轮播原样(精选片单本就是 2026 片)。

2026-09-28 增补(七):手机版个人中心重叠——两代头部系统的叠层打架

  • 现象:手机版会员中心,「Personal Center」标题和积分行被站点导航条盖住,人物卡整体顶进导航底下。
  • 排查:用测试账号种合法登录 cookie + 手机视口(390×844)真实渲染截图复现;逐层测量发现内容根容器渲染位置比文档流预期高约 130–280px,且随宽度变化。逐个禁用样式表二分定位,最终锁定 legacy user-head-early.css 在 ≤820px 的沉浸式头部设计——人物卡 .tou { margin-top:-3.7rem } 上拉、假设顶部导航透明。
  • 根因:momo2026 模板自带另一套头部(sticky .site-head),两代头部系统叠加后,负 margin 沿 .tou→#left→#member→#mac-user-info-root 逐级外边距塌陷,把整块内容顶进「固定 54px 滚动感知导航 + sticky 66px 站点导航」双层导航条底下。计算值全部正常(0/12px),只有真实渲染几何暴露问题——这类叠层 bug 用 computed style 排查会一无所获,必须量 getBoundingClientRect。
  • 修复:移动端归零 .tou 负 margin,内容恢复从导航下方流式排列;user-center.css 版本参数升级强制刷新缓存。实测 390–800px 全部宽度内容稳定落在导航下 12px。
  • 方法论:先复现、再二分、后归因。逐个禁用样式表把 173KB 的 legacy CSS 嫌疑范围收窄到一条规则,比通读 minified 源码快一个数量级。

备注:排查用的临时种 cookie 跳板页已删除;修复随 user-center.css 推送 GitHub(8315f72)。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容