From 2a905bbf0f8eeb35b982895925e41a2dd3b9f135 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Apr 2026 22:45:00 +0800 Subject: [PATCH] restore gpt seo copy pipeline and stabilize seo tkd output --- .../common/helper/SeoCopyFallbackBuilder.php | 6 + code/app/common/helper/SeoCopyStore.php | 88 +- code/app/common/helper/SeoWordsHelper.php | 44 +- code/app/common/helper/UrlBuilder.php | 11 +- code/app/home/config/router.php | 26 +- code/app/home/view/videoGpt1/index/index.html | 120 +- .../videoGpt1/module/detail_main/desc.html | 200 +- .../videoGpt1/module/seo_copy/collection.html | 57 + .../videoGpt1/module/seo_copy/detail.html | 55 + .../view/videoGpt1/module/seo_copy/play.html | 55 + .../view/videoGpt1/video/getVideoPlayUrl.html | 79 +- code/app/services/SiteContext.php | 378 +++- code/app/services/VideoService.php | 588 +++++- .../jpjdxs-com/detail/76342.json | 8 + .../jpjdxs-com/play/76342-play-douban-1.json | 6 + code/extend/template/taglib/Video.php | 77 +- .../static/js/modules/player/dplayer.init.js | 61 +- ...1270-SEONexus-Codex多版本协作交接总文档.md | 1813 +++++++++++++++++ 18 files changed, 3587 insertions(+), 85 deletions(-) create mode 100644 code/app/home/view/videoGpt1/module/seo_copy/collection.html create mode 100644 code/app/home/view/videoGpt1/module/seo_copy/detail.html create mode 100644 code/app/home/view/videoGpt1/module/seo_copy/play.html create mode 100644 code/data/seo_copy_published/jpjdxs-com/detail/76342.json create mode 100644 code/data/seo_copy_published/jpjdxs-com/play/76342-play-douban-1.json diff --git a/code/app/common/helper/SeoCopyFallbackBuilder.php b/code/app/common/helper/SeoCopyFallbackBuilder.php index 2ee94ad..184e484 100644 --- a/code/app/common/helper/SeoCopyFallbackBuilder.php +++ b/code/app/common/helper/SeoCopyFallbackBuilder.php @@ -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' => [ diff --git a/code/app/common/helper/SeoCopyStore.php b/code/app/common/helper/SeoCopyStore.php index 7011e1a..d8f5ec5 100644 --- a/code/app/common/helper/SeoCopyStore.php +++ b/code/app/common/helper/SeoCopyStore.php @@ -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 diff --git a/code/app/common/helper/SeoWordsHelper.php b/code/app/common/helper/SeoWordsHelper.php index 3c20011..090741e 100644 --- a/code/app/common/helper/SeoWordsHelper.php +++ b/code/app/common/helper/SeoWordsHelper.php @@ -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 = []; diff --git a/code/app/common/helper/UrlBuilder.php b/code/app/common/helper/UrlBuilder.php index dab8bbf..c8b03a3 100644 --- a/code/app/common/helper/UrlBuilder.php +++ b/code/app/common/helper/UrlBuilder.php @@ -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); + } } diff --git a/code/app/home/config/router.php b/code/app/home/config/router.php index 23c0a74..0af920c 100644 --- a/code/app/home/config/router.php +++ b/code/app/home/config/router.php @@ -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' => [ diff --git a/code/app/home/view/videoGpt1/index/index.html b/code/app/home/view/videoGpt1/index/index.html index 0388fa1..9cc743a 100644 --- a/code/app/home/view/videoGpt1/index/index.html +++ b/code/app/home/view/videoGpt1/index/index.html @@ -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"} @@ -14,8 +100,8 @@ {// 社交媒体标签 } - - + + {// 结构化数据 } @@ -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"}
+
+
+ + 首页导览 +
+

{$strHomeIntroTitle}

+

{$strHomeIntroText}

+

{$strHomeIntroMeta}

