restore gpt seo copy pipeline and stabilize seo tkd output
This commit is contained in:
@@ -26,8 +26,10 @@ class SeoCopyFallbackBuilder
|
||||
{
|
||||
$strSiteName = self::siteName($arrFacts);
|
||||
$strScopes = self::joinItems((array)($arrFacts['content_scopes'] ?? []), 3, '影视内容');
|
||||
$strLanding = $strSiteName . '首页当前覆盖' . $strScopes . '等常见内容入口,适合先看首屏推荐,再按分类、榜单或搜索继续缩小范围。';
|
||||
|
||||
return [
|
||||
'search_landing' => $strLanding,
|
||||
'intro_text' => $strSiteName . '首页当前覆盖' . $strScopes . '等常见内容入口,建议先看首屏内容卡片,再继续按分类、榜单或搜索缩小范围。',
|
||||
'intro_meta' => '首页更适合先发现值得点开的内容,说明性提示只做辅助,不建议压住首屏主体内容。',
|
||||
'guide_cards' => [
|
||||
@@ -94,8 +96,10 @@ class SeoCopyFallbackBuilder
|
||||
private static function buildRankIndex(array $arrFacts): array
|
||||
{
|
||||
$strPeriods = self::joinItems((array)($arrFacts['rank_periods'] ?? []), 3, '常见榜单周期');
|
||||
$strLanding = '当前榜单首页汇总了' . $strPeriods . '等常见周期入口,适合先确定周期,再继续进入具体榜单列表页。';
|
||||
|
||||
return [
|
||||
'search_landing' => $strLanding,
|
||||
'intro_text' => '当前榜单首页汇总了' . $strPeriods . '等常见周期入口,适合先确定要看的榜单范围再继续下钻。',
|
||||
'intro_meta' => '如果你更关心具体周期,可以直接进入对应榜单列表页,再继续看详情和播放路径。',
|
||||
'guide_cards' => [
|
||||
@@ -111,8 +115,10 @@ class SeoCopyFallbackBuilder
|
||||
$strPeriod = self::fallback((string)($arrFacts['rank_period_name'] ?? ''), '当前周期');
|
||||
$strScope = self::fallback((string)($arrFacts['rank_scope_name'] ?? ''), '当前范围');
|
||||
$intVisible = (int)($arrFacts['page_stats']['visible'] ?? 0);
|
||||
$strLanding = '当前榜单列表页展示的是' . $strScope . '下的' . $strPeriod . '结果,当前页可继续浏览的条目约为' . max(1, $intVisible) . '条。';
|
||||
|
||||
return [
|
||||
'search_landing' => $strLanding,
|
||||
'intro_text' => '当前榜单列表页展示的是' . $strScope . '下的' . $strPeriod . '结果,当前页可继续浏览的条目约为' . max(1, $intVisible) . '条。',
|
||||
'intro_meta' => '如果你想横向对比结果,可以切到其他周期榜单,或者直接进入详情页继续确认内容。',
|
||||
'guide_cards' => [
|
||||
|
||||
@@ -24,7 +24,93 @@ class SeoCopyStore
|
||||
return trim($strTemplate);
|
||||
}
|
||||
|
||||
return trim(strtr($strTemplate, $arrReplace));
|
||||
foreach ($arrTokens as $strKey => $mValue) {
|
||||
$strKey = trim((string)$strKey);
|
||||
if ($strKey === '' || trim((string)$mValue) !== '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strTemplate = self::stripEmptyTokenArtifacts($strTemplate, $strKey);
|
||||
}
|
||||
|
||||
return self::normalizeInterpolatedText(strtr($strTemplate, $arrReplace));
|
||||
}
|
||||
|
||||
private static function stripEmptyTokenArtifacts(string $strTemplate, string $strKey): string
|
||||
{
|
||||
$strToken = preg_quote('{' . $strKey . '}', '/');
|
||||
$arrLabelMap = [
|
||||
'video_alias' => ['别名'],
|
||||
'year' => ['年份'],
|
||||
'area_name' => ['地区'],
|
||||
'lang_name' => ['语言'],
|
||||
'actor_names' => ['演员'],
|
||||
'director_names' => ['导演'],
|
||||
'remarks' => ['备注'],
|
||||
'play_line' => ['播放线路', '线路'],
|
||||
'episode_name' => ['集数', '期数', '剧集'],
|
||||
];
|
||||
|
||||
foreach ((array)($arrLabelMap[$strKey] ?? []) as $strLabel) {
|
||||
$strLabel = preg_quote($strLabel, '/');
|
||||
$arrLabelPatterns = [
|
||||
'/' . $strLabel . '\s*' . $strToken . '\s*[、,,]/u',
|
||||
'/[、,,]\s*' . $strLabel . '\s*' . $strToken . '/u',
|
||||
'/' . $strLabel . '\s*' . $strToken . '/u',
|
||||
];
|
||||
|
||||
foreach ($arrLabelPatterns as $strPattern) {
|
||||
$strTemplate = preg_replace($strPattern, '', $strTemplate) ?? $strTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
$arrPatterns = [
|
||||
// 删掉前置连词 + 空 token,例如 “和{remarks}”
|
||||
'/\s*(?:和|及|与|并|或)\s*' . $strToken . '/u',
|
||||
// 删掉空 token + 后置连词,例如 “{remarks}和”
|
||||
'/' . $strToken . '\s*(?:和|及|与|并|或)\s*/u',
|
||||
// 删掉与空 token 紧邻的顿号/逗号
|
||||
'/[、,,]\s*' . $strToken . '/u',
|
||||
'/' . $strToken . '\s*[、,,]/u',
|
||||
// 默认兜底:直接删掉空 token
|
||||
'/' . $strToken . '/u',
|
||||
];
|
||||
|
||||
foreach ($arrPatterns as $strPattern) {
|
||||
$strTemplate = preg_replace($strPattern, '', $strTemplate) ?? $strTemplate;
|
||||
}
|
||||
|
||||
return $strTemplate;
|
||||
}
|
||||
|
||||
private static function normalizeInterpolatedText(string $strValue): string
|
||||
{
|
||||
$strValue = preg_replace('/\{[a-z0-9_]+\}/iu', '', $strValue) ?? $strValue;
|
||||
$strValue = str_replace(['高清高清播放', '高清高清'], ['高清播放', '高清'], $strValue);
|
||||
$strValue = preg_replace('/\s{2,}/u', ' ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s*[_||]+\s*/u', ' - ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s*-\s*-\s*/u', ' - ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(会把|把)([^,。;!?、\\s]{1,24})和([^,。;!?、\\s]{1,24})(等资料|这类线索)/u', '$1$2、$3$4', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(年份|地区|语言|演员|导演|备注|别名|线路|集数)(?=[A-Za-z0-9])/u', '$1 ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(地区|语言|演员|导演|备注|别名)([\p{Han}A-Za-z])/u', '$1 $2', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/地区\s*([^,。;!?、]+?)和语言\s*([^,。;!?、]+)/u', '地区 $1,语言 $2', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(会把)([^,。;!?、\\s]{1,24})、([^,。;!?、\\s]{1,24})(这类线索)/u', '$1$2和$3$4', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(会把)([^,。;!?、\\s]{1,24})、([^,。;!?、\\s]{1,24})(等资料)/u', '$1$2、$3$4', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/补看(\d{4}[^、,。;!?]*?)、([\p{Han}A-Za-z]{1,20})、([\p{Han}A-Za-z]{1,20})与([\p{Han}A-Za-z·、,,]{1,40})/u', '补看 $1、$2、$3,以及$4', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(别名|年份|地区|语言|演员|导演|备注)\s+与/u', '$1与', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/本页,建议/u', '本页建议', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/本页,适合/u', '本页适合', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/本页,方便/u', '本页方便', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/([、,,])(?:\s*\\1)+/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/([((【\[])\s*[、,,\-]+\s*/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s*[、,,\-]+\s*([))】\]])/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(会把|把|将|用|看)、/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/(\d{4})-\d{2}-\d{2}(?:\s+\d{2}:\d{2}:\d{2})?/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s*([,。;!?、])/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/([((【\[])\s+/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s+([))】\]])/u', '$1', $strValue) ?? $strValue;
|
||||
|
||||
return trim($strValue);
|
||||
}
|
||||
|
||||
public static function buildPageKey(array|string $mValue, string $strFallback = 'index'): string
|
||||
|
||||
@@ -4,6 +4,31 @@ namespace app\common\helper;
|
||||
|
||||
class SeoWordsHelper
|
||||
{
|
||||
/**
|
||||
* Resolve a stable forge keyword from video seo words.
|
||||
*/
|
||||
public static function resolveForgeWord(array $video = [], int $intForgeId = 1): string
|
||||
{
|
||||
$intForgeId = max(1, $intForgeId);
|
||||
|
||||
$arrWords = array_values(array_filter(array_map(
|
||||
static fn($mVal): string => trim((string)$mVal),
|
||||
(array)($video['v_seo_words'] ?? [])
|
||||
), static fn(string $strVal): bool => $strVal !== ''));
|
||||
|
||||
if (!empty($arrWords)) {
|
||||
$intIndex = ($intForgeId - 1) % count($arrWords);
|
||||
return $arrWords[$intIndex];
|
||||
}
|
||||
|
||||
$strName = trim((string)($video['v_name'] ?? ''));
|
||||
if ($strName !== '') {
|
||||
return $strName;
|
||||
}
|
||||
|
||||
return trim((string)($video['v_name_en'] ?? ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 $intRewrite 处理 SeoWords
|
||||
*/
|
||||
@@ -22,32 +47,39 @@ class SeoWordsHelper
|
||||
// 0:原序全部
|
||||
case 0:
|
||||
$arrSeoWords = $words;
|
||||
break;
|
||||
|
||||
// 1:前 6
|
||||
// 1:前 6
|
||||
case 1:
|
||||
$arrSeoWords = array_slice($words, 0, 6);
|
||||
break;
|
||||
|
||||
// 2:随机 8
|
||||
// 2:随机 8
|
||||
case 2:
|
||||
shuffle($words);
|
||||
$arrSeoWords = array_slice($words, 0, 8);
|
||||
break;
|
||||
|
||||
// 3:插年份
|
||||
// 3:插年份
|
||||
case 3:
|
||||
$arrSeoWords = self::appendYear($words, $video);
|
||||
break;
|
||||
|
||||
// 4:插“在线观看 / 免费 / 高清”
|
||||
// 4:插“在线观看 / 免费 / 高清”
|
||||
case 4:
|
||||
$arrSeoWords = self::appendSuffix($words);
|
||||
break;
|
||||
|
||||
// 5:重排 + 去重
|
||||
// 5:重排 + 去重
|
||||
case 5:
|
||||
shuffle($words);
|
||||
$arrSeoWords = array_values(array_unique($words));
|
||||
break;
|
||||
|
||||
// 兜底
|
||||
// 兜底
|
||||
default:
|
||||
$arrSeoWords = array_slice($words, 0, 6);
|
||||
break;
|
||||
}
|
||||
|
||||
$arrNewsSeoWords = [];
|
||||
|
||||
@@ -83,7 +83,7 @@ class UrlBuilder
|
||||
return $this->replacePattern($pattern, [
|
||||
'strParentCategory' => $strParentCategory,
|
||||
'strCategory' => $strCategory,
|
||||
'intPage' => max(1, (int)$intPage),
|
||||
'intPage' => $this->normalizePageValue($intPage),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -226,4 +226,13 @@ class UrlBuilder
|
||||
}
|
||||
return $this->trimSlash($pattern);
|
||||
}
|
||||
|
||||
protected function normalizePageValue(int|string $page): string
|
||||
{
|
||||
if (is_string($page) && strpos($page, '{page}') !== false) {
|
||||
return '{page}';
|
||||
}
|
||||
|
||||
return (string)max(1, (int)$page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
return $SiteContext->getSiteMapByCode('VIDEO');
|
||||
})->ext('xml');
|
||||
|
||||
// 榜单首页历史深链优先硬绑定,避免被通用 rank_list 模式误吞到列表页模板。
|
||||
Route::get('/phb-index', function () {
|
||||
return view('video/getRankIndex.html');
|
||||
});
|
||||
|
||||
// ========== 1️⃣ 取当前域名冻结的 family ==========
|
||||
$tpStyle = SiteStyle::getConfig();
|
||||
$family = $tpStyle['template_cfg']['url_family'];
|
||||
@@ -75,6 +80,21 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
}
|
||||
|
||||
$registered[$route] = true;
|
||||
|
||||
// 兼容带 .html 的冻结路径。
|
||||
// ThinkPHP 在旧路由里普遍使用 "/path" + ->ext('html'),
|
||||
// 而当前 family 里有一部分站点会直接冻结成 "/get.html"、"/record.html"。
|
||||
// 这里同时补一个无后缀别名,避免这类路径在 GPT 新路由分支下落不到视图。
|
||||
if (str_ends_with($route, '.html')) {
|
||||
$routeWithoutExt = substr($route, 0, -5);
|
||||
if ($routeWithoutExt !== '' && !isset($registered[$routeWithoutExt])) {
|
||||
$alias = Route::get($routeWithoutExt, fn() => view($view))->ext('html');
|
||||
if ($pattern) {
|
||||
$alias->pattern($pattern);
|
||||
}
|
||||
$registered[$routeWithoutExt] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 2.5️⃣ 冻结前按 sort 排序 ==========
|
||||
@@ -126,7 +146,7 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
break;
|
||||
|
||||
case 'category_home':
|
||||
$register($route, 'video/getCategory.html');
|
||||
$register($route, 'video/getMap.html');
|
||||
break;
|
||||
case 'category_parent':
|
||||
$register($route, 'video/getCategoryType.html',[
|
||||
@@ -167,7 +187,7 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
$register($route, 'video/getRankIndex.html');
|
||||
break;
|
||||
case 'rank_list':
|
||||
$register($route, 'video/getRankIndex.html',[
|
||||
$register($route, 'video/getRankList.html',[
|
||||
'strParentCategory' => '[a-z\-]*', // 允许空值
|
||||
'strCategory' => '[a-z\-]*', // 允许空值
|
||||
'strSortType' => '[a-z\-]*', // 允许空值
|
||||
@@ -851,7 +871,7 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
],
|
||||
|
||||
//排行榜
|
||||
'video_rank_index' => ['/paihang/index', '/rank/index', '/paihang-quan', '/top/index', '/paihang-bang', '/ranks/index', '/sort/index', '/order/index', '/phb/index'],
|
||||
'video_rank_index' => ['/paihang/index', '/rank/index', '/paihang-quan', '/top/index', '/paihang-bang', '/ranks/index', '/sort/index', '/order/index', '/phb/index', '/phb-index'],
|
||||
|
||||
//排行榜
|
||||
'video_rank_list' => [
|
||||
|
||||
@@ -1,12 +1,98 @@
|
||||
{extend name="base" /}
|
||||
|
||||
{block name="get-data"}
|
||||
{video:seocopy scene="home" export_name="seoCopy" /}
|
||||
|
||||
{php}
|
||||
$strHomeIntroTitle = trim((string)($DomainModel->d_name ?? ''));
|
||||
if ($strHomeIntroTitle === '') {
|
||||
$strHomeIntroTitle = '影视内容推荐';
|
||||
}
|
||||
|
||||
$strHomeIntroText = trim((string)($seoCopy['intro_text'] ?? $seoCopy['category_intro'] ?? ''));
|
||||
if ($strHomeIntroText === '') {
|
||||
$strHomeIntroText = trim((string)($seoCopy['description'] ?? ''));
|
||||
}
|
||||
if ($strHomeIntroText === '') {
|
||||
$strHomeIntroText = $strHomeIntroTitle . '首页整理了电影、剧集、综艺、动漫等常见入口,适合先看首屏推荐,再继续按分类、榜单和搜索缩小范围。';
|
||||
}
|
||||
|
||||
$strHomeIntroMeta = trim((string)($seoCopy['intro_meta'] ?? $seoCopy['faq_content'] ?? ''));
|
||||
if ($strHomeIntroMeta === '') {
|
||||
$strHomeIntroMeta = '建议先看首屏推荐,再根据题材、热度和搜索词继续深入浏览。';
|
||||
}
|
||||
|
||||
$arrHomeCategoryNames = array_values(array_filter(array_map(static function ($arrCategory): string {
|
||||
return trim((string)($arrCategory['name'] ?? ''));
|
||||
}, (array)($TpStyle['template_cfg']['pages']['home']['categories'] ?? [])), static function (string $strName): bool {
|
||||
return $strName !== '';
|
||||
}));
|
||||
$strHomeCategorySummary = implode('、', array_slice($arrHomeCategoryNames, 0, 3));
|
||||
if ($strHomeCategorySummary === '') {
|
||||
$strHomeCategorySummary = '电影、剧集、综艺';
|
||||
}
|
||||
|
||||
$arrHomeSlotKeys = array_values(array_filter(array_map(static function ($arrSlot): string {
|
||||
return trim((string)($arrSlot['layout_key'] ?? ''));
|
||||
}, (array)($TpStyle['template_cfg']['pages']['home']['slots'] ?? [])), static function (string $strKey): bool {
|
||||
return $strKey !== '';
|
||||
}));
|
||||
$strHomePrimarySlot = $arrHomeSlotKeys[0] ?? 'news';
|
||||
$arrHomePrimarySlotLabelMap = [
|
||||
'news' => '更新区',
|
||||
'rank' => '榜单区',
|
||||
'tuijian' => '推荐区',
|
||||
'update' => '更新区',
|
||||
'piaofang' => '热度区',
|
||||
'lunli' => '内容区',
|
||||
];
|
||||
$strHomePrimarySlotLabel = $arrHomePrimarySlotLabelMap[$strHomePrimarySlot] ?? '推荐区';
|
||||
|
||||
$intHomeTopSlotCount = count((array)($TpStyle['template_cfg']['pages']['home']['slots'] ?? []));
|
||||
$intHomeCategoryCount = count((array)($TpStyle['template_cfg']['pages']['home']['categories'] ?? []));
|
||||
|
||||
$arrHomeQuickSignals = [
|
||||
['label' => '首屏主区', 'text' => '先看' . $strHomePrimarySlotLabel],
|
||||
['label' => '频道方向', 'text' => $strHomeCategorySummary],
|
||||
['label' => '内容覆盖', 'text' => '当前约有' . (1 + $intHomeTopSlotCount + $intHomeCategoryCount) . '组入口区块'],
|
||||
['label' => '浏览节奏', 'text' => '首屏吸引 -> 详情判断 -> 播放选择'],
|
||||
];
|
||||
|
||||
$arrHomeGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
|
||||
if (empty($arrHomeGuideCards)) {
|
||||
$arrHomeGuideCards = [
|
||||
[
|
||||
'title' => '首屏入口',
|
||||
'text' => '首页第一屏更适合先看' . $strHomePrimarySlotLabel . '和刚刚更新,快速判断当前最值得点开的内容。',
|
||||
],
|
||||
[
|
||||
'title' => '内容范围',
|
||||
'text' => '当前首页已经把' . $strHomeCategorySummary . '等入口串起来,适合横向切换不同内容类型。',
|
||||
],
|
||||
[
|
||||
'title' => '搜索建议',
|
||||
'text' => '如果你已经知道片名、演员或题材词,直接用搜索通常会比翻列表更快。',
|
||||
],
|
||||
[
|
||||
'title' => '后续路径',
|
||||
'text' => '找到感兴趣的内容后,建议先看详情页确认题材和线路,再决定是否直接进入播放页。',
|
||||
],
|
||||
];
|
||||
}
|
||||
{/php}
|
||||
|
||||
{/block}
|
||||
|
||||
{block name="title"}{site:replace code="VIDEO@INDEX@INDEX@TITLE"}{/block}
|
||||
{block name="keywords"}{site:replace code="VIDEO@INDEX@INDEX@KEYWORDS"}{/block}
|
||||
{block name="description"}{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"}{/block}
|
||||
{block name="title"}{site:seotkd code="title" page="home" /}{/block}
|
||||
{block name="keywords"}{site:seotkd code="keywords" page="home" /}{/block}
|
||||
{block name="description"}{site:seotkd code="description" page="home" /}{/block}
|
||||
|
||||
{block name="head"}
|
||||
<meta name="robots" content="index,follow">
|
||||
@@ -14,8 +100,8 @@
|
||||
|
||||
|
||||
{// 社交媒体标签 }
|
||||
<meta property="og:title" content='{site:replace code="VIDEO@INDEX@INDEX@TITLE"}' />
|
||||
<meta property="og:description" content='{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"}' />
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="home" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="home" /}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:type" content="website" />
|
||||
{// 结构化数据 }
|
||||
@@ -26,7 +112,7 @@
|
||||
"name": "{$DomainModel->d_name}",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"alternateName": "{$DomainModel->d_domain}",
|
||||
"description": "{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"}",
|
||||
"description": "{site:seotkd code='description' page='home' /}",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": 'https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}',
|
||||
@@ -42,6 +128,25 @@
|
||||
{block name="main"}
|
||||
<main class="page-home" style="max-width:{$TpStyle.template_cfg.global.page_max_width_pc}px;margin:0 auto;">
|
||||
|
||||
<section class="{$TpStyle.dom_prefix}-home-intro" style="margin:0 0 18px;padding:20px 22px;border:1px solid var(--border-color);border-radius:24px;background:linear-gradient(135deg,rgba(255,255,255,.98),var(--bg-soft-color));box-shadow:0 12px 28px rgba(0,0,0,.04);">
|
||||
<div style="display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px;">
|
||||
<span style="display:inline-flex;width:28px;height:3px;border-radius:999px;background:linear-gradient(90deg,var(--color-primary),var(--color-secondary));"></span>
|
||||
<span style="display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:rgba(0,0,0,.04);color:var(--text-muted-color);font-size:12px;line-height:1.5;">首页导览</span>
|
||||
</div>
|
||||
<h1 style="margin:0 0 10px;color:var(--text-color);font-size:24px;line-height:1.35;">{$strHomeIntroTitle}</h1>
|
||||
<p style="margin:0 0 10px;color:var(--text-color);font-size:15px;line-height:1.9;">{$strHomeIntroText}</p>
|
||||
<p style="margin:0;color:var(--text-muted-color);font-size:13px;line-height:1.8;">{$strHomeIntroMeta}</p>
|
||||
</section>
|
||||
|
||||
<section class="{$TpStyle.dom_prefix}-home-quick-signals" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin:0 0 18px;">
|
||||
{foreach $arrHomeQuickSignals as $arrSignal}
|
||||
<article style="padding:12px 14px;border-radius:16px;background:rgba(255,255,255,.92);border:1px solid rgba(0,0,0,.06);box-shadow:0 8px 18px rgba(0,0,0,.03);">
|
||||
<strong style="display:block;margin:0 0 6px;color:var(--text-color);font-size:12px;line-height:1.5;">{$arrSignal.label}</strong>
|
||||
<span style="display:block;color:var(--text-muted-color);font-size:13px;line-height:1.7;">{$arrSignal.text}</span>
|
||||
</article>
|
||||
{/foreach}
|
||||
</section>
|
||||
|
||||
{// lunli }
|
||||
{assign name="Slot" value="$TpStyle.template_cfg.pages.home.lunliSlots[0]"}
|
||||
{assign name="title" value="$Slot.title_text"}
|
||||
@@ -74,6 +179,8 @@
|
||||
|
||||
{/foreach}
|
||||
|
||||
{include file="module/seo_copy/collection" /}
|
||||
|
||||
{// 分类}
|
||||
{foreach $TpStyle.template_cfg.pages.home.categories as $HomeCategory}
|
||||
|
||||
@@ -105,4 +212,3 @@
|
||||
</main>
|
||||
|
||||
{/block}
|
||||
|
||||
|
||||
@@ -1,80 +1,207 @@
|
||||
{// ===================== Desc Variants ===================== }
|
||||
|
||||
{php}
|
||||
$intCurrentForgeId = (int)(request()->route('intVForgeId') ?? 0);
|
||||
$strSeoCopyScene = $intCurrentForgeId > 0 ? 'forge' : 'detail';
|
||||
{/php}
|
||||
|
||||
{video:seocopy scene="$strSeoCopyScene" video="$arrVideo" export_name="seoCopy" /}
|
||||
|
||||
{php}
|
||||
$strDetailMainDescription = trim(preg_replace('/\s+/u', ' ', strip_tags((string)($arrVideo['v_description'] ?? ''))));
|
||||
if ($strDetailMainDescription === '') {
|
||||
$strDetailMainDescription = '当前内容暂无完整剧情简介,建议结合详情信息与播放入口继续判断。';
|
||||
}
|
||||
|
||||
$arrPlayGroups = is_array($arrVideo['v_play_url'] ?? null) ? $arrVideo['v_play_url'] : [];
|
||||
$strPrimaryPlayType = '';
|
||||
$intPrimaryPlayIndex = 1;
|
||||
if (!empty($arrPlayGroups)) {
|
||||
$strPrimaryPlayType = (string)array_key_first($arrPlayGroups);
|
||||
}
|
||||
|
||||
$strDetailSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strDetailSlug === '') {
|
||||
$strDetailSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
|
||||
$strPreferredPlayUrl = '';
|
||||
if ((int)($arrVideo['v_id'] ?? 0) > 0 && $strPrimaryPlayType !== '') {
|
||||
$strPreferredPlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)$arrVideo['v_id'],
|
||||
$strDetailSlug,
|
||||
$strPrimaryPlayType,
|
||||
$intPrimaryPlayIndex
|
||||
);
|
||||
}
|
||||
|
||||
$boolIsForgeDetail = $intCurrentForgeId > 0;
|
||||
$strOriginVideoName = trim((string)($arrVideo['v_name'] ?? ''));
|
||||
$strMainVideoInfoUrl = '';
|
||||
if ((int)($arrVideo['v_id'] ?? 0) > 0) {
|
||||
$strMainVideoInfoUrl = app(\app\services\VideoService::class)->getVideoInfoUrl(
|
||||
(int)$arrVideo['v_id'],
|
||||
$strDetailSlug
|
||||
);
|
||||
}
|
||||
|
||||
$strDetailBody = trim((string)($seoCopy['detail_faq'] ?? ''));
|
||||
$strDetailBodyLead = trim((string)($seoCopy['detail_body_lead'] ?? ''));
|
||||
$strDetailPlayLinkLead = trim((string)($seoCopy['detail_play_link_lead'] ?? ''));
|
||||
$strDetailBodyTail = trim((string)($seoCopy['detail_body_tail'] ?? ''));
|
||||
$strForgeBodyLead = trim((string)($seoCopy['forge_body_lead'] ?? ''));
|
||||
$strForgeReturnLead = trim((string)($seoCopy['forge_return_lead'] ?? ''));
|
||||
if ($strForgeReturnLead === '') {
|
||||
$strForgeReturnLead = '如果想回到标准详情,可返回';
|
||||
}
|
||||
$strForgePlayLead = trim((string)($seoCopy['forge_play_lead'] ?? ''));
|
||||
$strForgeDetailNote = trim((string)($seoCopy['forge_detail_note'] ?? ''));
|
||||
|
||||
$arrGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
|
||||
$arrForgeEntryLinks = [];
|
||||
foreach ($arrGuideCards as $arrGuideCard) {
|
||||
$strGuideTitle = trim((string)($arrGuideCard['title'] ?? ''));
|
||||
$strGuideText = trim((string)($arrGuideCard['text'] ?? ''));
|
||||
$strGuideLine = trim($strGuideTitle . ($strGuideText !== '' ? ':' . $strGuideText : ''));
|
||||
if ($strGuideLine !== '') {
|
||||
$arrForgeEntryLinks[] = [
|
||||
'url' => $strMainVideoInfoUrl !== '' ? $strMainVideoInfoUrl : '/',
|
||||
'text' => $strGuideLine,
|
||||
];
|
||||
}
|
||||
}
|
||||
{/php}
|
||||
|
||||
<style>
|
||||
.{$TpStyle.dom_prefix}-dm-desc,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-body,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-guide,
|
||||
.{$TpStyle.dom_prefix}-dm-forge-note {
|
||||
min-width: 0;
|
||||
word-break: break-word;
|
||||
}
|
||||
.{$TpStyle.dom_prefix}-dm-desc p,
|
||||
.{$TpStyle.dom_prefix}-dm-desc li,
|
||||
.{$TpStyle.dom_prefix}-dm-desc blockquote,
|
||||
.{$TpStyle.dom_prefix}-dm-desc summary,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-body p,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-guide p,
|
||||
.{$TpStyle.dom_prefix}-dm-forge-note p {
|
||||
font-size: 14px;
|
||||
line-height: 1.78;
|
||||
}
|
||||
.{$TpStyle.dom_prefix}-dm-desc header,
|
||||
.{$TpStyle.dom_prefix}-dm-desc summary {
|
||||
font-size: 16px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.{$TpStyle.dom_prefix}-dm-desc p,
|
||||
.{$TpStyle.dom_prefix}-dm-desc li,
|
||||
.{$TpStyle.dom_prefix}-dm-desc blockquote,
|
||||
.{$TpStyle.dom_prefix}-dm-desc summary,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-body p,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-guide p,
|
||||
.{$TpStyle.dom_prefix}-dm-forge-note p {
|
||||
font-size: 13px !important;
|
||||
line-height: 1.74 !important;
|
||||
}
|
||||
.{$TpStyle.dom_prefix}-dm-desc header,
|
||||
.{$TpStyle.dom_prefix}-dm-desc summary {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
.{$TpStyle.dom_prefix}-dm-desc-body,
|
||||
.{$TpStyle.dom_prefix}-dm-desc-guide,
|
||||
.{$TpStyle.dom_prefix}-dm-forge-note {
|
||||
margin-top: 8px !important;
|
||||
padding: 10px 10px !important;
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{if $variant == 0}
|
||||
<p class="{$TpStyle.dom_prefix}-dm-desc v0">
|
||||
{$arrVideo.v_description}
|
||||
{$strDetailMainDescription}
|
||||
</p>
|
||||
|
||||
{elseif $variant == 1}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v1">
|
||||
<p>{$arrVideo.v_description|mb_substr=0,120}</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,120}</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 2}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-desc v2">
|
||||
<p>{$arrVideo.v_description|mb_substr=0,200}</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,200}</p>
|
||||
</section>
|
||||
|
||||
{elseif $variant == 3}
|
||||
<article class="{$TpStyle.dom_prefix}-dm-desc v3">
|
||||
<p>{$arrVideo.v_description}</p>
|
||||
<p>{$strDetailMainDescription}</p>
|
||||
</article>
|
||||
|
||||
{elseif $variant == 4}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v4">
|
||||
<p>
|
||||
剧情简介:{$arrVideo.v_description|mb_substr=0,150}
|
||||
剧情简介:{$strDetailMainDescription|mb_substr=0,150}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 5}
|
||||
<details class="{$TpStyle.dom_prefix}-dm-desc v5">
|
||||
<summary>剧情介绍</summary>
|
||||
<p>{$arrVideo.v_description}</p>
|
||||
<p>{$strDetailMainDescription}</p>
|
||||
</details>
|
||||
|
||||
{elseif $variant == 6}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v6">
|
||||
<p>{$arrVideo.v_description|mb_substr=0,100}...</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,100}...</p>
|
||||
<a href="#desc">查看详情</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 7}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-desc v7">
|
||||
<header>影片简介</header>
|
||||
<p>{$arrVideo.v_description|mb_substr=0,180}</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,180}</p>
|
||||
</section>
|
||||
|
||||
{elseif $variant == 8}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v8">
|
||||
<blockquote>
|
||||
{$arrVideo.v_description|mb_substr=0,160}
|
||||
{$strDetailMainDescription|mb_substr=0,160}
|
||||
</blockquote>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 9}
|
||||
<p class="{$TpStyle.dom_prefix}-dm-desc v9">
|
||||
{$arrVideo.v_name}({$arrVideo.v_year})剧情:
|
||||
{$arrVideo.v_description|mb_substr=0,140}
|
||||
{$strDetailMainDescription|mb_substr=0,140}
|
||||
</p>
|
||||
|
||||
{elseif $variant == 10}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v10">
|
||||
<p itemprop="description">
|
||||
{$arrVideo.v_description|mb_substr=0,160}
|
||||
{$strDetailMainDescription|mb_substr=0,160}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 11}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v11"
|
||||
data-desc="{$arrVideo.v_description|mb_substr=0,200}">
|
||||
<p>{$arrVideo.v_description|mb_substr=0,120}</p>
|
||||
data-desc="{$strDetailMainDescription|mb_substr=0,200}">
|
||||
<p>{$strDetailMainDescription|mb_substr=0,120}</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 12}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-desc v12">
|
||||
<p>
|
||||
{$arrVideo.v_description|mb_substr=0,100}
|
||||
{$strDetailMainDescription|mb_substr=0,100}
|
||||
<span class="{$TpStyle.dom_prefix}-dm-desc-more">
|
||||
{$arrVideo.v_name} 在线观看
|
||||
</span>
|
||||
@@ -84,7 +211,7 @@
|
||||
{elseif $variant == 13}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v13">
|
||||
<p>
|
||||
{$arrVideo.v_description|mb_substr=0,130}
|
||||
{$strDetailMainDescription|mb_substr=0,130}
|
||||
高清完整版内容介绍。
|
||||
</p>
|
||||
</div>
|
||||
@@ -92,7 +219,7 @@
|
||||
{elseif $variant == 14}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v14">
|
||||
<p>
|
||||
{$arrVideo.v_description|mb_substr=0,150}
|
||||
{$strDetailMainDescription|mb_substr=0,150}
|
||||
</p>
|
||||
<p class="{$TpStyle.dom_prefix}-dm-desc-note">
|
||||
本片支持在线播放
|
||||
@@ -101,7 +228,7 @@
|
||||
|
||||
{elseif $variant == 15}
|
||||
<p class="{$TpStyle.dom_prefix}-dm-desc v15">
|
||||
{$arrVideo.v_description|mb_substr=0,120}
|
||||
{$strDetailMainDescription|mb_substr=0,120}
|
||||
免费观看{$arrVideo.v_name}
|
||||
</p>
|
||||
|
||||
@@ -109,30 +236,59 @@
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v16">
|
||||
<p>
|
||||
<strong>{$arrVideo.v_name}</strong>
|
||||
{$arrVideo.v_description|mb_substr=0,140}
|
||||
{$strDetailMainDescription|mb_substr=0,140}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 17}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-desc v17">
|
||||
<p>{$arrVideo.v_description|mb_substr=0,180}</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,180}</p>
|
||||
</section>
|
||||
|
||||
{elseif $variant == 18}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc v18">
|
||||
<p class="{$TpStyle.dom_prefix}-dm-desc-hidden">
|
||||
{$arrVideo.v_description}
|
||||
{$strDetailMainDescription}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 19}
|
||||
<article class="{$TpStyle.dom_prefix}-dm-desc v19">
|
||||
<header>剧情简介</header>
|
||||
<p>{$arrVideo.v_description|mb_substr=0,160}</p>
|
||||
<p>{$strDetailMainDescription|mb_substr=0,160}</p>
|
||||
</article>
|
||||
|
||||
{/if}
|
||||
|
||||
{notempty name="$strDetailBody"}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc-body" style="margin-top:10px;padding:12px 12px;border:1px solid var(--border-color);border-radius:14px;background:var(--bg-soft-color);">
|
||||
<p style="margin:0;color:var(--text-color);line-height:1.8;">{$strDetailBody}</p>
|
||||
</div>
|
||||
{/notempty}
|
||||
|
||||
{if empty($boolIsForgeDetail)}
|
||||
{include file="module/seo_copy/detail" /}
|
||||
{/if}
|
||||
|
||||
{if !empty($boolIsForgeDetail)}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-desc-guide" style="margin-top:10px;padding:12px 12px;border:1px solid var(--border-color);border-radius:14px;background:var(--bg-soft-color);">
|
||||
<p style="margin:0;color:var(--text-color);line-height:1.8;">{$strForgeBodyLead|default='当前页围绕延伸词补充主片相关信息,适合先确认内容归属。'}</p>
|
||||
<p style="margin:8px 0 0;color:var(--text-muted-color);line-height:1.7;">
|
||||
{notempty name="$strMainVideoInfoUrl"}
|
||||
{$strForgeReturnLead|default='如果想回到标准详情,可返回'} <a href="{$strMainVideoInfoUrl}">《{$strOriginVideoName}》详情页</a>;
|
||||
{/notempty}
|
||||
{notempty name="$strPreferredPlayUrl"}
|
||||
{$strForgePlayLead|default='如果准备继续观看,也可以直接进入'} <a href="{$strPreferredPlayUrl}">播放页</a>。
|
||||
{/notempty}
|
||||
</p>
|
||||
</div>
|
||||
<div class="{$TpStyle.dom_prefix}-dm-forge-note" style="margin-top:10px;padding:12px 12px;border:1px solid var(--border-color);border-radius:14px;background:var(--bg-soft-color);">
|
||||
<p style="margin:0;color:var(--text-color);line-height:1.8;">{$strForgeDetailNote|default='当前页提供的是延伸词视角,适合先确认内容归属,再决定是否回到标准详情或播放页。'}</p>
|
||||
{notempty name="$strMainVideoInfoUrl"}
|
||||
<p style="margin:8px 0 0;color:var(--text-muted-color);line-height:1.7;">如果你想继续查看主内容页,可以回到 <a href="{$strMainVideoInfoUrl}">《{$strOriginVideoName}》详情页</a>。</p>
|
||||
{/notempty}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{video:seoaddon video="$arrVideo" export_name="arrAddon" /}
|
||||
{include file="module/detail/_seo_addon" /}
|
||||
|
||||
|
||||
57
code/app/home/view/videoGpt1/module/seo_copy/collection.html
Normal file
57
code/app/home/view/videoGpt1/module/seo_copy/collection.html
Normal file
@@ -0,0 +1,57 @@
|
||||
{php}
|
||||
$arrGuideLines = [];
|
||||
foreach ([
|
||||
$seoCopy['search_landing'] ?? '',
|
||||
$seoCopy['category_intro'] ?? '',
|
||||
$seoCopy['intro_text'] ?? '',
|
||||
$seoCopy['intro_meta'] ?? '',
|
||||
$seoCopy['faq_content'] ?? '',
|
||||
] as $strGuideLine) {
|
||||
$strGuideLine = trim((string)$strGuideLine);
|
||||
if ($strGuideLine !== '' && !in_array($strGuideLine, $arrGuideLines, true)) {
|
||||
$arrGuideLines[] = $strGuideLine;
|
||||
}
|
||||
}
|
||||
$arrGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
{/php}
|
||||
|
||||
{if !empty($arrGuideLines) || !empty($arrGuideCards)}
|
||||
<section class="{$TpStyle.dom_prefix}-guide {$TpStyle.dom_prefix}-guide-collection"
|
||||
style="margin:28px 0 18px;padding:20px 22px;border:1px solid var(--border-color);border-radius:22px;background:linear-gradient(180deg,var(--bg-soft-color),rgba(255,255,255,.96));box-shadow:0 12px 28px rgba(0,0,0,.04);">
|
||||
|
||||
{notempty name="arrGuideLines"}
|
||||
<div style="display:grid;gap:10px;margin-bottom:{notempty name='arrGuideCards'}16px{else/}0{/notempty};">
|
||||
{foreach $arrGuideLines as $strGuideLine}
|
||||
<p style="margin:0;line-height:1.9;color:var(--text-color);font-size:15px;">
|
||||
{$strGuideLine}
|
||||
</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
|
||||
{notempty name="arrGuideCards"}
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;">
|
||||
{foreach $arrGuideCards as $arrGuideCard}
|
||||
<article style="padding:14px 16px;border-radius:16px;background:rgba(255,255,255,.9);border:1px solid rgba(0,0,0,.06);">
|
||||
{notempty name="$arrGuideCard.title"}
|
||||
<h3 style="margin:0 0 8px;font-size:15px;line-height:1.4;color:var(--text-color);">
|
||||
{$arrGuideCard.title}
|
||||
</h3>
|
||||
{/notempty}
|
||||
{notempty name="$arrGuideCard.text"}
|
||||
<p style="margin:0;line-height:1.8;color:var(--text-muted-color);font-size:13px;">
|
||||
{$arrGuideCard.text}
|
||||
</p>
|
||||
{/notempty}
|
||||
</article>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
</section>
|
||||
{/if}
|
||||
55
code/app/home/view/videoGpt1/module/seo_copy/detail.html
Normal file
55
code/app/home/view/videoGpt1/module/seo_copy/detail.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{php}
|
||||
$arrGuideLines = [];
|
||||
foreach ([
|
||||
$seoCopy['detail_body_lead'] ?? '',
|
||||
$seoCopy['detail_play_link_lead'] ?? '',
|
||||
$seoCopy['detail_body_tail'] ?? '',
|
||||
$seoCopy['detail_faq'] ?? '',
|
||||
] as $strGuideLine) {
|
||||
$strGuideLine = trim((string)$strGuideLine);
|
||||
if ($strGuideLine !== '' && !in_array($strGuideLine, $arrGuideLines, true)) {
|
||||
$arrGuideLines[] = $strGuideLine;
|
||||
}
|
||||
}
|
||||
$arrGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
{/php}
|
||||
|
||||
{if !empty($arrGuideLines) || !empty($arrGuideCards)}
|
||||
<section class="{$TpStyle.dom_prefix}-guide {$TpStyle.dom_prefix}-guide-detail"
|
||||
style="margin-top:16px;padding:18px 20px;border:1px solid rgba(0,0,0,.08);border-radius:20px;background:rgba(255,255,255,.92);">
|
||||
{notempty name="arrGuideLines"}
|
||||
<div style="display:grid;gap:10px;margin-bottom:{notempty name='arrGuideCards'}16px{else/}0{/notempty};">
|
||||
{foreach $arrGuideLines as $strGuideLine}
|
||||
<p style="margin:0;line-height:1.9;color:var(--text-color);font-size:14px;">
|
||||
{$strGuideLine}
|
||||
</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
|
||||
{notempty name="arrGuideCards"}
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;">
|
||||
{foreach $arrGuideCards as $arrGuideCard}
|
||||
<article style="padding:12px 14px;border-radius:14px;background:var(--bg-soft-color);border:1px solid rgba(0,0,0,.05);">
|
||||
{notempty name="$arrGuideCard.title"}
|
||||
<h3 style="margin:0 0 8px;font-size:14px;line-height:1.4;color:var(--text-color);">
|
||||
{$arrGuideCard.title}
|
||||
</h3>
|
||||
{/notempty}
|
||||
{notempty name="$arrGuideCard.text"}
|
||||
<p style="margin:0;line-height:1.8;color:var(--text-muted-color);font-size:13px;">
|
||||
{$arrGuideCard.text}
|
||||
</p>
|
||||
{/notempty}
|
||||
</article>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
</section>
|
||||
{/if}
|
||||
55
code/app/home/view/videoGpt1/module/seo_copy/play.html
Normal file
55
code/app/home/view/videoGpt1/module/seo_copy/play.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{php}
|
||||
$arrGuideLines = [];
|
||||
foreach ([
|
||||
$seoCopy['play_intro'] ?? '',
|
||||
$seoCopy['play_meta_note'] ?? '',
|
||||
$seoCopy['play_body_lead'] ?? '',
|
||||
$seoCopy['play_body_next'] ?? '',
|
||||
] as $strGuideLine) {
|
||||
$strGuideLine = trim((string)$strGuideLine);
|
||||
if ($strGuideLine !== '' && !in_array($strGuideLine, $arrGuideLines, true)) {
|
||||
$arrGuideLines[] = $strGuideLine;
|
||||
}
|
||||
}
|
||||
$arrGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
{/php}
|
||||
|
||||
{if !empty($arrGuideLines) || !empty($arrGuideCards)}
|
||||
<section class="{$TpStyle.dom_prefix}-guide {$TpStyle.dom_prefix}-guide-play"
|
||||
style="margin:20px 0 6px;padding:18px 20px;border:1px solid rgba(0,0,0,.08);border-radius:20px;background:linear-gradient(180deg,rgba(255,255,255,.98),var(--bg-soft-color));">
|
||||
{notempty name="arrGuideLines"}
|
||||
<div style="display:grid;gap:10px;margin-bottom:{notempty name='arrGuideCards'}16px{else/}0{/notempty};">
|
||||
{foreach $arrGuideLines as $strGuideLine}
|
||||
<p style="margin:0;line-height:1.9;color:var(--text-color);font-size:14px;">
|
||||
{$strGuideLine}
|
||||
</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
|
||||
{notempty name="arrGuideCards"}
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;">
|
||||
{foreach $arrGuideCards as $arrGuideCard}
|
||||
<article style="padding:12px 14px;border-radius:14px;background:rgba(255,255,255,.92);border:1px solid rgba(0,0,0,.05);">
|
||||
{notempty name="$arrGuideCard.title"}
|
||||
<h3 style="margin:0 0 8px;font-size:14px;line-height:1.4;color:var(--text-color);">
|
||||
{$arrGuideCard.title}
|
||||
</h3>
|
||||
{/notempty}
|
||||
{notempty name="$arrGuideCard.text"}
|
||||
<p style="margin:0;line-height:1.8;color:var(--text-muted-color);font-size:13px;">
|
||||
{$arrGuideCard.text}
|
||||
</p>
|
||||
{/notempty}
|
||||
</article>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/notempty}
|
||||
</section>
|
||||
{/if}
|
||||
@@ -2,9 +2,9 @@
|
||||
{block name="get-data"}
|
||||
|
||||
{if $Request.route.intVId && $Request.route.intVForgeId}
|
||||
{video:info v_id="$Request.route.intVId" v_key="arrVideo" v_fid="$Request.route.intVForgeId" export_name="arrVideo"/}
|
||||
{elseif $Request.route.intVId}
|
||||
{video:info v_id="$Request.route.intVId" v_key="arrVideo" export_name="arrVideo"/}
|
||||
{video:info v_id="$Request.route.intVId" v_pinyin="$Request.route.strPinyin" v_key="arrVideo" v_fid="$Request.route.intVForgeId" export_name="arrVideo"/}
|
||||
{elseif $Request.route.intVId || $Request.route.strPinyin}
|
||||
{video:info v_id="$Request.route.intVId" v_pinyin="$Request.route.strPinyin" v_key="arrVideo" export_name="arrVideo"/}
|
||||
{else}
|
||||
{video:info v_id="$DomainModel->info_id" n_key="arrVideo" export_name="arrVideo" /}
|
||||
{/if}
|
||||
@@ -15,12 +15,83 @@
|
||||
area="all" lang="all" year="all" order="all" page="1"
|
||||
export_name="strVclurl" /}
|
||||
|
||||
{video:seocopy scene="play"
|
||||
video="$arrVideo"
|
||||
play_type="$Request.route.strPlayType"
|
||||
play_index="$Request.route.intPlayIndex"
|
||||
export_name="seoCopy" /}
|
||||
|
||||
{php}
|
||||
$arrBreadcrumb = [
|
||||
['title'=>'首页','url'=>'/'],
|
||||
['title'=>$arrVideo['v_parent_category'],'url'=>$strVciurl],
|
||||
['title'=>$arrVideo['v_category'],'url'=>$strVclurl],
|
||||
];
|
||||
|
||||
$strPlaySlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlaySlug === '') {
|
||||
$strPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
|
||||
$strCurrentPlayType = trim((string)(request()->route('strPlayType') ?? ''));
|
||||
$intCurrentPlayIndex = max(1, (int)(request()->route('intPlayIndex') ?? 1));
|
||||
$arrPlayGroups = is_array($arrVideo['v_play_url'] ?? null) ? $arrVideo['v_play_url'] : [];
|
||||
if ($strCurrentPlayType === '' && !empty($arrPlayGroups)) {
|
||||
$strCurrentPlayType = (string)array_key_first($arrPlayGroups);
|
||||
}
|
||||
|
||||
$strCurrentPlayLine = trim((string)app(\app\services\VideoService::class)->getConverterPlayLineVal($strCurrentPlayType));
|
||||
if ($strCurrentPlayLine === '') {
|
||||
$strCurrentPlayLine = strtoupper($strCurrentPlayType !== '' ? $strCurrentPlayType : 'default');
|
||||
}
|
||||
|
||||
$strCurrentEpisodeName = '第' . $intCurrentPlayIndex . '集';
|
||||
if (!empty($arrPlayGroups[$strCurrentPlayType][$intCurrentPlayIndex - 1]['name'])) {
|
||||
$strCurrentEpisodeName = trim((string)$arrPlayGroups[$strCurrentPlayType][$intCurrentPlayIndex - 1]['name']);
|
||||
}
|
||||
|
||||
$strPlayDetailUrl = '';
|
||||
if ((int)($arrVideo['v_id'] ?? 0) > 0) {
|
||||
$strPlayDetailUrl = app(\app\services\VideoService::class)->getVideoInfoUrl(
|
||||
(int)$arrVideo['v_id'],
|
||||
$strPlaySlug
|
||||
);
|
||||
}
|
||||
|
||||
$strPlayVisibleIntro = trim((string)($seoCopy['play_intro'] ?? ''));
|
||||
if ($strPlayVisibleIntro === '') {
|
||||
$strPlayVisibleIntro = '当前页围绕《' . (string)($arrVideo['v_name'] ?? '当前内容') . '》' . $strCurrentEpisodeName . '整理在线播放入口,并补充剧情与观看线索,方便直接续看。';
|
||||
}
|
||||
|
||||
$strPlayMetaNote = trim((string)($seoCopy['play_meta_note'] ?? ''));
|
||||
if ($strPlayMetaNote === '') {
|
||||
$strPlayMetaNote = '当前线路为' . $strCurrentPlayLine . ',如果加载较慢可以切换线路,或先回详情页补看剧情资料。';
|
||||
}
|
||||
|
||||
$strPlayBodyGuideLead = trim((string)($seoCopy['play_body_lead'] ?? ''));
|
||||
if ($strPlayBodyGuideLead === '') {
|
||||
$strPlayBodyGuideLead = '播放页更适合先确认当前线路、集数和回详情路径,再决定是否继续完整观看。';
|
||||
}
|
||||
|
||||
$strPlayBodyGuideNext = trim((string)($seoCopy['play_body_next'] ?? ''));
|
||||
if ($strPlayBodyGuideNext === '') {
|
||||
$strPlayBodyGuideNext = '如果想先了解完整剧情和人物资料,可以回到详情页继续浏览。';
|
||||
}
|
||||
|
||||
$arrPlayGuideCards = array_values(array_filter((array)($seoCopy['guide_cards'] ?? []), static function ($arrCard): bool {
|
||||
return is_array($arrCard)
|
||||
&& (
|
||||
trim((string)($arrCard['title'] ?? '')) !== ''
|
||||
|| trim((string)($arrCard['text'] ?? '')) !== ''
|
||||
);
|
||||
}));
|
||||
if (empty($arrPlayGuideCards)) {
|
||||
$arrPlayGuideCards = [
|
||||
['title' => '当前剧集', 'text' => $strCurrentEpisodeName],
|
||||
['title' => '当前线路', 'text' => $strCurrentPlayLine],
|
||||
['title' => '观看建议', 'text' => '如果当前线路不够稳定,可先切换线路,或返回详情页继续浏览剧情资料。'],
|
||||
];
|
||||
}
|
||||
{/php}
|
||||
{/block}
|
||||
|
||||
@@ -181,6 +252,8 @@
|
||||
|
||||
{include file="module/page/page_router" /}
|
||||
|
||||
{include file="module/seo_copy/play" /}
|
||||
|
||||
<script>
|
||||
const strMaxPlayHeight = '480px'
|
||||
const strVideoId = `{$arrVideo.v_id}`;
|
||||
|
||||
@@ -458,10 +458,14 @@ class SiteContext
|
||||
$strCategory = $this->Request->param('strCategory');
|
||||
$strSortType = $this->Request->param('strSortType');
|
||||
$intPage = $this->Request->param('intPage');
|
||||
$strOrderName = '';
|
||||
if (!empty($strSortType) && $strSortType !== 'all' && isset(StaticConfig::$arrVideoRankSortType[$strSortType])) {
|
||||
$strOrderName = StaticConfig::$arrVideoRankSortType[$strSortType];
|
||||
}
|
||||
ConverterMovel::setVal([
|
||||
'strVideoParentCategoryName' => empty($strParentCategory) || $strParentCategory == 'all' ? '' : StaticConfig::$arrVideoClass[$strParentCategory],
|
||||
'strVideoCategoryName' => empty($strCategory) || $strCategory == 'all' ? '' : VideoCategoryModel::$arrCategory[$strCategory],
|
||||
'strOrderName' => empty($strSortType) || $strSortType == 'all' ? '' : StaticConfig::$arrVideoRankSortType[$strSortType],
|
||||
'strOrderName' => $strOrderName,
|
||||
'intPage' => $intPage ?? 1,
|
||||
]);
|
||||
}
|
||||
@@ -746,23 +750,377 @@ class SiteContext
|
||||
*/
|
||||
public function getSeoTkd(string $strCode, string $strPage): string
|
||||
{
|
||||
// TpStyle 已在 initCfg() 中注入
|
||||
$TpStyle = $this->TpStyle;
|
||||
$strCode = strtolower(trim($strCode));
|
||||
$strPage = strtolower(trim($strPage));
|
||||
|
||||
if (empty($TpStyle)) {
|
||||
if (!in_array($strCode, ['title', 'keywords', 'description'], true)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 1️⃣ 选 SEO 模板
|
||||
$renderer = new SeoRenderer($TpStyle);
|
||||
$tpl = $renderer->getTemplate($strCode, $strPage);
|
||||
$arrCandidates = [];
|
||||
|
||||
// 1. 新 GPT SEO 池
|
||||
$arrCandidates[] = $this->renderSeoFromPool($strCode, $strPage);
|
||||
|
||||
// 2. 旧 SubjectFomart key 兼容层
|
||||
$arrCandidates[] = $this->renderSeoFromLegacyKey($strCode, $strPage);
|
||||
|
||||
// 3. domain 表兜底
|
||||
$arrCandidates[] = $this->renderSeoFromDomainRecord($strCode, $strPage);
|
||||
|
||||
foreach ($arrCandidates as $strCandidate) {
|
||||
$strCandidate = $this->normalizeSeoText($strCandidate);
|
||||
if ($strCode === 'keywords') {
|
||||
$strCandidate = $this->normalizeSeoKeywords($strCandidate);
|
||||
} elseif ($strCode === 'description') {
|
||||
$strCandidate = $this->normalizeSeoDescription($strCandidate, $strPage);
|
||||
}
|
||||
if ($strCandidate !== '') {
|
||||
return $strCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function renderSeoFromPool(string $strCode, string $strPage): string
|
||||
{
|
||||
$TpStyle = $this->TpStyle;
|
||||
if (empty($TpStyle) || !is_array($TpStyle)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$renderer = new SeoRenderer($TpStyle);
|
||||
$tpl = trim((string)$renderer->getTemplate($strCode, $strPage));
|
||||
if ($tpl === '') {
|
||||
return '';
|
||||
}
|
||||
// var_dump($tpl);
|
||||
// 2️⃣ 统一走 Converter(你现有系统)
|
||||
return ConverterMovel::convert($tpl);
|
||||
|
||||
return $this->normalizeSeoText(ConverterMovel::convert($tpl));
|
||||
}
|
||||
|
||||
protected function renderSeoFromLegacyKey(string $strCode, string $strPage): string
|
||||
{
|
||||
$strLegacyKey = $this->resolveLegacySeoKey($strCode, $strPage);
|
||||
if ($strLegacyKey === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->normalizeSeoText($this->converterTemplate($strLegacyKey));
|
||||
} catch (\Throwable $Throwable) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderSeoFromDomainRecord(string $strCode, string $strPage): string
|
||||
{
|
||||
if (!$this->DomainModel instanceof DomainModel) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$strSiteName = trim((string)($this->DomainModel->d_name ?? ''));
|
||||
$strHost = trim((string)($this->DomainModel->d_domain ?? ''));
|
||||
|
||||
if ($strPage === 'home') {
|
||||
return match ($strCode) {
|
||||
'title' => trim((string)($this->DomainModel->d_index_title ?? $strSiteName)),
|
||||
'keywords' => trim((string)($this->DomainModel->d_index_keywords ?? $this->DomainModel->d_keywords ?? $strSiteName)),
|
||||
'description' => trim((string)($this->DomainModel->d_index_description ?? $this->DomainModel->d_description ?? $strSiteName)),
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
|
||||
if ($strCode === 'title') {
|
||||
$strEntityTitle = trim((string)(
|
||||
ConverterMovel::getVal('strVideoName')
|
||||
?? ConverterMovel::getVal('strVideoCategoryName')
|
||||
?? ConverterMovel::getVal('strVideoParentCategoryName')
|
||||
?? ''
|
||||
));
|
||||
|
||||
if ($strEntityTitle !== '' && $strSiteName !== '') {
|
||||
return $strEntityTitle . ' - ' . $strSiteName;
|
||||
}
|
||||
}
|
||||
|
||||
if ($strCode === 'keywords') {
|
||||
$arrKeywords = array_filter([
|
||||
trim((string)(ConverterMovel::getVal('strVideoName') ?? '')),
|
||||
trim((string)(ConverterMovel::getVal('strVideoCategoryName') ?? '')),
|
||||
trim((string)($this->DomainModel->d_index_keywords ?? '')),
|
||||
trim((string)($this->DomainModel->d_keywords ?? '')),
|
||||
], static function (string $strValue): bool {
|
||||
return $strValue !== '';
|
||||
});
|
||||
|
||||
if (!empty($arrKeywords)) {
|
||||
return implode(',', array_values(array_unique($arrKeywords)));
|
||||
}
|
||||
}
|
||||
|
||||
if ($strCode === 'description') {
|
||||
$strEntityDescription = trim((string)(
|
||||
ConverterMovel::getVal('strVideoDescription')
|
||||
?? ConverterMovel::getVal('strVideoCategoryName')
|
||||
?? ConverterMovel::getVal('strVideoParentCategoryName')
|
||||
?? ''
|
||||
));
|
||||
|
||||
if ($strEntityDescription !== '') {
|
||||
return $strEntityDescription;
|
||||
}
|
||||
|
||||
$strIndexDescription = trim((string)($this->DomainModel->d_index_description ?? ''));
|
||||
if ($strIndexDescription !== '') {
|
||||
return $strIndexDescription;
|
||||
}
|
||||
}
|
||||
|
||||
$arrGeneric = [
|
||||
'title' => $strSiteName !== '' ? $strSiteName : $strHost,
|
||||
'keywords' => trim((string)($this->DomainModel->d_keywords ?? $this->DomainModel->d_index_keywords ?? $strSiteName)),
|
||||
'description' => trim((string)($this->DomainModel->d_description ?? $this->DomainModel->d_index_description ?? $strSiteName)),
|
||||
];
|
||||
|
||||
return trim((string)($arrGeneric[$strCode] ?? ''));
|
||||
}
|
||||
|
||||
protected function normalizeSeoText(mixed $mValue): string
|
||||
{
|
||||
$strValue = trim((string)$mValue);
|
||||
if ($strValue === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$strValue = html_entity_decode($strValue, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$strValue = strip_tags($strValue);
|
||||
$strValue = preg_replace('/\s+/u', ' ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\{[A-Z0-9@_:-]+\}/iu', '', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/^[\\s\\-|_,,。;;::]+/u', '', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/[\\s\\-|_,,。;;::]+$/u', '', $strValue) ?? $strValue;
|
||||
|
||||
if ($strValue === '' || preg_match('/^[-|_,,。;;::\\s]+$/u', $strValue)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim($strValue);
|
||||
}
|
||||
|
||||
protected function normalizeSeoKeywords(string $strValue): string
|
||||
{
|
||||
$strValue = str_replace([',', '、', ';', ';', '|', '|'], ',', $strValue);
|
||||
$arrRawParts = preg_split('/\s*,\s*/u', $strValue) ?: [];
|
||||
$arrNoiseSuffixes = [
|
||||
'叙事模型',
|
||||
'英雄之旅',
|
||||
'成长母题',
|
||||
'精神图腾',
|
||||
'精神内核',
|
||||
'热血传奇',
|
||||
'荒诞美学',
|
||||
'流浪美学',
|
||||
'怀旧元素',
|
||||
'世界观构建',
|
||||
'文化符号',
|
||||
'末日隐喻',
|
||||
'双重叙事结构',
|
||||
'叙事策略',
|
||||
'氛围营造手法',
|
||||
'角色塑造',
|
||||
'诗化表达手法',
|
||||
'史诗建构',
|
||||
'疗愈力',
|
||||
'英雄塑造',
|
||||
'人性挖掘',
|
||||
];
|
||||
$arrStopWords = [
|
||||
'在线',
|
||||
'播放',
|
||||
'观看',
|
||||
'免费',
|
||||
'高清',
|
||||
'热播',
|
||||
'推荐',
|
||||
'完整',
|
||||
'完整版',
|
||||
'全集',
|
||||
'中字',
|
||||
'未删减',
|
||||
'在线播放',
|
||||
'在线观看',
|
||||
'免费高清',
|
||||
'高清免费视频',
|
||||
'高清免费',
|
||||
'免费播放',
|
||||
];
|
||||
|
||||
$arrParts = [];
|
||||
foreach ($arrRawParts as $strPart) {
|
||||
$strPart = trim((string)$strPart);
|
||||
if ($strPart === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($arrNoiseSuffixes as $strSuffix) {
|
||||
if ($strSuffix !== '' && str_ends_with($strPart, $strSuffix)) {
|
||||
$strPart = trim(mb_substr($strPart, 0, mb_strlen($strPart) - mb_strlen($strSuffix)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$strPart = trim($strPart, " \t\n\r\0\x0B-_|,。;、,;");
|
||||
|
||||
if (in_array($strPart, $arrStopWords, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mb_strlen($strPart) <= 2 && !preg_match('/^\d+$/u', $strPart)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($strPart, $arrParts, true)) {
|
||||
$arrParts[] = $strPart;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(',', $arrParts);
|
||||
}
|
||||
|
||||
protected function normalizeSeoDescription(string $strValue, string $strPage): string
|
||||
{
|
||||
$strValue = trim($strValue);
|
||||
if ($strValue === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$strSiteName = trim((string)($this->DomainModel->d_name ?? ''));
|
||||
$strSiteKeywords = trim((string)(ConverterMovel::getVal('strSiteKeywords') ?? ''));
|
||||
$strVideoName = trim((string)(ConverterMovel::getVal('strVideoName') ?? ''));
|
||||
$strVideoDescription = trim((string)(ConverterMovel::getVal('strVideoDescription') ?? ''));
|
||||
$strVideoCategoryName = trim((string)(ConverterMovel::getVal('strVideoCategoryName') ?? ''));
|
||||
|
||||
if ($strSiteKeywords !== '') {
|
||||
$strValue = str_replace($strSiteKeywords, '', $strValue);
|
||||
}
|
||||
|
||||
if ($strVideoName !== '') {
|
||||
$strQuotedVideoName = preg_quote($strVideoName, '/');
|
||||
|
||||
// 合并连续重复片名,如 “片名……片名”
|
||||
$strValue = preg_replace('/(' . $strQuotedVideoName . ')(?:\s*[,。;、,\-]?\s*\1){1,}/u', '$1', $strValue) ?? $strValue;
|
||||
// 清掉 “片名的热血传奇片名” 这类硬拼句式
|
||||
$strValue = preg_replace('/' . $strQuotedVideoName . '(的)?(热血传奇|精神图腾|叙事模型|英雄之旅|成长母题)' . $strQuotedVideoName . '/u', $strVideoName, $strValue) ?? $strValue;
|
||||
}
|
||||
|
||||
$arrNoisePhrases = [
|
||||
'的热血传奇',
|
||||
'的精神图腾',
|
||||
'的叙事模型',
|
||||
'的英雄之旅',
|
||||
'的成长母题',
|
||||
];
|
||||
$strValue = str_replace($arrNoisePhrases, '', $strValue);
|
||||
|
||||
$strValue = preg_replace('/([,。;、])\1+/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s+/u', ' ', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/\s*([,。;、])/u', '$1', $strValue) ?? $strValue;
|
||||
$strValue = preg_replace('/([,。;、])(?=[,。;、])/u', '', $strValue) ?? $strValue;
|
||||
$strValue = trim($strValue, " \t\n\r\0\x0B,。;、,;");
|
||||
|
||||
$boolDescriptionLooksWeak = false;
|
||||
if ($strVideoName !== '') {
|
||||
$intNameCount = preg_match_all('/' . preg_quote($strVideoName, '/') . '/u', $strValue);
|
||||
if ($intNameCount >= 2) {
|
||||
$boolDescriptionLooksWeak = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($strVideoDescription !== '' && $strVideoName !== '' && $strVideoDescription === $strVideoName) {
|
||||
$boolDescriptionLooksWeak = true;
|
||||
}
|
||||
|
||||
if (mb_strlen($strValue) < 20) {
|
||||
$boolDescriptionLooksWeak = true;
|
||||
}
|
||||
|
||||
if ($boolDescriptionLooksWeak) {
|
||||
return $this->buildSeoDescriptionFallback($strPage, $strSiteName, $strVideoName, $strVideoCategoryName);
|
||||
}
|
||||
|
||||
return $strValue;
|
||||
}
|
||||
|
||||
protected function buildSeoDescriptionFallback(string $strPage, string $strSiteName, string $strVideoName, string $strVideoCategoryName): string
|
||||
{
|
||||
$strSiteName = trim($strSiteName);
|
||||
$strVideoName = trim($strVideoName);
|
||||
$strVideoCategoryName = trim($strVideoCategoryName);
|
||||
|
||||
if ($strVideoName === '') {
|
||||
return trim((string)($this->DomainModel->d_index_description ?? $strSiteName));
|
||||
}
|
||||
|
||||
$strCategorySuffix = $strVideoCategoryName !== '' ? $strVideoCategoryName . '内容' : '相关内容';
|
||||
|
||||
return match ($strPage) {
|
||||
'play' => $strSiteName . '提供' . $strVideoName . '播放入口与剧集浏览信息,方便继续查看' . $strCategorySuffix . '与线路安排。',
|
||||
'detail' => $strSiteName . '整理' . $strVideoName . '的详情信息、演员资料与观看线索,方便继续了解' . $strCategorySuffix . '与播放入口。',
|
||||
default => $strSiteName . '提供' . $strVideoName . '相关介绍与浏览入口,方便继续查看' . $strCategorySuffix . '。',
|
||||
};
|
||||
}
|
||||
|
||||
protected function resolveLegacySeoKey(string $strCode, string $strPage): string
|
||||
{
|
||||
$arrMap = [
|
||||
'home' => [
|
||||
'title' => 'VIDEO@INDEX@INDEX@TITLE',
|
||||
'keywords' => 'VIDEO@INDEX@INDEX@KEYWORDS',
|
||||
'description' => 'VIDEO@INDEX@INDEX@DESCRIPTION',
|
||||
],
|
||||
'detail' => [
|
||||
'title' => 'VIDEO@GETVIDEOINFO@TITLE',
|
||||
'keywords' => 'VIDEO@GETVIDEOINFO@KEYWORDS',
|
||||
'description' => 'VIDEO@GETVIDEOINFO@DESCRIPTION',
|
||||
],
|
||||
'play' => [
|
||||
'title' => 'VIDEO@GETVIDEOPLAY@TITLE',
|
||||
'keywords' => 'VIDEO@GETVIDEOPLAY@KEYWORDS',
|
||||
'description' => 'VIDEO@GETVIDEOPLAY@DESCRIPTION',
|
||||
],
|
||||
'search' => [
|
||||
'title' => 'VIDEO@GETSEARCHVIDEO@TITLE',
|
||||
'keywords' => 'VIDEO@GETSEARCHVIDEO@KEYWORDS',
|
||||
'description' => 'VIDEO@GETSEARCHVIDEO@DESCRIPTION',
|
||||
],
|
||||
'rank_index' => [
|
||||
'title' => 'VIDEO@GETVIDEORANKINDEX@TITLE',
|
||||
'keywords' => 'VIDEO@GETVIDEORANKINDEX@KEYWORDS',
|
||||
'description' => 'VIDEO@GETVIDEORANKINDEX@DESCRIPTION',
|
||||
],
|
||||
'rank_list' => [
|
||||
'title' => 'VIDEO@GETVIDEORANKLIST@TITLE',
|
||||
'keywords' => 'VIDEO@GETVIDEORANKLIST@KEYWORDS',
|
||||
'description' => 'VIDEO@GETVIDEORANKLIST@DESCRIPTION',
|
||||
],
|
||||
'category_home' => [
|
||||
'title' => 'VIDEO@GETSEARCHVIDEO@TITLE',
|
||||
'keywords' => 'VIDEO@GETSEARCHVIDEO@KEYWORDS',
|
||||
'description' => 'VIDEO@GETSEARCHVIDEO@DESCRIPTION',
|
||||
],
|
||||
'category_index' => [
|
||||
'title' => 'VIDEO@GETCATEGORYINDEX@TITLE',
|
||||
'keywords' => 'VIDEO@GETCATEGORYINDEX@KEYWORDS',
|
||||
'description' => 'VIDEO@GETCATEGORYINDEX@DESCRIPTION',
|
||||
],
|
||||
'category_list' => [
|
||||
'title' => 'VIDEO@GETCATEGORY@TITLE',
|
||||
'keywords' => 'VIDEO@GETCATEGORY@KEYWORDS',
|
||||
'description' => 'VIDEO@GETCATEGORY@DESCRIPTION',
|
||||
],
|
||||
];
|
||||
|
||||
return (string)($arrMap[$strPage][$strCode] ?? '');
|
||||
}
|
||||
|
||||
public function getTemplate(): string
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace app\services;
|
||||
|
||||
use app\common\helper\SeoCopyFallbackBuilder;
|
||||
use app\common\helper\SeoCopySchema;
|
||||
use app\common\helper\SeoCopyStore;
|
||||
use app\model\CategoryModel;
|
||||
use app\model\ChapterModel;
|
||||
use app\model\ConverterMovel;
|
||||
@@ -48,14 +51,18 @@ class VideoService
|
||||
/**
|
||||
* 获取视频分类首页 URl
|
||||
*
|
||||
* @param string $strParentCategory
|
||||
* @param string|null $strParentCategory
|
||||
* @return string
|
||||
*/
|
||||
public function getVideoCategoryIndexUrl(string $strParentCategory): string
|
||||
public function getVideoCategoryIndexUrl(?string $strParentCategory): string
|
||||
{
|
||||
$strParentCategory = trim((string)$strParentCategory);
|
||||
$strTmpCode = $this->SiteContext->TemplatesModel['t_code'];
|
||||
|
||||
if ($strTmpCode == 'videoGpt1') {
|
||||
if ($strParentCategory === '') {
|
||||
return $this->SiteContext->UrlBuilder->categoryHome();
|
||||
}
|
||||
return $this->SiteContext->UrlBuilder->categoryParent($strParentCategory);
|
||||
} else {
|
||||
$strKey = 'VIDEO_CATEGORY_INDEX_URL';
|
||||
@@ -184,17 +191,23 @@ class VideoService
|
||||
/**
|
||||
* 排行榜 列表 url
|
||||
*
|
||||
* @param string $strCategory
|
||||
* @param string $strParentCategory
|
||||
* @param string $strSortType
|
||||
* @param integer $intPage
|
||||
* @param string|null $strCategory
|
||||
* @param string|null $strParentCategory
|
||||
* @param string|null $strSortType
|
||||
* @param integer|null $intPage
|
||||
* @return string
|
||||
*/
|
||||
public function getVideoRankUrl(string $strCategory, string $strParentCategory, string $strSortType, int $intPage): string
|
||||
public function getVideoRankUrl(?string $strCategory, ?string $strParentCategory, ?string $strSortType, ?int $intPage): string
|
||||
{
|
||||
$strCategory = trim((string)$strCategory);
|
||||
$strParentCategory = trim((string)$strParentCategory);
|
||||
$strSortType = trim((string)$strSortType);
|
||||
$strTmpCode = $this->SiteContext->TemplatesModel['t_code'];
|
||||
|
||||
if ($strTmpCode == 'videoGpt1') {
|
||||
if ($strSortType === '') {
|
||||
return $this->SiteContext->UrlBuilder->rankIndex();
|
||||
}
|
||||
return $this->SiteContext->UrlBuilder->rankList($strSortType);
|
||||
} else {
|
||||
$strKey = 'VIDEO_RANK_LIST_URL';
|
||||
@@ -244,7 +257,17 @@ class VideoService
|
||||
$strTmpCode = $this->SiteContext->TemplatesModel['t_code'];
|
||||
|
||||
if ($strTmpCode == 'videoGpt1') {
|
||||
return $this->SiteContext->UrlBuilder->searchResult($strKeyWords);
|
||||
$strUrl = $this->SiteContext->UrlBuilder->searchResult($strKeyWords);
|
||||
if (is_string($intPage) && strpos($intPage, '{page}') !== false) {
|
||||
return $strUrl . '&page={page}';
|
||||
}
|
||||
|
||||
$intPage = max(1, (int)$intPage);
|
||||
if ($intPage <= 1) {
|
||||
return $strUrl;
|
||||
}
|
||||
|
||||
return $strUrl . '&page=' . $intPage;
|
||||
} else {
|
||||
$strKey = 'VIDEO_SEARCH_LIST_URL';
|
||||
$strUrl = $this->SiteContext->DomainModel->getFomartUrl($strKey, [
|
||||
@@ -791,6 +814,11 @@ class VideoService
|
||||
throw new HttpException(404, '视频不存在');
|
||||
}
|
||||
|
||||
return $this->hydrateResolvedVideo($arrVideo, $intVId, $intVForgeId);
|
||||
}
|
||||
|
||||
protected function hydrateResolvedVideo(array $arrVideo, int $intRequestedVId, int|NULL $intVForgeId): array
|
||||
{
|
||||
if ($intVForgeId >= 0 && !empty($arrVideo['v_seo_words'])) {
|
||||
$intForgeOffset = max(0, $intVForgeId - 1);
|
||||
if (!empty($arrVideo['v_seo_words'][$intForgeOffset])) {
|
||||
@@ -809,14 +837,23 @@ class VideoService
|
||||
$strTopLang = '2025';
|
||||
|
||||
// 点击数
|
||||
$arrVideoClicks = $this->VideoClicksModel->getStats((int)($arrVideo['v_id'] ?? $intVId));
|
||||
$arrVideoClicks = $this->VideoClicksModel->getStats((int)($arrVideo['v_id'] ?? $intRequestedVId));
|
||||
|
||||
$strVideoParentCategoryName = trim((string)($arrVideo['v_parent_category'] ?? ''));
|
||||
$strVideoCategoryName = trim((string)($arrVideo['v_category'] ?? ''));
|
||||
|
||||
// 一部分视频数据里父分类与子分类会被写成同一个值,例如“伦理片 / 伦理片”。
|
||||
// 这里在注入 SEO/TKD 变量前做一次去重,避免标题拼出“伦理片伦理片”。
|
||||
if ($strVideoParentCategoryName !== '' && $strVideoParentCategoryName === $strVideoCategoryName) {
|
||||
$strVideoParentCategoryName = '';
|
||||
}
|
||||
|
||||
// 更新值
|
||||
ConverterMovel::setVal([
|
||||
'intPage' => 1,
|
||||
'strVideoName' => $arrVideo['v_name'],
|
||||
'strVideoParentCategoryName' => $arrVideo['v_parent_category'],
|
||||
'strVideoCategoryName' => $arrVideo['v_category'],
|
||||
'strVideoParentCategoryName' => $strVideoParentCategoryName,
|
||||
'strVideoCategoryName' => $strVideoCategoryName,
|
||||
'strVideoStatus' => $arrVideo['v_isend'],
|
||||
'strVideoDescription' => $arrVideo['v_description'],
|
||||
'strLangName' => $strTopLang,
|
||||
@@ -827,7 +864,27 @@ class VideoService
|
||||
|
||||
$arrVideo['arrVideoClicks'] = $arrVideoClicks;
|
||||
|
||||
return $arrVideo;
|
||||
return $arrVideo;
|
||||
}
|
||||
|
||||
public function getVideoByRouteContext(int|string|null $intVId, int|string|null $intVForgeId, string|null $strPinyin = '')
|
||||
{
|
||||
$intVId = (int)$intVId;
|
||||
$intVForgeId = $intVForgeId === null ? -1 : (int)$intVForgeId;
|
||||
$strPinyin = trim((string)$strPinyin);
|
||||
|
||||
if ($intVId > 0) {
|
||||
return $this->getVideoByVId($intVId, $intVForgeId);
|
||||
}
|
||||
|
||||
if ($strPinyin !== '') {
|
||||
$arrVideo = $this->VideoModel->getVideoByNameEn($strPinyin);
|
||||
if (!empty($arrVideo)) {
|
||||
return $this->hydrateResolvedVideo($arrVideo, (int)($arrVideo['v_id'] ?? 0), $intVForgeId);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getVideoByVId($intVId, $intVForgeId);
|
||||
}
|
||||
|
||||
protected function resolveFallbackVideoByRequest(int $requestedVId, bool $requirePlayable = false): ?array
|
||||
@@ -1103,6 +1160,82 @@ class VideoService
|
||||
return SiteStyle::buildDetailSeoAddon($arrTpStyle, $arrVideo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一获取页面补料块
|
||||
*
|
||||
* 优先读取 seo_copy 发布/本地文件;
|
||||
* 缺失时退回 fallback builder,保证页面不空。
|
||||
*/
|
||||
public function getSeoCopyBlock(string $strScene, array $arrOptions = []): array
|
||||
{
|
||||
$strScene = trim(strtolower($strScene));
|
||||
if ($strScene === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$strHost = trim((string)($this->SiteContext->DomainModel->d_domain ?? ''));
|
||||
if ($strHost === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$arrPageKeys = $this->buildSeoCopyPageKeys($strScene, $arrOptions);
|
||||
|
||||
if ($this->shouldSuppressDefaultOnlySeoCopy($strScene, $strHost, $arrPageKeys)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$arrData = !empty($arrPageKeys)
|
||||
? SeoCopyStore::getPreferredPageData($strHost, $strScene, $arrPageKeys)
|
||||
: [];
|
||||
|
||||
if (!empty($arrData)) {
|
||||
$arrData = $this->interpolateSeoCopyData($arrData, $this->buildSeoCopyTokens($strScene, $arrOptions));
|
||||
}
|
||||
|
||||
if (empty($arrData)) {
|
||||
$arrData = SeoCopyFallbackBuilder::build($strScene, $this->buildSeoCopyFacts($strScene, $arrOptions));
|
||||
}
|
||||
|
||||
return $this->normalizeSeoCopyBlock($strScene, $arrData);
|
||||
}
|
||||
|
||||
private function shouldSuppressDefaultOnlySeoCopy(string $strScene, string $strHost, array $arrPageKeys): bool
|
||||
{
|
||||
if (!in_array($strScene, ['detail', 'play'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$arrSpecificKeys = array_values(array_filter($arrPageKeys, static function ($strPageKey): bool {
|
||||
$strPageKey = trim((string)$strPageKey);
|
||||
return $strPageKey !== '' && $strPageKey !== 'default';
|
||||
}));
|
||||
|
||||
if (empty($arrSpecificKeys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (SeoCopyStore::preferredRoots() as $strRoot) {
|
||||
$strRoot = rtrim(trim((string)$strRoot), '/');
|
||||
if ($strRoot === '' || !is_dir($strRoot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($arrSpecificKeys as $strPageKey) {
|
||||
$strPath = SeoCopyStore::resolvePagePathFromRoot($strRoot, $strHost, $strScene, $strPageKey);
|
||||
if ($strPath !== '' && is_file($strPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$strDefaultPath = SeoCopyStore::resolvePagePathFromRoot($strRoot, $strHost, $strScene, 'default');
|
||||
if ($strDefaultPath !== '' && is_file($strDefaultPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* BSONDocument / 对象 / array 统一转数组(递归)
|
||||
*/
|
||||
@@ -1144,4 +1277,435 @@ class VideoService
|
||||
}
|
||||
return $mixVal;
|
||||
}
|
||||
|
||||
private function buildSeoCopyPageKeys(string $strScene, array $arrOptions): array
|
||||
{
|
||||
$arrKeys = [];
|
||||
|
||||
switch ($strScene) {
|
||||
case 'home':
|
||||
case 'rank_index':
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, 'index');
|
||||
break;
|
||||
|
||||
case 'category_index':
|
||||
$strParentCategory = trim((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory')));
|
||||
if ($strParentCategory !== '') {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$strParentCategory]);
|
||||
}
|
||||
$arrKeys[] = 'index';
|
||||
break;
|
||||
|
||||
case 'category_list':
|
||||
$strParentCategory = trim((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory')));
|
||||
$strCategory = trim((string)($arrOptions['category'] ?? request()->route('strCategory')));
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$strParentCategory, $strCategory]);
|
||||
$arrKeys[] = 'default';
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
$strKeyword = trim((string)($arrOptions['keyword'] ?? request()->get('keyword', '')));
|
||||
if ($strKeyword !== '') {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$strKeyword]);
|
||||
}
|
||||
$arrKeys[] = 'landing';
|
||||
break;
|
||||
|
||||
case 'rank_list':
|
||||
$strSortType = trim((string)($arrOptions['sort_type'] ?? request()->route('strSortType')));
|
||||
$strParentCategory = trim((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory')));
|
||||
$strCategory = trim((string)($arrOptions['category'] ?? request()->route('strCategory')));
|
||||
if ($strSortType !== '') {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$strSortType, $strParentCategory, $strCategory]);
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$strSortType]);
|
||||
}
|
||||
$arrKeys[] = 'default';
|
||||
break;
|
||||
|
||||
case 'detail':
|
||||
$arrVideo = $this->toArraySafe($arrOptions['video'] ?? []);
|
||||
$intVideoId = (int)($arrVideo['v_id'] ?? ($arrOptions['video_id'] ?? 0));
|
||||
if ($intVideoId > 0) {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$intVideoId]);
|
||||
}
|
||||
$arrKeys[] = 'default';
|
||||
break;
|
||||
|
||||
case 'forge':
|
||||
$arrVideo = $this->toArraySafe($arrOptions['video'] ?? []);
|
||||
$intVideoId = (int)($arrVideo['v_id'] ?? ($arrOptions['video_id'] ?? 0));
|
||||
$intForgeId = (int)($arrOptions['forge_id'] ?? request()->route('intVForgeId'));
|
||||
if ($intVideoId > 0 && $intForgeId > 0) {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$intVideoId, $intForgeId]);
|
||||
}
|
||||
$arrKeys[] = 'default';
|
||||
break;
|
||||
|
||||
case 'play':
|
||||
$arrVideo = $this->toArraySafe($arrOptions['video'] ?? []);
|
||||
$intVideoId = (int)($arrVideo['v_id'] ?? ($arrOptions['video_id'] ?? 0));
|
||||
$strPlayType = trim((string)($arrOptions['play_type'] ?? request()->route('strPlayType')));
|
||||
$intPlayIndex = (int)($arrOptions['play_index'] ?? request()->route('intPlayIndex'));
|
||||
if ($intVideoId > 0) {
|
||||
$arrKeys[] = SeoCopySchema::buildScenePageKey($strScene, [$intVideoId, $strPlayType, $intPlayIndex]);
|
||||
}
|
||||
$arrKeys[] = 'default';
|
||||
break;
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($arrKeys, static function ($strKey): bool {
|
||||
return trim((string)$strKey) !== '';
|
||||
})));
|
||||
}
|
||||
|
||||
private function buildSeoCopyFacts(string $strScene, array $arrOptions): array
|
||||
{
|
||||
$arrVideo = $this->toArraySafe($arrOptions['video'] ?? []);
|
||||
$arrPagerData = $this->toArraySafe($arrOptions['pager_data'] ?? []);
|
||||
$arrPageStats = [
|
||||
'page' => (int)($arrPagerData['page'] ?? ($arrOptions['page'] ?? 1)),
|
||||
'pages' => (int)($arrPagerData['pages'] ?? ($arrOptions['pages'] ?? 1)),
|
||||
'total' => (int)($arrPagerData['total'] ?? ($arrOptions['total'] ?? 0)),
|
||||
'visible' => (int)($arrOptions['visible'] ?? 0),
|
||||
];
|
||||
|
||||
return match ($strScene) {
|
||||
'home' => [
|
||||
'site_name' => (string)($this->SiteContext->DomainModel->d_name ?? ''),
|
||||
'content_scopes' => $this->extractHomeContentScopes(),
|
||||
'navigation_paths' => ['首页 -> 分类频道 -> 详情页', '首页 -> 搜索页 -> 详情页 -> 播放页'],
|
||||
],
|
||||
'category_index' => [
|
||||
'parent_category_name' => $this->resolveCategoryLabel((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory'))),
|
||||
'sub_category_examples' => $this->extractCategoryExamples((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory'))),
|
||||
'search_path' => '如果题材仍然不够精确,可继续走站内搜索。',
|
||||
],
|
||||
'category_list' => [
|
||||
'parent_category_name' => $this->resolveCategoryLabel((string)($arrOptions['parent_category'] ?? request()->route('strParentCategory'))),
|
||||
'category_name' => $this->resolveCategoryLabel((string)($arrOptions['category'] ?? request()->route('strCategory'))),
|
||||
'page_stats' => $arrPageStats,
|
||||
],
|
||||
'search' => [
|
||||
'keyword' => (string)($arrOptions['keyword'] ?? request()->get('keyword', '')),
|
||||
'result_stats' => $arrPageStats,
|
||||
],
|
||||
'rank_index' => [
|
||||
'rank_periods' => $this->extractRankPeriods(),
|
||||
'rank_path' => '榜单首页 -> 榜单列表 -> 详情页 -> 播放页',
|
||||
],
|
||||
'rank_list' => [
|
||||
'rank_period_name' => $this->resolveRankSortLabel((string)($arrOptions['sort_type'] ?? request()->route('strSortType'))),
|
||||
'rank_scope_name' => $this->resolveRankScopeName(
|
||||
(string)($arrOptions['parent_category'] ?? request()->route('strParentCategory')),
|
||||
(string)($arrOptions['category'] ?? request()->route('strCategory'))
|
||||
),
|
||||
'page_stats' => $arrPageStats,
|
||||
],
|
||||
'detail' => [
|
||||
'video_name' => (string)($arrVideo['v_name'] ?? ''),
|
||||
'category_name' => (string)($arrVideo['v_category'] ?? ''),
|
||||
'year' => $this->normalizeSeoCopyYear($arrVideo),
|
||||
'core_plot_or_positioning' => (string)($arrVideo['v_description'] ?? ''),
|
||||
],
|
||||
'play' => [
|
||||
'video_name' => (string)($arrVideo['v_name'] ?? ''),
|
||||
'play_type' => $this->getConverterPlayLineVal((string)($arrOptions['play_type'] ?? request()->route('strPlayType'))),
|
||||
'episode_label_or_index' => (string)($arrOptions['play_index'] ?? request()->route('intPlayIndex') ?? 1),
|
||||
'play_index' => (int)($arrOptions['play_index'] ?? request()->route('intPlayIndex') ?? 1),
|
||||
'core_plot_or_positioning' => (string)($arrVideo['v_description'] ?? ''),
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
private function normalizeSeoCopyBlock(string $strScene, array $arrData): array
|
||||
{
|
||||
$arrTemplate = SeoCopySchema::getSceneTemplate($strScene);
|
||||
$arrMerged = array_merge($arrTemplate, $arrData);
|
||||
|
||||
$arrGuideCards = [];
|
||||
foreach ((array)($arrMerged['guide_cards'] ?? []) as $arrCard) {
|
||||
if (!is_array($arrCard)) {
|
||||
continue;
|
||||
}
|
||||
$strTitle = trim((string)($arrCard['title'] ?? ''));
|
||||
$strText = trim((string)($arrCard['text'] ?? ''));
|
||||
$strHref = trim((string)($arrCard['href'] ?? ''));
|
||||
if ($strTitle === '' && $strText === '') {
|
||||
continue;
|
||||
}
|
||||
$arrItem = [
|
||||
'title' => $strTitle,
|
||||
'text' => $strText,
|
||||
];
|
||||
if ($strHref !== '') {
|
||||
$arrItem['href'] = $strHref;
|
||||
}
|
||||
$arrGuideCards[] = $arrItem;
|
||||
}
|
||||
$arrMerged['guide_cards'] = $arrGuideCards;
|
||||
$arrMerged['_scene'] = $strScene;
|
||||
|
||||
return $arrMerged;
|
||||
}
|
||||
|
||||
private function interpolateSeoCopyData(array $arrData, array $arrTokens): array
|
||||
{
|
||||
foreach ($arrData as $strKey => $mValue) {
|
||||
$arrData[$strKey] = $this->interpolateSeoCopyValue($mValue, $arrTokens);
|
||||
}
|
||||
|
||||
return $arrData;
|
||||
}
|
||||
|
||||
private function interpolateSeoCopyValue($mValue, array $arrTokens)
|
||||
{
|
||||
if (is_array($mValue)) {
|
||||
foreach ($mValue as $strKey => $mChildValue) {
|
||||
$mValue[$strKey] = $this->interpolateSeoCopyValue($mChildValue, $arrTokens);
|
||||
}
|
||||
|
||||
return $mValue;
|
||||
}
|
||||
|
||||
if (!is_string($mValue)) {
|
||||
return $mValue;
|
||||
}
|
||||
|
||||
return SeoCopyStore::interpolateTemplate($mValue, $arrTokens);
|
||||
}
|
||||
|
||||
private function buildSeoCopyTokens(string $strScene, array $arrOptions): array
|
||||
{
|
||||
$arrVideo = $this->toArraySafe($arrOptions['video'] ?? []);
|
||||
$strPlayType = trim((string)($arrOptions['play_type'] ?? request()->route('strPlayType')));
|
||||
$intPlayIndex = (int)($arrOptions['play_index'] ?? request()->route('intPlayIndex') ?? 1);
|
||||
$arrFacts = $this->buildSeoCopyFacts($strScene, $arrOptions);
|
||||
|
||||
$strSiteName = trim((string)($this->SiteContext->DomainModel->d_name ?? ''));
|
||||
$strVideoName = trim((string)($arrVideo['v_name'] ?? ''));
|
||||
$strYear = $this->normalizeSeoCopyYear($arrVideo);
|
||||
$strAreaName = $this->implodeSeoCopyScalar($arrVideo['v_area'] ?? '');
|
||||
$strLangName = $this->implodeSeoCopyScalar($arrVideo['v_lang'] ?? '');
|
||||
$strDirectorNames = $this->implodeSeoCopyScalar($arrVideo['v_director'] ?? '', 3);
|
||||
$strActorNames = $this->implodeSeoCopyScalar($arrVideo['v_actor'] ?? '', 4);
|
||||
$strRemarks = trim((string)($arrVideo['v_remarks'] ?? ''));
|
||||
$strVideoAlias = $this->implodeSeoCopyScalar($arrVideo['v_alias'] ?? ($arrVideo['v_aliases'] ?? ''), 3);
|
||||
$strPlayLine = $this->getConverterPlayLineVal($strPlayType);
|
||||
$strEpisodeName = $this->buildSeoCopyEpisodeLabel($arrVideo, $intPlayIndex);
|
||||
|
||||
$arrTokens = [
|
||||
'site_name' => $strSiteName,
|
||||
'video_name' => $strVideoName,
|
||||
'video_alias' => $strVideoAlias,
|
||||
'year' => $strYear,
|
||||
'area_name' => $strAreaName,
|
||||
'lang_name' => $strLangName,
|
||||
'director_names' => $strDirectorNames,
|
||||
'actor_names' => $strActorNames,
|
||||
'remarks' => $strRemarks,
|
||||
'play_line' => $strPlayLine,
|
||||
'play_type' => $strPlayType,
|
||||
'episode_name' => $strEpisodeName,
|
||||
'episode_label_or_index' => $strEpisodeName,
|
||||
'category_name' => trim((string)($arrVideo['v_category'] ?? '')),
|
||||
'scene' => $strScene,
|
||||
];
|
||||
|
||||
$arrKnownFactKeys = [
|
||||
'keyword',
|
||||
'parent_category_name',
|
||||
'category_name',
|
||||
'rank_period_name',
|
||||
'rank_scope_name',
|
||||
'search_path',
|
||||
'rank_path',
|
||||
];
|
||||
|
||||
foreach ($arrKnownFactKeys as $strFactKey) {
|
||||
if (!isset($arrFacts[$strFactKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrTokens[$strFactKey] = $this->implodeSeoCopyScalar($arrFacts[$strFactKey]);
|
||||
}
|
||||
|
||||
return array_map(static function ($mValue): string {
|
||||
return trim((string)$mValue);
|
||||
}, $arrTokens);
|
||||
}
|
||||
|
||||
private function normalizeSeoCopyYear(array $arrVideo): string
|
||||
{
|
||||
$strYear = trim((string)($arrVideo['v_year'] ?? ''));
|
||||
if ($strYear !== '') {
|
||||
if (preg_match('/\b(\d{4})\b/u', $strYear, $arrMatches) === 1) {
|
||||
return $arrMatches[1];
|
||||
}
|
||||
|
||||
return $strYear;
|
||||
}
|
||||
|
||||
$strPublishDate = trim((string)($arrVideo['v_publish_date'] ?? ''));
|
||||
if ($strPublishDate !== '' && preg_match('/\b(\d{4})\b/u', $strPublishDate, $arrMatches) === 1) {
|
||||
return $arrMatches[1];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function implodeSeoCopyScalar($mValue, int $intLimit = 0): string
|
||||
{
|
||||
if (is_string($mValue)) {
|
||||
return trim($mValue);
|
||||
}
|
||||
|
||||
if (!is_array($mValue)) {
|
||||
return trim((string)$mValue);
|
||||
}
|
||||
|
||||
$arrItems = array_values(array_filter(array_map(static function ($mItem): string {
|
||||
return trim((string)$mItem);
|
||||
}, $mValue), static function (string $strVal): bool {
|
||||
return $strVal !== '';
|
||||
}));
|
||||
|
||||
if ($intLimit > 0) {
|
||||
$arrItems = array_slice($arrItems, 0, $intLimit);
|
||||
}
|
||||
|
||||
return implode('、', $arrItems);
|
||||
}
|
||||
|
||||
private function buildSeoCopyEpisodeLabel(array $arrVideo, int $intPlayIndex): string
|
||||
{
|
||||
$intPlayIndex = max(1, $intPlayIndex);
|
||||
$arrPlayFrom = (array)($arrVideo['v_play_from'] ?? []);
|
||||
$arrPlayUrl = (array)($arrVideo['v_play_url'] ?? []);
|
||||
$strPlayType = trim((string)(request()->route('strPlayType') ?? ''));
|
||||
|
||||
if ($strPlayType !== '' && isset($arrPlayFrom[$strPlayType]) && !empty($arrPlayUrl[$strPlayType][$intPlayIndex - 1]['p_title'])) {
|
||||
return trim((string)$arrPlayUrl[$strPlayType][$intPlayIndex - 1]['p_title']);
|
||||
}
|
||||
|
||||
return '第' . $intPlayIndex . '集';
|
||||
}
|
||||
|
||||
private function extractHomeContentScopes(): array
|
||||
{
|
||||
$arrCategories = (array)($this->SiteContext->TpStyle['template_cfg']['pages']['home']['categories'] ?? []);
|
||||
$arrOut = [];
|
||||
foreach ($arrCategories as $arrCategory) {
|
||||
$strTitle = $this->resolveDisplayText($arrCategory['title_text'] ?? ($arrCategory['name'] ?? $arrCategory['key'] ?? ''));
|
||||
if ($strTitle !== '') {
|
||||
$arrOut[] = $strTitle;
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($arrOut) ? array_slice(array_values(array_unique($arrOut)), 0, 5) : ['电影', '电视剧', '综艺'];
|
||||
}
|
||||
|
||||
private function extractCategoryExamples(string $strParentCategory): array
|
||||
{
|
||||
$arrMap = (array)($this->SiteContext->TpStyle['template_cfg']['pages']['category']['cat1_map'][$strParentCategory]['subcat_slots'] ?? []);
|
||||
$arrOut = [];
|
||||
foreach ($arrMap as $arrItem) {
|
||||
$strTitle = $this->resolveDisplayText($arrItem['title_text'] ?? ($arrItem['name'] ?? $arrItem['key'] ?? ''));
|
||||
if ($strTitle !== '') {
|
||||
$arrOut[] = $strTitle;
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($arrOut) ? array_slice(array_values(array_unique($arrOut)), 0, 4) : [];
|
||||
}
|
||||
|
||||
private function extractRankPeriods(): array
|
||||
{
|
||||
$arrSlots = (array)($this->SiteContext->TpStyle['template_cfg']['pages']['rank_home']['slots'] ?? []);
|
||||
$arrOut = [];
|
||||
foreach ($arrSlots as $arrSlot) {
|
||||
$strSortType = trim((string)($arrSlot['sort_type'] ?? ''));
|
||||
if ($strSortType !== '') {
|
||||
$arrOut[] = $this->resolveRankSortLabel($strSortType);
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($arrOut) ? array_slice(array_values(array_unique($arrOut)), 0, 4) : ['日榜', '周榜', '月榜'];
|
||||
}
|
||||
|
||||
private function resolveCategoryLabel(string $strCategory): string
|
||||
{
|
||||
$strCategory = trim($strCategory);
|
||||
if ($strCategory === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$arrFlatMap = (array)VideoCategoryModel::$arrCategory;
|
||||
if (isset($arrFlatMap[$strCategory])) {
|
||||
return trim((string)$arrFlatMap[$strCategory]);
|
||||
}
|
||||
|
||||
foreach ((array)VideoCategoryModel::getCustomCategory('ONE') as $arrParentCategory) {
|
||||
if ((string)($arrParentCategory['v_category_en'] ?? '') === $strCategory) {
|
||||
return trim((string)($arrParentCategory['v_category'] ?? $strCategory));
|
||||
}
|
||||
|
||||
foreach ((array)($arrParentCategory['children'] ?? []) as $arrChildCategory) {
|
||||
if ((string)($arrChildCategory['v_category_en'] ?? '') === $strCategory) {
|
||||
return trim((string)($arrChildCategory['v_category'] ?? $strCategory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $strCategory;
|
||||
}
|
||||
|
||||
private function resolveRankSortLabel(string $strSortType): string
|
||||
{
|
||||
return match (trim(strtolower($strSortType))) {
|
||||
'daily' => '日榜',
|
||||
'weekly' => '周榜',
|
||||
'monthly' => '月榜',
|
||||
default => $strSortType !== '' ? $strSortType : '榜单',
|
||||
};
|
||||
}
|
||||
|
||||
private function resolveRankScopeName(string $strParentCategory, string $strCategory): string
|
||||
{
|
||||
$strParentLabel = $this->resolveCategoryLabel($strParentCategory);
|
||||
$strCategoryLabel = $this->resolveCategoryLabel($strCategory);
|
||||
|
||||
if ($strParentLabel !== '' && $strCategoryLabel !== '' && $strCategoryLabel !== 'all') {
|
||||
return $strParentLabel . ' / ' . $strCategoryLabel;
|
||||
}
|
||||
|
||||
if ($strParentLabel !== '') {
|
||||
return $strParentLabel;
|
||||
}
|
||||
|
||||
return '当前范围';
|
||||
}
|
||||
|
||||
private function resolveDisplayText($mValue): string
|
||||
{
|
||||
if (is_array($mValue)) {
|
||||
foreach (['primary', 'seo', 'secondary', 'name', 'title', 'text'] as $strKey) {
|
||||
$strVal = trim((string)($mValue[$strKey] ?? ''));
|
||||
if ($strVal !== '') {
|
||||
return $strVal;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($mValue as $mItem) {
|
||||
$strVal = $this->resolveDisplayText($mItem);
|
||||
if ($strVal !== '') {
|
||||
return $strVal;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return trim((string)$mValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"detail_body": "本页围绕《色降2之血玫瑰》整理剧情简介、题材信息与播放线索,方便先确认内容方向。",
|
||||
"detail_body_lead": "本页围绕《色降2之血玫瑰》整理剧情简介、题材信息与播放线索,方便先确认内容方向。",
|
||||
"detail_play_link_lead": "如果准备继续观看,可直接进入",
|
||||
"detail_body_tail": "如果想换个切口继续了解这部内容,可以继续浏览延伸词入口或返回分类页。",
|
||||
"detail_faq": "常见问题:如果你已经确认要看这部内容,可以先看剧情简介,再进入播放页;如果还想补充检索角度,也可以先回到分类页继续筛选。",
|
||||
"main_description": "《色降2之血玫瑰》当前归类到伦理片,参考年份为1992。泰国一名邪恶女巫绑架并洗脑美丽女性,强迫她们加入她的邪教。一名男子和他的女友为了寻找失踪的妹妹,与一名牧师和一名当地警察联手对抗邪教。"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"play_intro": "当前播放页提供《色降2之血玫瑰》的douban线路入口,当前可继续浏览的集数位置为正片。",
|
||||
"play_meta_note": "如果当前线路不合适,可以先回详情页确认内容归属,再切换其他播放入口继续浏览。",
|
||||
"play_body_lead": "播放器下方正文主要帮助补充《色降2之血玫瑰》的剧情、题材和返回路径信息。",
|
||||
"play_body_next": "如果想继续确认内容信息,可以回详情页;如果想换范围继续看,也可以返回题材页。"
|
||||
}
|
||||
@@ -11,7 +11,7 @@ class Video extends TagLib
|
||||
'listexp' => ['attr' => 'count,v_category_en,v_parent_category_en,sort_type,v_status,v_sex,diff_key,cache_life,d_key,d_val,export_name', 'close' => 0],
|
||||
'pager' => ['attr' => 'page,limit,v_category_en,v_parent_category_en,sort_type,v_status,v_sex,key,diff_key,cache_life,d_key,d_val,p_val,func,export_name', 'close' => 1],
|
||||
'pagerexp' => ['attr' => 'page,limit,v_class,v_category_en,v_parent_category_en,sort_type,v_status,v_sex,key,diff_key,cache_life,d_key,d_val,p_val,func,export_name', 'close' => 0],
|
||||
'info' => ['attr' => 'v_id,v_forge_id,v_key', 'close' => 0],
|
||||
'info' => ['attr' => 'v_id,v_forge_id,v_key,v_pinyin', 'close' => 0],
|
||||
'sort' => ['attr' => 'd_key,d_val'],
|
||||
'ranksort' => ['attr' => 'd_key,d_val'],
|
||||
'status' => ['attr' => 'd_key,d_val'],
|
||||
@@ -29,6 +29,7 @@ class Video extends TagLib
|
||||
|
||||
'imgalt' => ['attr' => 'video,tp_style,slot,item_type,export_name', 'close' => 0],
|
||||
'seoaddon' => ['attr' => 'video,tp_style,export_name', 'close' => 0],
|
||||
'seocopy' => ['attr' => 'scene,video,parent_category,category,keyword,sort_type,page,total,pages,visible,play_type,play_index,export_name', 'close' => 0],
|
||||
|
||||
];
|
||||
|
||||
@@ -132,6 +133,72 @@ EOD;
|
||||
return $strParse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面补料块(优先 seo_copy,缺失走 fallback)
|
||||
*
|
||||
* 用法:
|
||||
* {video:seocopy scene="home" export_name="seoCopy" /}
|
||||
* {video:seocopy scene="detail" video="$arrVideo" export_name="seoCopy" /}
|
||||
*/
|
||||
public function tagSeoCopy($tag)
|
||||
{
|
||||
$strScene = $this->customBuildVar($tag['scene'] ?? 'home');
|
||||
$strVideo = $this->customBuildVar($tag['video'] ?? '[]');
|
||||
$strParentCategory = $this->customBuildVar($tag['parent_category'] ?? '');
|
||||
$strCategory = $this->customBuildVar($tag['category'] ?? '');
|
||||
$strKeyword = $this->customBuildVar($tag['keyword'] ?? '');
|
||||
$strSortType = $this->customBuildVar($tag['sort_type'] ?? '');
|
||||
$intPage = $this->customBuildVar($tag['page'] ?? 1);
|
||||
$intTotal = $this->customBuildVar($tag['total'] ?? 0);
|
||||
$intPages = $this->customBuildVar($tag['pages'] ?? 1);
|
||||
$intVisible = $this->customBuildVar($tag['visible'] ?? 0);
|
||||
$strPlayType = $this->customBuildVar($tag['play_type'] ?? '');
|
||||
$intPlayIndex = $this->customBuildVar($tag['play_index'] ?? 1);
|
||||
|
||||
$export_name = $tag['export_name'] ?? '';
|
||||
$strDataName = randomString(8);
|
||||
|
||||
if (!empty($export_name) && !preg_match('/^[A-Za-z_]\w*$/', $export_name)) {
|
||||
$export_name = '';
|
||||
}
|
||||
|
||||
$strParse = <<<EOD
|
||||
<?php
|
||||
\${$strDataName} = app(\\app\\services\\VideoService::class)->getSeoCopyBlock({$strScene}, [
|
||||
'video' => {$strVideo},
|
||||
'parent_category' => {$strParentCategory},
|
||||
'category' => {$strCategory},
|
||||
'keyword' => {$strKeyword},
|
||||
'sort_type' => {$strSortType},
|
||||
'page' => {$intPage},
|
||||
'total' => {$intTotal},
|
||||
'pages' => {$intPages},
|
||||
'visible' => {$intVisible},
|
||||
'play_type' => {$strPlayType},
|
||||
'play_index' => {$intPlayIndex},
|
||||
'pager_data' => [
|
||||
'page' => {$intPage},
|
||||
'total' => {$intTotal},
|
||||
'pages' => {$intPages},
|
||||
],
|
||||
]);
|
||||
EOD;
|
||||
|
||||
if (empty($export_name)) {
|
||||
$strParse .= <<<EOD
|
||||
echo json_encode(\${$strDataName}, JSON_UNESCAPED_UNICODE);
|
||||
?>
|
||||
EOD;
|
||||
} else {
|
||||
$strParse .= <<<EOD
|
||||
\${$export_name} = \${$strDataName};
|
||||
?>
|
||||
EOD;
|
||||
}
|
||||
|
||||
return $strParse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tagRankListExp
|
||||
@@ -439,16 +506,20 @@ EOT;
|
||||
|
||||
$v_key = $tag['v_key'] ?? 'arrVideo';
|
||||
|
||||
$v_pinyin = $tag['v_pinyin'] ?? '';
|
||||
|
||||
$v_id = $this->customBuildVar($v_id);
|
||||
$v_fid = $this->customBuildVar($v_fid);
|
||||
$v_pinyin = $this->customBuildVar($v_pinyin);
|
||||
|
||||
$arrDataName = randomString(5);
|
||||
|
||||
$strParse = <<<EOT
|
||||
<?php
|
||||
\${$arrDataName} = app(\\app\\services\\VideoService::class)->getVideoByVId(
|
||||
\${$arrDataName} = app(\\app\\services\\VideoService::class)->getVideoByRouteContext(
|
||||
{$v_id},
|
||||
{$v_fid}
|
||||
{$v_fid},
|
||||
{$v_pinyin}
|
||||
);
|
||||
|
||||
\${$v_key} = \${$arrDataName} ?? [];
|
||||
|
||||
@@ -9,12 +9,36 @@
|
||||
* =====================================================
|
||||
*/
|
||||
|
||||
function initDPlayer(strPlayUrl) {
|
||||
function ensurePrefix(root) {
|
||||
if (!root || root.dataset.prefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cls = root.className || '';
|
||||
var m = cls.match(/([a-z0-9]{4,8})-player/);
|
||||
if (m) {
|
||||
root.dataset.prefix = m[1];
|
||||
}
|
||||
}
|
||||
|
||||
function resolveContainer(root) {
|
||||
if (root && root.querySelector) {
|
||||
return root.querySelector('.dplayer, [id="dplayer"]');
|
||||
}
|
||||
|
||||
return document.getElementById('dplayer') || document.querySelector('.dplayer');
|
||||
}
|
||||
|
||||
function initDPlayer(root, strPlayUrl) {
|
||||
var DomDPlayer = resolveContainer(root);
|
||||
if (!DomDPlayer) {
|
||||
showError(root, '播放器容器不存在');
|
||||
return null;
|
||||
}
|
||||
|
||||
let DomDPlayer = document.getElementById('dplayer')
|
||||
// ---- 创建 DPlayer 实例 ----
|
||||
try {
|
||||
videoPlayer = new DPlayer({
|
||||
var videoPlayer = new DPlayer({
|
||||
container: DomDPlayer,
|
||||
autoplay: true,
|
||||
screenshot: false,
|
||||
@@ -37,13 +61,22 @@
|
||||
showError(root, '播放失败,请尝试切换线路');
|
||||
});
|
||||
|
||||
return videoPlayer;
|
||||
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
showError(root, '播放器初始化失败');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function showError(root, msg) {
|
||||
if (!root || !root.querySelector) {
|
||||
console.warn(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
ensurePrefix(root);
|
||||
var err = root.querySelector('.' + root.dataset.prefix + '-player-status');
|
||||
if (!err) {
|
||||
err = document.createElement('div');
|
||||
@@ -58,16 +91,7 @@
|
||||
if (!players.length) return;
|
||||
|
||||
players.forEach(function (root) {
|
||||
// dom_prefix 注入(防串)
|
||||
if (!root.dataset.prefix) {
|
||||
var cls = root.className || '';
|
||||
var m = cls.match(/([a-z0-9]{4,8})-player/);
|
||||
if (m) {
|
||||
root.dataset.prefix = m[1];
|
||||
}
|
||||
}
|
||||
console.log(root)
|
||||
initDPlayer(root);
|
||||
ensurePrefix(root);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,6 +124,10 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
try {
|
||||
if (typeof strVideoId == "undefined") return;
|
||||
var root = document.querySelector('[data-engine="dplayer"]');
|
||||
if (!root) return;
|
||||
|
||||
ensurePrefix(root);
|
||||
|
||||
console.log(strPlayType)
|
||||
console.log(boolIsPlayPage)
|
||||
@@ -118,7 +146,7 @@
|
||||
// checkLine(defaultLine);
|
||||
}
|
||||
console.log('strPlayUrlstrPlayUrl')
|
||||
initDPlayer(strPlayUrl);
|
||||
initDPlayer(root, strPlayUrl);
|
||||
|
||||
} else if (strPlayType == "default" && !boolIsPlayPage) {
|
||||
|
||||
@@ -126,13 +154,12 @@
|
||||
strPlayUrl = getFirstPlayUrl(defaultLine);
|
||||
// checkLine(defaultLine);
|
||||
|
||||
initDPlayer(strPlayUrl);
|
||||
initDPlayer(root, strPlayUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user