+
+ +
+ {foreach $arrHomeQuickSignals as $arrSignal} +
+ {$arrSignal.label} + {$arrSignal.text} +
+ {/foreach} +
+ {// 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 @@
{/block} - diff --git a/code/app/home/view/videoGpt1/module/detail_main/desc.html b/code/app/home/view/videoGpt1/module/detail_main/desc.html index 670e1e0..973b0d8 100644 --- a/code/app/home/view/videoGpt1/module/detail_main/desc.html +++ b/code/app/home/view/videoGpt1/module/detail_main/desc.html @@ -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} + + + {if $variant == 0}

- {$arrVideo.v_description} + {$strDetailMainDescription}

{elseif $variant == 1}
-

{$arrVideo.v_description|mb_substr=0,120}

+

{$strDetailMainDescription|mb_substr=0,120}

{elseif $variant == 2}
-

{$arrVideo.v_description|mb_substr=0,200}

+

{$strDetailMainDescription|mb_substr=0,200}

{elseif $variant == 3}
-

{$arrVideo.v_description}

+

{$strDetailMainDescription}

{elseif $variant == 4}

- 剧情简介:{$arrVideo.v_description|mb_substr=0,150} + 剧情简介:{$strDetailMainDescription|mb_substr=0,150}

{elseif $variant == 5}
剧情介绍 -

{$arrVideo.v_description}

+

{$strDetailMainDescription}

{elseif $variant == 6}
-

{$arrVideo.v_description|mb_substr=0,100}...

+

{$strDetailMainDescription|mb_substr=0,100}...

查看详情
{elseif $variant == 7}
影片简介
-

{$arrVideo.v_description|mb_substr=0,180}

+

{$strDetailMainDescription|mb_substr=0,180}

{elseif $variant == 8}
- {$arrVideo.v_description|mb_substr=0,160} + {$strDetailMainDescription|mb_substr=0,160}
{elseif $variant == 9}

{$arrVideo.v_name}({$arrVideo.v_year})剧情: - {$arrVideo.v_description|mb_substr=0,140} + {$strDetailMainDescription|mb_substr=0,140}

{elseif $variant == 10}

- {$arrVideo.v_description|mb_substr=0,160} + {$strDetailMainDescription|mb_substr=0,160}

{elseif $variant == 11}
-

{$arrVideo.v_description|mb_substr=0,120}

+ data-desc="{$strDetailMainDescription|mb_substr=0,200}"> +

{$strDetailMainDescription|mb_substr=0,120}

{elseif $variant == 12}

- {$arrVideo.v_description|mb_substr=0,100} + {$strDetailMainDescription|mb_substr=0,100} {$arrVideo.v_name} 在线观看 @@ -84,7 +211,7 @@ {elseif $variant == 13}

- {$arrVideo.v_description|mb_substr=0,130} + {$strDetailMainDescription|mb_substr=0,130} 高清完整版内容介绍。

@@ -92,7 +219,7 @@ {elseif $variant == 14}

- {$arrVideo.v_description|mb_substr=0,150} + {$strDetailMainDescription|mb_substr=0,150}

本片支持在线播放 @@ -101,7 +228,7 @@ {elseif $variant == 15}

- {$arrVideo.v_description|mb_substr=0,120} + {$strDetailMainDescription|mb_substr=0,120} 免费观看{$arrVideo.v_name}

@@ -109,30 +236,59 @@

{$arrVideo.v_name} - {$arrVideo.v_description|mb_substr=0,140} + {$strDetailMainDescription|mb_substr=0,140}

{elseif $variant == 17}
-

{$arrVideo.v_description|mb_substr=0,180}

+

{$strDetailMainDescription|mb_substr=0,180}

{elseif $variant == 18}

- {$arrVideo.v_description} + {$strDetailMainDescription}

{elseif $variant == 19}
剧情简介
-

{$arrVideo.v_description|mb_substr=0,160}

+

{$strDetailMainDescription|mb_substr=0,160}

{/if} +{notempty name="$strDetailBody"} +
+

{$strDetailBody}

+
+{/notempty} + +{if empty($boolIsForgeDetail)} +{include file="module/seo_copy/detail" /} +{/if} + +{if !empty($boolIsForgeDetail)} +
+

{$strForgeBodyLead|default='当前页围绕延伸词补充主片相关信息,适合先确认内容归属。'}

+

+ {notempty name="$strMainVideoInfoUrl"} + {$strForgeReturnLead|default='如果想回到标准详情,可返回'} 《{$strOriginVideoName}》详情页; + {/notempty} + {notempty name="$strPreferredPlayUrl"} + {$strForgePlayLead|default='如果准备继续观看,也可以直接进入'} 播放页。 + {/notempty} +

+
+
+

{$strForgeDetailNote|default='当前页提供的是延伸词视角,适合先确认内容归属,再决定是否回到标准详情或播放页。'}

+ {notempty name="$strMainVideoInfoUrl"} +

如果你想继续查看主内容页,可以回到 《{$strOriginVideoName}》详情页

+ {/notempty} +
+{/if} + {video:seoaddon video="$arrVideo" export_name="arrAddon" /} {include file="module/detail/_seo_addon" /} - diff --git a/code/app/home/view/videoGpt1/module/seo_copy/collection.html b/code/app/home/view/videoGpt1/module/seo_copy/collection.html new file mode 100644 index 0000000..73767c6 --- /dev/null +++ b/code/app/home/view/videoGpt1/module/seo_copy/collection.html @@ -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)} +
+ + {notempty name="arrGuideLines"} +
+ {foreach $arrGuideLines as $strGuideLine} +

+ {$strGuideLine} +

+ {/foreach} +
+ {/notempty} + + {notempty name="arrGuideCards"} +
+ {foreach $arrGuideCards as $arrGuideCard} +
+ {notempty name="$arrGuideCard.title"} +

+ {$arrGuideCard.title} +

+ {/notempty} + {notempty name="$arrGuideCard.text"} +

+ {$arrGuideCard.text} +

+ {/notempty} +
+ {/foreach} +
+ {/notempty} +
+{/if} diff --git a/code/app/home/view/videoGpt1/module/seo_copy/detail.html b/code/app/home/view/videoGpt1/module/seo_copy/detail.html new file mode 100644 index 0000000..016cd0e --- /dev/null +++ b/code/app/home/view/videoGpt1/module/seo_copy/detail.html @@ -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)} +
+ {notempty name="arrGuideLines"} +
+ {foreach $arrGuideLines as $strGuideLine} +

+ {$strGuideLine} +

+ {/foreach} +
+ {/notempty} + + {notempty name="arrGuideCards"} +
+ {foreach $arrGuideCards as $arrGuideCard} +
+ {notempty name="$arrGuideCard.title"} +

+ {$arrGuideCard.title} +

+ {/notempty} + {notempty name="$arrGuideCard.text"} +

+ {$arrGuideCard.text} +

+ {/notempty} +
+ {/foreach} +
+ {/notempty} +
+{/if} diff --git a/code/app/home/view/videoGpt1/module/seo_copy/play.html b/code/app/home/view/videoGpt1/module/seo_copy/play.html new file mode 100644 index 0000000..4c914b4 --- /dev/null +++ b/code/app/home/view/videoGpt1/module/seo_copy/play.html @@ -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)} +
+ {notempty name="arrGuideLines"} +
+ {foreach $arrGuideLines as $strGuideLine} +

+ {$strGuideLine} +

+ {/foreach} +
+ {/notempty} + + {notempty name="arrGuideCards"} +
+ {foreach $arrGuideCards as $arrGuideCard} +
+ {notempty name="$arrGuideCard.title"} +

+ {$arrGuideCard.title} +

+ {/notempty} + {notempty name="$arrGuideCard.text"} +

+ {$arrGuideCard.text} +

+ {/notempty} +
+ {/foreach} +
+ {/notempty} +
+{/if} diff --git a/code/app/home/view/videoGpt1/video/getVideoPlayUrl.html b/code/app/home/view/videoGpt1/video/getVideoPlayUrl.html index 3bb6877..569b8d4 100644 --- a/code/app/home/view/videoGpt1/video/getVideoPlayUrl.html +++ b/code/app/home/view/videoGpt1/video/getVideoPlayUrl.html @@ -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" /} +