Compare commits
5 Commits
09b2137fa6
...
0c534d2903
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c534d2903 | ||
|
|
56644e7f50 | ||
|
|
dd3cbf09cc | ||
|
|
fc61444993 | ||
|
|
2a905bbf0f |
2
code/.gitignore
vendored
2
code/.gitignore
vendored
@@ -17,4 +17,6 @@ Thumbs.db
|
||||
public/static/css/compiled/*
|
||||
public/static/js/compiled/*
|
||||
public/static/favicon/generated/*
|
||||
/public/_seo_copy_release/*
|
||||
/data/seo_copy_published/*
|
||||
/storage/*
|
||||
|
||||
@@ -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,4 +1,10 @@
|
||||
<article class="{$TpStyle.dom_prefix}-d1-wrap">
|
||||
{php}
|
||||
$strDetailPlaySlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($strDetailPlaySlug === '') {
|
||||
$strDetailPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
|
||||
{// 顶部基本信息 }
|
||||
<header class="{$TpStyle.dom_prefix}-d1-hd">
|
||||
@@ -42,14 +48,17 @@
|
||||
<?php $keyLine = 0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $keyLine++ ;?>
|
||||
{php}
|
||||
$strEpisodePlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailPlaySlug,
|
||||
(string)$strPlayGroupName,
|
||||
(int)$keyLine
|
||||
);
|
||||
{/php}
|
||||
<li>
|
||||
<a class="{if $keyLine == $Request.route.intPlayIndex}active{/if}"
|
||||
href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$keyLine"
|
||||
/}'
|
||||
href="{$strEpisodePlayUrl}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
</a>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<main class="{$TpStyle.dom_prefix}-d2-page">
|
||||
{php}
|
||||
$strDetailPlaySlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($strDetailPlaySlug === '') {
|
||||
$strDetailPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
|
||||
{// 顶部大横图 }
|
||||
<section class="{$TpStyle.dom_prefix}-d2-hero">
|
||||
@@ -45,8 +51,16 @@
|
||||
<?php $keyLine = 0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $keyLine++ ;?>
|
||||
{php}
|
||||
$strEpisodePlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailPlaySlug,
|
||||
(string)$strPlayGroupName,
|
||||
(int)$keyLine
|
||||
);
|
||||
{/php}
|
||||
<li>
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="$strPlayGroupName" play_index="$keyLine" /}'
|
||||
<a href="{$strEpisodePlayUrl}"
|
||||
class="{if $keyLine == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<article class="{$TpStyle.dom_prefix}-d3-page">
|
||||
{php}
|
||||
$strDetailPlaySlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($strDetailPlaySlug === '') {
|
||||
$strDetailPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
|
||||
<section class="{$TpStyle.dom_prefix}-d3-top">
|
||||
<div class="{$TpStyle.dom_prefix}-d3-picbox">
|
||||
@@ -32,12 +38,20 @@
|
||||
<?php $keyLineGroup = 0; ?>
|
||||
{foreach $arrVideo.v_play_url as $strPlayGroupName=>$arrPlayUrlType }
|
||||
<?php $keyLineGroup++ ;?>
|
||||
<div class="{$TpStyle.dom_prefix}-d3-epbox {eq name='$keyLineGroup' value='1'}active{/eq}"
|
||||
<div class="{$TpStyle.dom_prefix}-d3-epbox {eq name='$keyLineGroup' value='1'}active{/eq}"
|
||||
id="d3_box_{$strPlayGroupName}">
|
||||
<?php $keyLine = 0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $keyLine++ ;?>
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="$strPlayGroupName" play_index="$keyLine" /}'
|
||||
{php}
|
||||
$strEpisodePlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailPlaySlug,
|
||||
(string)$strPlayGroupName,
|
||||
(int)$keyLine
|
||||
);
|
||||
{/php}
|
||||
<a href="{$strEpisodePlayUrl}"
|
||||
class="{$TpStyle.dom_prefix}-d3-epitem {if $keyLine == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<section class="{$TpStyle.dom_prefix}-d4-page">
|
||||
{php}
|
||||
$strDetailPlaySlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($strDetailPlaySlug === '') {
|
||||
$strDetailPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
|
||||
<header class="{$TpStyle.dom_prefix}-d4-head">
|
||||
<h1>{$arrVideo.v_name}</h1>
|
||||
@@ -42,8 +48,16 @@
|
||||
<?php $keyLine = 0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $keyLine++ ;?>
|
||||
{php}
|
||||
$strEpisodePlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailPlaySlug,
|
||||
(string)$strPlayGroupName,
|
||||
(int)$keyLine
|
||||
);
|
||||
{/php}
|
||||
<li>
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="$strPlayGroupName" play_index="$keyLine" /}'
|
||||
<a href="{$strEpisodePlayUrl}"
|
||||
class="{if $keyLine == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<article class="{$TpStyle.dom_prefix}-d5-page">
|
||||
{php}
|
||||
$strDetailPlaySlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($strDetailPlaySlug === '') {
|
||||
$strDetailPlaySlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
|
||||
<header class="{$TpStyle.dom_prefix}-d5-head">
|
||||
<h1>{$arrVideo.v_name}</h1>
|
||||
@@ -35,7 +41,15 @@
|
||||
<?php $keyLine = 0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $keyLine++ ;?>
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="$strPlayGroupName" play_index="$keyLine" /}'
|
||||
{php}
|
||||
$strEpisodePlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailPlaySlug,
|
||||
(string)$strPlayGroupName,
|
||||
(int)$keyLine
|
||||
);
|
||||
{/php}
|
||||
<a href="{$strEpisodePlayUrl}"
|
||||
class="{$TpStyle.dom_prefix}-d5-epbtn {if $keyLine == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,111 +1,123 @@
|
||||
{// ===================== Action Variants ===================== }
|
||||
{php}
|
||||
$strDetailActionSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strDetailActionSlug === '') {
|
||||
$strDetailActionSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
$strDetailDefaultPlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailActionSlug,
|
||||
'default',
|
||||
1
|
||||
);
|
||||
{/php}
|
||||
|
||||
{if $variant == 0}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v0">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>立即播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">立即播放</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 1}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v1">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>在线播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">在线播放</a>
|
||||
<span>无需安装</span>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 2}
|
||||
<nav class="{$TpStyle.dom_prefix}-dm-action v2">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>▶ 播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">▶ 播放</a>
|
||||
<a href="#desc">剧情</a>
|
||||
</nav>
|
||||
|
||||
{elseif $variant == 3}
|
||||
<ul class="{$TpStyle.dom_prefix}-dm-action v3">
|
||||
<li><a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>高清</a></li>
|
||||
<li><a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>免费</a></li>
|
||||
<li><a href="{$strDetailDefaultPlayUrl}">高清</a></li>
|
||||
<li><a href="{$strDetailDefaultPlayUrl}">免费</a></li>
|
||||
</ul>
|
||||
|
||||
{elseif $variant == 4}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v4">
|
||||
<button onclick="location.href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'">立即观看</button>
|
||||
<button onclick="location.href='{$strDetailDefaultPlayUrl}'">立即观看</button>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 5}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v5">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>{$arrVideo.v_name} 在线观看</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">{$arrVideo.v_name} 在线观看</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 6}
|
||||
<footer class="{$TpStyle.dom_prefix}-dm-action v6">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>开始播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">开始播放</a>
|
||||
</footer>
|
||||
|
||||
{elseif $variant == 7}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-action v7">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>免费观看</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">免费观看</a>
|
||||
<small>高清资源</small>
|
||||
</section>
|
||||
|
||||
{elseif $variant == 8}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v8">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>播放正片</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">播放正片</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 9}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v9">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>立即观看 {$arrVideo.v_name}</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">立即观看 {$arrVideo.v_name}</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 10}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v10">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>在线播放 · 高清</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">在线播放 · 高清</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 11}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v11">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>点击播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">点击播放</a>
|
||||
<span>支持多线路</span>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 12}
|
||||
<nav class="{$TpStyle.dom_prefix}-dm-action v12">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>播放</a>
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>下载</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">下载</a>
|
||||
</nav>
|
||||
|
||||
{elseif $variant == 13}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v13">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>▶</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">▶</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 14}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v14">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>{$arrVideo.v_name} 免费看</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">{$arrVideo.v_name} 免费看</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 15}
|
||||
<aside class="{$TpStyle.dom_prefix}-dm-action v15">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>立即播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">立即播放</a>
|
||||
</aside>
|
||||
|
||||
{elseif $variant == 16}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v16"
|
||||
data-action="play">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>播放影片</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">播放影片</a>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 17}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-action v17">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>高清播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">高清播放</a>
|
||||
<em>无需登录</em>
|
||||
</div>
|
||||
|
||||
{elseif $variant == 18}
|
||||
<section class="{$TpStyle.dom_prefix}-dm-action v18">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>在线观看</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">在线观看</a>
|
||||
</section>
|
||||
|
||||
{elseif $variant == 19}
|
||||
<footer class="{$TpStyle.dom_prefix}-dm-action v19">
|
||||
<a href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>马上播放</a>
|
||||
<a href="{$strDetailDefaultPlayUrl}">马上播放</a>
|
||||
</footer>
|
||||
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
{// ===================== Cover Variants ===================== }
|
||||
{video:imgalt video="$arrVideo" slot="detail_cover" item_type="poster" export_name="strAlt" /}
|
||||
{php}
|
||||
$strDetailCoverSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strDetailCoverSlug === '') {
|
||||
$strDetailCoverSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
$strDetailCoverPlayUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)($arrVideo['v_id'] ?? 0),
|
||||
$strDetailCoverSlug,
|
||||
'default',
|
||||
1
|
||||
);
|
||||
{/php}
|
||||
|
||||
{if $variant == 0}
|
||||
<div class="{$TpStyle.dom_prefix}-dm-cover v0">
|
||||
@@ -20,7 +32,7 @@
|
||||
|
||||
{elseif $variant == 2}
|
||||
<a class="{$TpStyle.dom_prefix}-dm-cover v2"
|
||||
href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>
|
||||
href="{$strDetailCoverPlayUrl}">
|
||||
<img class="{$TpStyle.dom_prefix}-dm-cover-img"
|
||||
src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}"
|
||||
alt="{$strAlt}">
|
||||
@@ -89,7 +101,7 @@
|
||||
|
||||
{elseif $variant == 11}
|
||||
<a class="{$TpStyle.dom_prefix}-dm-cover v11"
|
||||
href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'>
|
||||
href="{$strDetailCoverPlayUrl}">
|
||||
<img class="{$TpStyle.dom_prefix}-dm-cover-bg" alt="{$strAlt}"
|
||||
src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}">
|
||||
</a>
|
||||
|
||||
@@ -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" /}
|
||||
|
||||
|
||||
@@ -18,6 +18,4 @@ $title = $Slot['title_text'] ?? null;
|
||||
{// ===== Shell + Item ===== }
|
||||
{include file="module/list/shell/_shell_router" /}
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
@@ -38,7 +38,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</nav>
|
||||
{/case}
|
||||
@@ -118,7 +118,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
{/case}
|
||||
@@ -198,7 +198,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</footer>
|
||||
{/case}
|
||||
@@ -278,7 +278,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</section>
|
||||
{/case}
|
||||
@@ -358,7 +358,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</aside>
|
||||
{/case}
|
||||
@@ -438,7 +438,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</nav>
|
||||
{/case}
|
||||
@@ -518,7 +518,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
{/case}
|
||||
@@ -598,7 +598,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</footer>
|
||||
{/case}
|
||||
@@ -678,7 +678,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</section>
|
||||
{/case}
|
||||
@@ -758,7 +758,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_url ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_url ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</aside>
|
||||
{/case}
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_urll ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_urll ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -745,7 +745,7 @@
|
||||
<label>跳到</label>
|
||||
<input class="{$TpStyle.dom_prefix}-pg-inp" type="number" min="1" max="{$pages}" value="{$page}">
|
||||
<button class="{$TpStyle.dom_prefix}-pg-btn" type="button"
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);p=Math.max(1,Math.min(max,p));var u='{:$first_urll ?? '#'}';if(!u){return;}u=u.replace(/page\d+$/i,'page'+p);window.location.href=u;})(this)">确定</button>
|
||||
onclick="(function(btn){var wrap=btn.parentNode;var inp=wrap.querySelector('input');var max=parseInt(wrap.getAttribute('data-pages')||1);var p=parseInt(inp.value||1);var u='{:$first_urll ?? '#'}';p=Math.max(1,Math.min(max,p));if(!u||u==='#'){return;}if(/[?&]page=\\d+/i.test(u)){u=u.replace(/([?&]page=)\\d+/i,'$1'+p);}else if(/[?&]page=\\{page\\}/i.test(u)){u=u.replace(/([?&]page=)\\{page\\}/i,'$1'+p);}else if(/-\\{page\\}(?=$|[?#])/i.test(u)){u=u.replace(/-\\{page\\}(?=$|[?#])/i,'-'+p);}else{u=u.replace(/-(\\d+)(?=$|[?#])/,'-'+p);}window.location.href=u;})(this)">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlayModuleSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlayModuleSlug === '') {
|
||||
$strPlayModuleSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<article class="{$TpStyle.dom_prefix}-p1-wrap">
|
||||
|
||||
{// 顶部区域:封面 + 简介 }
|
||||
@@ -56,12 +62,7 @@
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $epIndex++; ?>
|
||||
<li>
|
||||
<a href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$epIndex"
|
||||
/}'
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlayModuleSlug, (string)$strPlayGroupName, (int)$epIndex)}"
|
||||
class="
|
||||
{if $epIndex == $Request.route.intPlayIndex}active{/if}
|
||||
{$TpStyle.dom_prefix}-p1-ep
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlayModuleSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlayModuleSlug === '') {
|
||||
$strPlayModuleSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<article class="{$TpStyle.dom_prefix}-p2-shell">
|
||||
|
||||
{// 全宽播放器 }
|
||||
@@ -42,12 +48,7 @@
|
||||
<?php $epIndex=0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $epIndex++; ?>
|
||||
<a href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$epIndex"
|
||||
/}'
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlayModuleSlug, (string)$strPlayGroupName, (int)$epIndex)}"
|
||||
class="{$TpStyle.dom_prefix}-p2-epbtn {if $epIndex == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlayModuleSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlayModuleSlug === '') {
|
||||
$strPlayModuleSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<main class="{$TpStyle.dom_prefix}-p3-layout">
|
||||
|
||||
<section class="{$TpStyle.dom_prefix}-p3-main">
|
||||
@@ -41,12 +47,7 @@
|
||||
<?php $epIndex=0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $epIndex++; ?>
|
||||
<a href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$epIndex"
|
||||
/}'
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlayModuleSlug, (string)$strPlayGroupName, (int)$epIndex)}"
|
||||
class="{$TpStyle.dom_prefix}-p3-ep {if $epIndex == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlayModuleSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlayModuleSlug === '') {
|
||||
$strPlayModuleSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<section class="{$TpStyle.dom_prefix}-p4-page">
|
||||
|
||||
<div class="{$TpStyle.dom_prefix}-p4-top">
|
||||
@@ -34,12 +40,7 @@
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $epIndex++; ?>
|
||||
<li>
|
||||
<a href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$epIndex"
|
||||
/}'
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlayModuleSlug, (string)$strPlayGroupName, (int)$epIndex)}"
|
||||
class="{if $epIndex == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlayModuleSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlayModuleSlug === '') {
|
||||
$strPlayModuleSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<article class="{$TpStyle.dom_prefix}-p5-wrap">
|
||||
|
||||
{// 封面 + 播放器组合卡片 }
|
||||
@@ -40,12 +46,7 @@
|
||||
<?php $epIndex=0; ?>
|
||||
{volist name='$arrPlayUrlType' id='arrPlayUrl'}
|
||||
<?php $epIndex++; ?>
|
||||
<a href='{site:vpurl
|
||||
v_id="$arrVideo.v_id"
|
||||
v_py="$arrVideo.v_name_en"
|
||||
play_type="$strPlayGroupName"
|
||||
play_index="$epIndex"
|
||||
/}'
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlayModuleSlug, (string)$strPlayGroupName, (int)$epIndex)}"
|
||||
class="{$TpStyle.dom_prefix}-p5-epbtn {if $epIndex == $Request.route.intPlayIndex}active{/if}"
|
||||
m3u8="{$arrPlayUrl.url}">
|
||||
{$arrPlayUrl.name}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlaylineSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlaylineSlug === '') {
|
||||
$strPlaylineSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<section class="{$TpStyle.dom_prefix}-pl {$TpStyle.dom_prefix}-pl-a">
|
||||
|
||||
{assign name="variant" value="$pageCfg.playline.variant"}
|
||||
@@ -12,7 +18,7 @@
|
||||
<ul class="{$TpStyle.dom_prefix}-pl-grid">
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
</ul>
|
||||
@@ -24,7 +30,7 @@
|
||||
<h4>{video:getlinename code="$line" encode="false"/}</h4>
|
||||
<ol>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
</ol>
|
||||
</section>
|
||||
@@ -37,7 +43,7 @@
|
||||
<dt>{video:getlinename code="$line" encode="false"/}</dt>
|
||||
<dd>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</dd>
|
||||
{/foreach}
|
||||
@@ -49,7 +55,7 @@
|
||||
<strong>{video:getlinename code="$line" encode="false"/}</strong>
|
||||
<ul>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
</ul>
|
||||
{/foreach}
|
||||
@@ -61,7 +67,7 @@
|
||||
<header>{video:getlinename code="$line" encode="false"/}</header>
|
||||
<div>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</div>
|
||||
</article>
|
||||
@@ -75,7 +81,7 @@
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<tr>
|
||||
<td>{$i}</td>
|
||||
<td><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></td>
|
||||
<td><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></td>
|
||||
</tr>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
@@ -87,7 +93,7 @@
|
||||
<nav>
|
||||
<b>{video:getlinename code="$line" encode="false"/}</b>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</nav>
|
||||
{/foreach}
|
||||
@@ -98,7 +104,7 @@
|
||||
<p>{video:getlinename code="$line" encode="false"/}:</p>
|
||||
<div>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<span><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></span>
|
||||
<span><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></span>
|
||||
{/volist}
|
||||
</div>
|
||||
{/foreach}
|
||||
@@ -111,7 +117,7 @@
|
||||
{video:getlinename code="$line" encode="false"/}
|
||||
<ul>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
</ul>
|
||||
</li>
|
||||
@@ -123,7 +129,7 @@
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
<h5>{video:getlinename code="$line" encode="false"/}</h5>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
|
||||
@@ -133,7 +139,7 @@
|
||||
<section>
|
||||
<h4>{video:getlinename code="$line" encode="false"/}</h4>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</section>
|
||||
{/foreach}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlaylineSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlaylineSlug === '') {
|
||||
$strPlaylineSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<section class="{$TpStyle.dom_prefix}-pl {$TpStyle.dom_prefix}-pl-b">
|
||||
|
||||
{assign name="variant" value="$pageCfg.playline.variant"}
|
||||
@@ -14,7 +20,7 @@
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
<ul class="{$TpStyle.dom_prefix}-pl-list">
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
</ul>
|
||||
{/foreach}
|
||||
@@ -32,7 +38,7 @@
|
||||
<ul class="{$TpStyle.dom_prefix}-pl-episodes">
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
</ul>
|
||||
@@ -45,7 +51,7 @@
|
||||
<dt>{video:getlinename code="$line" encode="false"/}</dt>
|
||||
<dd>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</dd>
|
||||
{/foreach}
|
||||
@@ -58,7 +64,7 @@
|
||||
<header>{video:getlinename code="$line" encode="false"/}</header>
|
||||
<div>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<span><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></span>
|
||||
<span><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></span>
|
||||
{/volist}
|
||||
</div>
|
||||
</section>
|
||||
@@ -70,7 +76,7 @@
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
<tr><th>{video:getlinename code="$line" encode="false"/}</th><td>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</td></tr>
|
||||
{/foreach}
|
||||
@@ -87,7 +93,7 @@
|
||||
<section>
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
</section>
|
||||
@@ -103,7 +109,7 @@
|
||||
<ol class="{$TpStyle.dom_prefix}-pl-episodes">
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></li>
|
||||
<li><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></li>
|
||||
{/volist}
|
||||
{/foreach}
|
||||
</ol>
|
||||
@@ -114,7 +120,7 @@
|
||||
<p><b>{video:getlinename code="$line" encode="false"/}</b></p>
|
||||
<p>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</p>
|
||||
{/foreach}
|
||||
@@ -126,7 +132,7 @@
|
||||
<tr><td>{video:getlinename code="$line" encode="false"/}</td><td>
|
||||
<table>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<tr><td><a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a></td></tr>
|
||||
<tr><td><a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a></td></tr>
|
||||
{/volist}
|
||||
</table>
|
||||
</td></tr>
|
||||
@@ -140,7 +146,7 @@
|
||||
<h3>{video:getlinename code="$line" encode="false"/}</h3>
|
||||
<p>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</p>
|
||||
</article>
|
||||
@@ -152,7 +158,7 @@
|
||||
<div class="{if $intLineIndex==0}active{/if}">
|
||||
<strong>{video:getlinename code="$line" encode="false"/}</strong>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">{$ep.name}</a>
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">{$ep.name}</a>
|
||||
{/volist}
|
||||
</div>
|
||||
{php}$intLineIndex++;{/php}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{php}
|
||||
$strPlaylineSlug = trim((string)($arrVideo['v_name_en'] ?? request()->route('strPinyin') ?? ''));
|
||||
if ($strPlaylineSlug === '') {
|
||||
$strPlaylineSlug = (string)($arrVideo['v_id'] ?? '');
|
||||
}
|
||||
{/php}
|
||||
<section class="{$TpStyle.dom_prefix}-pl {$TpStyle.dom_prefix}-pl-c">
|
||||
|
||||
{assign name="variant" value="$pageCfg.playline.variant"}
|
||||
@@ -10,7 +16,7 @@
|
||||
<ol>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</li>
|
||||
@@ -27,7 +33,7 @@
|
||||
<div>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<p>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</p>
|
||||
@@ -43,7 +49,7 @@
|
||||
<dt>{video:getlinename code="$line" encode="false"/}</dt>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<dd>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</dd>
|
||||
@@ -58,7 +64,7 @@
|
||||
<p>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<span>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</span>
|
||||
@@ -73,7 +79,7 @@
|
||||
<strong>{video:getlinename code="$line" encode="false"/}</strong>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<div>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</div>
|
||||
@@ -89,7 +95,7 @@
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<li>
|
||||
<p>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</p>
|
||||
@@ -106,7 +112,7 @@
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<div>
|
||||
<time>{$i}</time>
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</div>
|
||||
@@ -122,7 +128,7 @@
|
||||
<p>
|
||||
本线路提供以下资源:
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
{/volist}
|
||||
@@ -137,7 +143,7 @@
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<p>
|
||||
第 {$i} 集:
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
</p>
|
||||
@@ -149,7 +155,7 @@
|
||||
{foreach $arrVideo.v_play_url as $line=>$list}
|
||||
<h5>{video:getlinename code="$line" encode="false"/}</h5>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
{/volist}
|
||||
@@ -161,7 +167,7 @@
|
||||
<section>
|
||||
<strong>{video:getlinename code="$line" encode="false"/} : </strong>
|
||||
{volist name="list" id="ep" key="i"}
|
||||
<a href="{site:vpurl v_id='$arrVideo.v_id' play_type='$line' play_index='$i'/}">
|
||||
<a href="{:app('app\\services\\VideoService')->getVideoPlayUrl((int)$arrVideo['v_id'], $strPlaylineSlug, (string)$line, (int)$i)}">
|
||||
{$ep.name}
|
||||
</a>
|
||||
{/volist}
|
||||
|
||||
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}
|
||||
@@ -4,7 +4,7 @@
|
||||
{if !empty($arrGrm[$key + [start]])}
|
||||
{$arrGrm[$key+[start]]|raw}
|
||||
{/if}
|
||||
<a href='{site:vurl v_id='$vo.v_id' v_py='$vo.v_name_en'/}'
|
||||
<a href='{site:vurl v_id='$Video.v_id' v_py='$Video.v_name_en'/}'
|
||||
title="{$Video.v_name ?? ''} - {$Video.v_parent_category ?? ''}{$Video.v_category ?? ''}免费高清电影在线观看">
|
||||
<span class='badge
|
||||
{eq name="$key" value="1"}badge-first {/eq}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{$arrGrm[$key+[start]]|raw}
|
||||
{/if}
|
||||
<a class="stui-vodlist__thumb "
|
||||
href='{site:vurl v_id='$vo.v_id' v_py='$vo.v_name_en'/}'
|
||||
href='{site:vurl v_id='$Video.v_id' v_py='$Video.v_name_en'/}'
|
||||
title="{$Video.v_name ?? ''} - {$Video.v_parent_category ?? ''}{$Video.v_category ?? ''}免费高清电影在线观看"
|
||||
>
|
||||
<img class="lazyload-img" src="/static/img/loading.webp" data-src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$Video.v_pic}"
|
||||
@@ -15,7 +15,7 @@
|
||||
</a>
|
||||
<div class="stui-vodlist__detail">
|
||||
<h3 class="title text-overflow">
|
||||
<a href='{site:vurl v_id='$vo.v_id' v_py='$vo.v_name_en'/}'
|
||||
<a href='{site:vurl v_id='$Video.v_id' v_py='$Video.v_name_en'/}'
|
||||
title="{$Video.v_name ?? ''} - {$Video.v_parent_category ?? ''}{$Video.v_category ?? ''}免费高清电影在线观看">
|
||||
{$Video.v_name}
|
||||
</a>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{$arrGrm[$key+[start]]|raw}
|
||||
{/if}
|
||||
<a class="top-line-dot text-overflow"
|
||||
href='{site:vurl v_id='$vo.v_id' v_py='$vo.v_name_en'/}'
|
||||
href='{site:vurl v_id='$Video.v_id' v_py='$Video.v_name_en'/}'
|
||||
title="{$Video.v_name ?? ''} - {$Video.v_parent_category ?? ''}{$Video.v_category ?? ''}免费高清电影在线观看">
|
||||
<i class="icon iconfont icon-more pull-right text-muted"></i>
|
||||
{$Video.v_name}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
{// 社交媒体标签}
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="category_list" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="category_list" /}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{site:vclurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" page="$Request.route.intPage" /}' />
|
||||
{switch $Request.route.strParentCategory }
|
||||
{case 'dian-ying' }
|
||||
<meta property="og:type" content="video.movie" />
|
||||
@@ -64,27 +64,13 @@
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
"name": "{$DomainModel->d_name} - 最新{site:getval code="strVideoParentCategoryName" /}推荐",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"url": "https://{$DomainModel->d_domain}{site:vclurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" page="$Request.route.intPage" /}",
|
||||
"description": "{site:seotkd code="description" page="category_list" /}",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": 'https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
"hasPart": [
|
||||
{volist name="resData.data" id="Video" key="key"}
|
||||
{if $key < 5 }
|
||||
{
|
||||
"@type": "VideoObject",
|
||||
"name": "{$Video.v_name}",
|
||||
"description": "{$Video.v_description}",
|
||||
"thumbnailUrl": '{$Video.v_pic}',
|
||||
"uploadDate": "{:date('Y-m-d')}",
|
||||
"url": 'https://{$DomainModel->d_domain}{site:vurl v_id="$Video.v_id" v_py="$Video.v_name_en"/}',
|
||||
},
|
||||
{/if}
|
||||
{/volist}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
@@ -109,7 +95,7 @@
|
||||
"item": "https://{$DomainModel->d_domain}{site:vclurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" page="$Request.route.intPage"}"
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -130,6 +116,16 @@
|
||||
|
||||
{include file="module/list/shell/_shell_router" /}
|
||||
|
||||
{video:seocopy scene="category_list"
|
||||
parent_category="$Request.route.strParentCategory"
|
||||
category="$Request.route.strCategory"
|
||||
page="$resData.p_data.page|default=1"
|
||||
total="$resData.p_data.total|default=0"
|
||||
pages="$resData.p_data.pages|default=1"
|
||||
visible="$resData.data|count"
|
||||
export_name="seoCopy" /}
|
||||
{include file="module/seo_copy/collection" /}
|
||||
|
||||
{// ===== Pagination ===== }
|
||||
{php}
|
||||
$pagerArr = $resData['p_data']['pager'] ?? [];
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{site:vciurl parent_category="$Request.route.strParentCategory" /}'>
|
||||
|
||||
<meta property="og:title" content='{site:replace code="VIDEO@GETCATEGORYINDEX@TITLE"}' />
|
||||
<meta property="og:description" content='{site:replace code="VIDEO@GETCATEGORYINDEX@DESCRIPTION"}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="category_index" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="category_index" /}' />
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{site:vciurl parent_category="$Request.route.strParentCategory" /}' />
|
||||
{switch $Request.route.strParentCategory }
|
||||
{case 'dian-ying' }
|
||||
<meta property="og:type" content="video.movie" />
|
||||
@@ -39,37 +39,17 @@
|
||||
|
||||
<script type="application/ld+json">
|
||||
[
|
||||
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
"name": "{$DomainModel->d_name} - 最新{site:getval code="strVideoParentCategoryName" /}推荐",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"description": "{site:replace code="VIDEO@GETCATEGORYINDEX@DESCRIPTION"}",
|
||||
"url": "https://{$DomainModel->d_domain}{site:vciurl parent_category="$Request.route.strParentCategory" /}",
|
||||
"description": "{site:seotkd code='description' page='category_index' /}",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": 'https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
"hasPart": [
|
||||
{video:ranklist count="5"
|
||||
v_parent_category_en="$Request.route.strParentCategory"
|
||||
sort_type="weekly"
|
||||
d_key="key" d_val="Video" cache_life="3600"}
|
||||
|
||||
{if $key < 5 }
|
||||
{
|
||||
"@type": "VideoObject",
|
||||
"name": "{$Video.v_name}",
|
||||
"description": "{$Video.v_description}",
|
||||
"thumbnailUrl": '{$Video.v_pic}',
|
||||
"uploadDate": "{:date('Y-m-d')}",
|
||||
"url": 'https://{$DomainModel->d_domain}{site:vurl v_id="$Video.v_id" v_py="$Video.v_name_en"/}',
|
||||
},
|
||||
{/if}
|
||||
{/video:ranklist}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
@@ -88,7 +68,7 @@
|
||||
"item": "https://{$DomainModel->d_domain}{site:vciurl parent_category="$Request.route.strParentCategory" /}"
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
</script>
|
||||
{/block}
|
||||
@@ -170,6 +150,10 @@
|
||||
{/video:categorytype}
|
||||
|
||||
|
||||
{video:seocopy scene="category_index"
|
||||
parent_category="$Request.route.strParentCategory"
|
||||
export_name="seoCopy" /}
|
||||
{include file="module/seo_copy/collection" /}
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
|
||||
|
||||
{block name="head"}
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{site:vciurl parent_category="$Request.route.strParentCategory" /}'>
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="category_home" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="category_home" /}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content='{site:seotkd code="title" page="category_home" /}'>
|
||||
<meta name="twitter:description" content='{site:seotkd code="description" page="category_home" /}'>
|
||||
|
||||
{/block}
|
||||
|
||||
|
||||
@@ -5,20 +5,28 @@
|
||||
{/block}
|
||||
|
||||
{block name="title"}{site:seotkd code="title" page="rank_index" /}{/block}
|
||||
{block name="keywords"}{site:seotkd code="keywords" page="rank_index" /}{/block}
|
||||
{block name="keywords"}{php}
|
||||
$strRankKeywords = trim((string)($DomainModel->d_keywords ?: $DomainModel->d_name));
|
||||
$strRankKeywords = $strRankKeywords !== ''
|
||||
? $strRankKeywords . ',热门榜单,视频排行'
|
||||
: trim((string)$DomainModel->d_name) . ',热门榜单,视频排行';
|
||||
{/php}{$strRankKeywords}{/block}
|
||||
{block name="description"}{site:seotkd code="description" page="rank_index" /}{/block}
|
||||
|
||||
|
||||
{block name="head"}
|
||||
{php}
|
||||
$strRankCanonicalUrl = 'https://' . $DomainModel->d_domain . $strRankUrlTemp;
|
||||
{/php}
|
||||
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{$strRankUrlTemp}'>
|
||||
<link rel="canonical" href='{$strRankCanonicalUrl}'>
|
||||
|
||||
{// 社交媒体标签}
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="rank_index" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="rank_index" /}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:type" content="video.movie" />
|
||||
<meta property="og:url" content="{$strRankCanonicalUrl}" />
|
||||
<meta property="og:type" content="website" />
|
||||
|
||||
<script type="application/ld+json">
|
||||
[
|
||||
@@ -27,7 +35,7 @@
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
"name": "{$DomainModel->d_name} - 最新排行榜推荐",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"url": "{$strRankCanonicalUrl}",
|
||||
"headline": '{site:seotkd code="title" page="rank_index" /}',
|
||||
"description": '{site:seotkd code="description" page="rank_index" /}',
|
||||
"potentialAction": {
|
||||
@@ -35,24 +43,6 @@
|
||||
"target": 'https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
"hasPart": [
|
||||
{video:ranklist count="5"
|
||||
sort_type="weekly"
|
||||
d_key="key" d_val="Video" cache_life="3600"}
|
||||
|
||||
{if $key < 10 }
|
||||
{
|
||||
"@type": "VideoObject",
|
||||
"name": "{$Video.v_name}",
|
||||
"description": "{$Video.v_description}",
|
||||
"thumbnailUrl": '{$Video.v_pic}',
|
||||
"uploadDate": "{:date('Y-m-d')}",
|
||||
"url": 'https://{$DomainModel->d_domain}{site:vurl v_id="$Video.v_id" v_py="$Video.v_name_en"/}',
|
||||
},
|
||||
{/if}
|
||||
{/video:ranklist}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
@@ -71,7 +61,7 @@
|
||||
"item": "https://{$DomainModel->d_domain}{$strRankUrlTemp}"
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -105,6 +95,9 @@
|
||||
{include file="module/list/shell/_shell_router" /}
|
||||
|
||||
{/foreach}
|
||||
|
||||
{video:seocopy scene="rank_index" export_name="seoCopy" /}
|
||||
{include file="module/seo_copy/collection" /}
|
||||
</section>
|
||||
|
||||
{/block}
|
||||
|
||||
@@ -12,19 +12,35 @@ p_val="page_data" button_num="10" cache_life="3600" export_name="arrVideoRankLis
|
||||
|
||||
{site:grm page_code="h5_rank_list_type" diff="$Request.route.intPage" num="100" g_key="arrGrm" /}
|
||||
{site:helper func="initVideoRankListTKD" /}
|
||||
{php}
|
||||
$strRankListKeywordsBase = trim((string)($DomainModel->d_keywords ?: $DomainModel->d_name));
|
||||
$strRankListKeywordsOrder = trim((string)($strOrderName ?? '热门'));
|
||||
$strRankListKeywords = $strRankListKeywordsBase !== ''
|
||||
? $strRankListKeywordsBase . ',' . $strRankListKeywordsOrder . '榜单,视频排行'
|
||||
: trim((string)$DomainModel->d_name) . ',' . $strRankListKeywordsOrder . '榜单,视频排行';
|
||||
|
||||
$strRankListDescCategory = trim((string)(\app\model\ConverterMovel::getVal('strVideoParentCategoryName') ?? '影视'));
|
||||
if ($strRankListDescCategory === '') {
|
||||
$strRankListDescCategory = '影视';
|
||||
}
|
||||
$strRankListDescOrder = $strRankListKeywordsOrder !== '' ? $strRankListKeywordsOrder : '热门';
|
||||
$strRankListDescription = trim((string)$DomainModel->d_name) . '整理' . $strRankListDescOrder . $strRankListDescCategory . '榜单入口,方便继续查看热度内容、详情页与播放页。';
|
||||
{/php}
|
||||
|
||||
{/block}
|
||||
|
||||
{block name="title"}{site:seotkd code="title" page="rank_list" /}{/block}
|
||||
{block name="keywords"}{site:seotkd code="title" page="rank_list" /}{/block}
|
||||
{block name="description"}{site:seotkd code="title" page="rank_list" /}{/block}
|
||||
{block name="keywords"}{$strRankListKeywords}{/block}
|
||||
{block name="description"}{$strRankListDescription}{/block}
|
||||
|
||||
{block name="head"}
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{site:vrlurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" sort_type="$Request.route.strSortType" page="1"/}'>
|
||||
|
||||
{// 社交媒体标签}
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="rank_list" /}' />
|
||||
<meta property="og:description" content='{site:seotkd code="title" page="rank_list" /}' />
|
||||
<meta property="og:url" content="https://{$DomainModel->d_domain}" />
|
||||
<meta property="og:description" content='{$strRankListDescription}' />
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{site:vrlurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" sort_type="$Request.route.strSortType" page="1"/}' />
|
||||
<meta property="og:type" content="video.movie" />
|
||||
|
||||
|
||||
@@ -35,30 +51,14 @@ p_val="page_data" button_num="10" cache_life="3600" export_name="arrVideoRankLis
|
||||
"@context": "https://schema.org",
|
||||
"@type": "CollectionPage",
|
||||
"name": "{$DomainModel->d_name} - 最新排行榜推荐",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"url": "https://{$DomainModel->d_domain}{site:vrlurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" sort_type="$Request.route.strSortType" page="1"/}",
|
||||
"headline": '{site:seotkd code="title" page="rank_list" /}',
|
||||
"description": '{site:seotkd code="title" page="rank_list" /}',
|
||||
"description": '{$strRankListDescription}',
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": 'https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
"hasPart": [
|
||||
{volist name="arrVideoRankList" id="Video" key="key"}
|
||||
|
||||
{if $key < 10 }
|
||||
{
|
||||
"@type": "VideoObject",
|
||||
"name": "{$Video.v_name}",
|
||||
"description": "{$Video.v_description}",
|
||||
"thumbnailUrl": '{$Video.v_pic}',
|
||||
"uploadDate": "{:date('Y-m-d')}",
|
||||
"url": 'https://{$DomainModel->d_domain}{site:vurl v_id='$vo.v_id' v_py='$vo.v_name_en'/}',
|
||||
},
|
||||
{/if}
|
||||
{/volist}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
@@ -83,7 +83,7 @@ p_val="page_data" button_num="10" cache_life="3600" export_name="arrVideoRankLis
|
||||
"item": "https://{$DomainModel->d_domain}{site:vrlurl parent_category="$Request.route.strParentCategory" category="$Request.route.strCategory" sort_type="$Request.route.strSortType" page="1"/}"
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -129,6 +129,17 @@ p_val="page_data" button_num="10" cache_life="3600" export_name="arrVideoRankLis
|
||||
</h1>
|
||||
<h4 class="view-description">{site:replace code="VIDEO@GETVIDEORANKLIST@TEXT"}</h4>
|
||||
|
||||
{video:seocopy scene="rank_list"
|
||||
sort_type="$Request.route.strSortType"
|
||||
parent_category="$Request.route.strParentCategory"
|
||||
category="$Request.route.strCategory"
|
||||
page="1"
|
||||
total="0"
|
||||
pages="1"
|
||||
visible="$arrVideoRankList|count"
|
||||
export_name="seoCopy" /}
|
||||
{include file="module/seo_copy/collection" /}
|
||||
|
||||
<div class="row">
|
||||
<div class="stui-pannel stui-pannel-bg clearfix">
|
||||
<div class="stui-pannel-box">
|
||||
|
||||
@@ -18,20 +18,27 @@ p_val="page_data" button_num="5" cache_life="3600" func="generateSearchPager" ex
|
||||
{block name="description"}{site:seotkd code="description" page="search" /}{/block}
|
||||
|
||||
{block name="head"}
|
||||
{php}
|
||||
$strSearchKeyword = trim((string)($Request->get('keyword', '')));
|
||||
$urlBuilder = new \app\common\helper\UrlBuilder($TpStyle);
|
||||
$strSearchCanonicalUrl = $strSearchKeyword !== ''
|
||||
? 'https://' . $DomainModel->d_domain . $urlBuilder->searchResult($strSearchKeyword)
|
||||
: 'https://' . $DomainModel->d_domain;
|
||||
{/php}
|
||||
|
||||
<meta name="robots" content="index,follow">
|
||||
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{site:vsurl key="$Request.get.keyword" p="1"/}'>
|
||||
<link rel="canonical" href='{$strSearchCanonicalUrl}'>
|
||||
|
||||
<meta property="og:title" content='{site:seotkd code="title" page="search" /}'>
|
||||
<meta property="og:description" content='{site:seotkd code="description" page="search" /}'>
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{site:vsurl key="search_term_string" p="1"/}'>
|
||||
<meta property="og:url" content='{$strSearchCanonicalUrl}'>
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content='{$DomainModel->d_name}'>
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content='{site:seotkd code="title" page="search" /}'>
|
||||
<meta name="twitter:description" content='{site:seotkd code="description" page="search" /}'>
|
||||
<meta name="twitter:image:alt" content="动作电影 搜索结果第1页">
|
||||
<meta name="twitter:image:alt" content="{if $strSearchKeyword !== ''}{$strSearchKeyword|raw} 搜索结果第{$resData.p_data.page|default=1}页{else/}{$DomainModel->d_name|raw} 搜索结果{/if}">
|
||||
|
||||
{// 结构化数据}
|
||||
<script type="application/ld+json">
|
||||
@@ -41,7 +48,7 @@ p_val="page_data" button_num="5" cache_life="3600" func="generateSearchPager" ex
|
||||
"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"/}',
|
||||
@@ -52,41 +59,9 @@ p_val="page_data" button_num="5" cache_life="3600" func="generateSearchPager" ex
|
||||
{
|
||||
"@type": "CollectionPage",
|
||||
"name": "{$DomainModel->d_name} - {$DomainModel->d_logo_text}",
|
||||
"url": "https://{$DomainModel->d_domain}",
|
||||
"url": "{$strSearchCanonicalUrl}",
|
||||
"alternateName": "{$DomainModel->d_domain}",
|
||||
"description": '{site:seotkd code="description" page="search" /}',
|
||||
"mainEntity": {
|
||||
"@type": "ItemList",
|
||||
"itemListElement": [
|
||||
{volist name="resData.data" id="Video" key="key"}
|
||||
{if $key < 5 }
|
||||
{
|
||||
"@type": "ListItem",
|
||||
"position": {$key},
|
||||
"item": {
|
||||
"@type": "Movies",
|
||||
"name": "{$Video.v_name}",
|
||||
"url": 'https://{$DomainModel->d_domain}{site:vurl v_id="$Video.v_id" v_py="$Video.v_name_en"/}',
|
||||
"description": "{$Video.v_description}",
|
||||
"actor": [
|
||||
{foreach $Video.v_actor as $strActorKey=>$strActor }
|
||||
{if $strActorKey < 5 }
|
||||
{ "@type": "Person", "name": "{$strActor}" },
|
||||
{/if}
|
||||
{/foreach}
|
||||
],
|
||||
"numberOfEpisodes": {$Video.v_play_url|count},
|
||||
"image": "{$Video.v_pic}",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "{$Video.v_score}",
|
||||
}
|
||||
}
|
||||
},
|
||||
{/if}
|
||||
{/volist}
|
||||
]
|
||||
}
|
||||
"description": '{site:seotkd code="description" page="search" /}'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
@@ -112,6 +87,14 @@ p_val="page_data" button_num="5" cache_life="3600" func="generateSearchPager" ex
|
||||
{assign name="__LIST__" value="$resData.data"}
|
||||
{include file="module/list/shell/_shell_router" /}
|
||||
|
||||
{video:seocopy scene="search"
|
||||
keyword="$Request.get.keyword"
|
||||
page="$resData.p_data.page|default=1"
|
||||
total="$resData.p_data.total|default=0"
|
||||
pages="$resData.p_data.pages|default=1"
|
||||
visible="$resData.data|count"
|
||||
export_name="seoCopy" /}
|
||||
{include file="module/seo_copy/collection" /}
|
||||
|
||||
{// ===== Pagination ===== }
|
||||
{php}
|
||||
|
||||
@@ -3,9 +3,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}
|
||||
@@ -26,11 +26,11 @@
|
||||
|
||||
{/block}
|
||||
|
||||
{block name="title"}{if $DomainModel->info_id == $Request.route.intVId || empty($Request.route.intVId)}{site:replace code="VIDEO@INDEX@INDEX@TITLE" /}{else/}{site:seotkd code="title" page="detail" /}{/if}{/block}
|
||||
{block name="title"}{if !empty($arrVideo.v_id) && ((int)$arrVideo.v_id !== (int)$DomainModel->info_id || !empty($Request.route.strPinyin))}{site:seotkd code="title" page="detail" /}{else/}{site:seotkd code="title" page="home" /}{/if}{/block}
|
||||
|
||||
{block name="keywords"}{if $DomainModel->info_id == $Request.route.intVId || empty($Request.route.intVId)}{site:replace code="VIDEO@INDEX@INDEX@KEYWORDS" /}{else/}{site:seotkd code="keywords" page="detail" /}{/if}{/block}
|
||||
{block name="keywords"}{if !empty($arrVideo.v_id) && ((int)$arrVideo.v_id !== (int)$DomainModel->info_id || !empty($Request.route.strPinyin))}{site:seotkd code="keywords" page="detail" /}{else/}{site:seotkd code="keywords" page="home" /}{/if}{/block}
|
||||
|
||||
{block name="description"}{if $DomainModel->info_id == $Request.route.intVId || empty($Request.route.intVId)}{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION" /}{else/}{site:seotkd code="description" page="detail" /}{/if}{/block}
|
||||
{block name="description"}{if !empty($arrVideo.v_id) && ((int)$arrVideo.v_id !== (int)$DomainModel->info_id || !empty($Request.route.strPinyin))}{site:seotkd code="description" page="detail" /}{else/}{site:seotkd code="description" page="home" /}{/if}{/block}
|
||||
|
||||
|
||||
{block name="head"}
|
||||
|
||||
@@ -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,93 @@
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
$strPlayCanonicalUrl = '';
|
||||
if ((int)($arrVideo['v_id'] ?? 0) > 0) {
|
||||
$strPlayCanonicalUrl = app(\app\services\VideoService::class)->getVideoPlayUrl(
|
||||
(int)$arrVideo['v_id'],
|
||||
$strPlaySlug,
|
||||
$strCurrentPlayType !== '' ? $strCurrentPlayType : 'default',
|
||||
$intCurrentPlayIndex
|
||||
);
|
||||
}
|
||||
|
||||
$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}
|
||||
|
||||
@@ -33,7 +114,7 @@
|
||||
<meta name="robots" content="noindex,follow">
|
||||
|
||||
{if $Request.route.intVId }
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{site:vurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" /}'>
|
||||
<link rel="canonical" href='https://{$DomainModel->d_domain}{$strPlayCanonicalUrl}'>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -59,7 +140,7 @@
|
||||
{default /}
|
||||
<meta property="og:type" content="video.movie" />
|
||||
{/switch}
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{site:vurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en"/}'>
|
||||
<meta property="og:url" content='https://{$DomainModel->d_domain}{$strPlayCanonicalUrl}'>
|
||||
<meta property="og:image" content="https://{$DomainModel->d_domain}{$arrVideo.v_pic}">
|
||||
<meta property="og:site_name" content="{$DomainModel->d_name}">
|
||||
<meta property="og:video:type" content="video/m3u8">
|
||||
@@ -118,7 +199,7 @@
|
||||
"description": "{if !empty($arrVideo.v_description)}{$arrVideo.v_description|raw}{else/}暂无简介{/if}",
|
||||
"thumbnailUrl": "{$arrVideo.v_pic|raw}",
|
||||
"uploadDate": "{:date('Y-m-d')}",
|
||||
"url": "https://{$DomainModel->d_domain}{site:vurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en"/}"
|
||||
"url": "https://{$DomainModel->d_domain}{$strPlayCanonicalUrl}"
|
||||
{if !empty($arrVideo.v_lang)},{/if}
|
||||
{if !empty($arrVideo.v_lang)}
|
||||
"inLanguage": [
|
||||
@@ -181,6 +262,8 @@
|
||||
|
||||
{include file="module/page/page_router" /}
|
||||
|
||||
{include file="module/seo_copy/play" /}
|
||||
|
||||
<script>
|
||||
const strMaxPlayHeight = '480px'
|
||||
const strVideoId = `{$arrVideo.v_id}`;
|
||||
|
||||
@@ -369,6 +369,41 @@ class VideoCategoryModel extends MongoModel
|
||||
return self::$arrCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve category display name by slug.
|
||||
*/
|
||||
static public function getNameBySlug(string $strSlug): string
|
||||
{
|
||||
$strSlug = trim($strSlug);
|
||||
if ($strSlug === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isset(self::$arrCategory[$strSlug])) {
|
||||
return (string)self::$arrCategory[$strSlug];
|
||||
}
|
||||
|
||||
foreach (self::getCustomCategory() as $arrParentCategory) {
|
||||
if (!is_array($arrParentCategory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((string)($arrParentCategory['v_category_en'] ?? '') === $strSlug) {
|
||||
return trim((string)($arrParentCategory['v_category'] ?? ''));
|
||||
}
|
||||
|
||||
foreach ((array)($arrParentCategory['children'] ?? []) as $arrChildCategory) {
|
||||
if ((string)($arrChildCategory['v_category_en'] ?? '') !== $strSlug) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return trim((string)($arrChildCategory['v_category'] ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* get pinyin based on chinese characters
|
||||
*
|
||||
|
||||
@@ -145,6 +145,20 @@ class VideoModel extends MongoModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility wrapper for callers that expect a sorted cached query helper.
|
||||
*/
|
||||
public function findManySortedWithCache(
|
||||
array $arrFilter,
|
||||
int $intCount = 0,
|
||||
array $arrSort = [],
|
||||
string $strKey = '',
|
||||
int $intLifeTime = 24 * 3600,
|
||||
array $arrOptions = []
|
||||
): null|array {
|
||||
return $this->findManyWithCache($arrFilter, $intCount, $arrSort, $arrOptions, $strKey, $intLifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* find one Video
|
||||
*
|
||||
@@ -179,6 +193,16 @@ class VideoModel extends MongoModel
|
||||
return $this->findOne($arrFilter);
|
||||
}
|
||||
|
||||
public function getVideoByNameEn(string $strNameEn): null|array
|
||||
{
|
||||
$strNameEn = trim($strNameEn);
|
||||
if ($strNameEn === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->findOne(['v_name_en' => $strNameEn]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询符合条件的随机多条小说
|
||||
|
||||
@@ -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,411 @@ 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
|
||||
{
|
||||
$strSanitized = @iconv('UTF-8', 'UTF-8//IGNORE', $strValue);
|
||||
if ($strSanitized !== false) {
|
||||
$strValue = $strSanitized;
|
||||
}
|
||||
$strValue = str_replace("\u{FFFD}", '', $strValue);
|
||||
$strValue = preg_replace('/排<>+/u', '排行', $strValue) ?? $strValue;
|
||||
$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-_|,。;、,;");
|
||||
$strPart = preg_replace('/排<>+/u', '排行', $strPart) ?? $strPart;
|
||||
if (preg_match('/榜排$/u', $strPart) || preg_match('/视频排$/u', $strPart)) {
|
||||
$strPart .= '行';
|
||||
}
|
||||
|
||||
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 '';
|
||||
}
|
||||
|
||||
$strSanitized = @iconv('UTF-8', 'UTF-8//IGNORE', $strValue);
|
||||
if ($strSanitized !== false) {
|
||||
$strValue = $strSanitized;
|
||||
}
|
||||
|
||||
$strValue = str_replace("\u{FFFD}", '', $strValue);
|
||||
$strValue = preg_replace('/排<>+/u', '排行', $strValue) ?? $strValue;
|
||||
|
||||
$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 (preg_match('/精神内核|文化符号|隐喻意义|叙事模型|英雄之旅|成长母题/u', $strValue)) {
|
||||
$boolDescriptionLooksWeak = true;
|
||||
}
|
||||
|
||||
if ($strPage === 'play' && preg_match('/解析/u', $strValue)) {
|
||||
$boolDescriptionLooksWeak = true;
|
||||
}
|
||||
|
||||
if ($strPage === 'play' && mb_strlen($strValue) > 80) {
|
||||
$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, [
|
||||
@@ -353,6 +376,21 @@ class VideoService
|
||||
$arrFilter['v_parent_category_en'],
|
||||
);
|
||||
|
||||
if (empty($arrVideo) || !is_array($arrVideo)) {
|
||||
$arrVideo = $this->getVideoList([
|
||||
'count' => $intCount,
|
||||
'v_category_en' => $arrFilter['v_category_en'] ?? 'all',
|
||||
'v_parent_category_en' => $arrFilter['v_parent_category_en'] ?? 'all',
|
||||
'v_lang_en' => 'all',
|
||||
'v_area_en' => 'all',
|
||||
'v_year' => 'all',
|
||||
'sort_type' => 'news',
|
||||
'v_status' => $strVStatus,
|
||||
'diff_key' => $strDiffKey !== '' ? $strDiffKey . ':rank_fallback' : 'rank_fallback',
|
||||
'cache_life' => $intCacheLifeTime,
|
||||
]);
|
||||
}
|
||||
|
||||
return $arrVideo;
|
||||
}
|
||||
|
||||
@@ -791,6 +829,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 +852,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 +879,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 +1175,80 @@ 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);
|
||||
|
||||
$boolSuppressDefaultOnlySeoCopy = $this->shouldSuppressDefaultOnlySeoCopy($strScene, $strHost, $arrPageKeys);
|
||||
|
||||
$arrData = (!$boolSuppressDefaultOnlySeoCopy && !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 +1290,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} ?? [];
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
<?php
|
||||
return [
|
||||
[
|
||||
'为您提供《{strVideoName}》高清高清播放服务,包含剧情简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'为您提供《{strVideoName}》高清播放服务,包含剧情简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},演员:{strVideoActor}。内容简介:{strVideoDescription}。在{strSiteName}支持在线点播与多端访问。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},演员:{strVideoActor}。内容简介:{strVideoDescription}。在{strSiteName}支持在线播放与多端访问。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'为您提供《{strVideoName}》高清在线播放服务,包含精彩看点、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'为您提供《{strVideoName}》高清高清播放服务,包含精彩看点、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'为您提供《{strVideoName}》高清播放服务,包含精彩看点、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},主演:{strVideoActor}。精彩看点:{strVideoDescription}。在{strSiteName}支持在线播放与多端访问。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线点播、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'为您提供《{strVideoName}》高清高清播放服务,包含内容简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'为您提供《{strVideoName}》高清播放服务,包含内容简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'为您提供《{strVideoName}》高清在线点播服务,包含内容简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},演员:{strVideoActor}。精彩看点:{strVideoDescription}。在{strSiteName}支持高清播放与多端访问。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线点播、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},主演:{strVideoActor}。精彩看点:{strVideoDescription}。在{strSiteName}支持高清播放与多端访问。',
|
||||
'为您提供《{strVideoName}》高清在线播放服务,包含内容简介、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。导演:{strVideoDirector},主演:{strVideoActor}。剧情简介:{strVideoDescription}。在{strSiteName}支持在线点播与多端访问。',
|
||||
'为您提供《{strVideoName}》高清在线点播服务,包含精彩看点、演员信息({strVideoActor})与导演({strVideoDirector}),支持快速选集与流畅播放。',
|
||||
'{strVideoName}由{strVideoDirector}导演,{strVideoActor}演员参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清在线点播、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。执导:{strVideoDirector},演员:{strVideoActor}。剧情简介:{strVideoDescription}。在{strSiteName}支持在线播放与多端访问。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'{strVideoName}由{strVideoDirector}执导,{strVideoActor}主演参演,类型为{strVideoParentCategoryName}/{strVideoCategoryName}。提供高清播放、剧情介绍与相关推荐,尽在{strSiteName}。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。导演:{strVideoDirector},演员:{strVideoActor}。内容简介:{strVideoDescription}。在{strSiteName}支持在线点播与多端访问。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。导演:{strVideoDirector},演员:{strVideoActor}。内容简介:{strVideoDescription}。在{strSiteName}支持高清播放与多端访问。',
|
||||
'《{strVideoName}》({strYear}){strVideoCategoryName},{strAreaName}{strLangName}。导演:{strVideoDirector},演员:{strVideoActor}。精彩看点:{strVideoDescription}。在{strSiteName}支持高清播放与多端访问。',
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
'《{strVideoName}》在线播放页面:第{intPlayIndex}集,免费观看,播放稳定流畅,支持手机与电脑访问。',
|
||||
'《{strVideoName}》在线播放页面:第{intPlayIndex}集,无需注册观看,播放稳定流畅,支持手机与电脑访问。',
|
||||
'正在播放《{strVideoName}》第{intPlayIndex}集,支持高清在线点播与快速选集。更多{strVideoCategoryName}内容与相关推荐尽在{strSiteName}。',
|
||||
'正在播放《{strVideoName}》第{intPlayIndex}集,支持高清高清播放与快速选集。更多{strVideoCategoryName}内容与相关推荐尽在{strSiteName}。',
|
||||
'正在播放《{strVideoName}》第{intPlayIndex}集,支持高清播放与快速选集。更多{strVideoCategoryName}内容与相关推荐尽在{strSiteName}。',
|
||||
'{strSiteName}提供{strVideoName} 第{intVideoPlaySort}集免费播放,{strVideoDescription},主演{strVideoActor},{strSiteKeywords},访问{strSiteDomain}!',
|
||||
'畅享{strVideoName} 第{intVideoPlaySort}集高清,{strVideoDescription},{strVideoDirector}执导,{strSiteKeywords},立即访问{strSiteDomain}!',
|
||||
'{strSiteName}献上{strVideoName} 第{intVideoPlaySort}集,{strVideoDescription},{strVideoActor}主演{strVideoCategoryName},{strSiteKeywords},访问{strSiteDomain}!',
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
293
code/scripts/seo_copy_published_audit.php
Normal file
293
code/scripts/seo_copy_published_audit.php
Normal file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
require dirname(__DIR__) . '/app/common/helper/SeoCopyStore.php';
|
||||
require dirname(__DIR__) . '/app/common/helper/SeoCopySchema.php';
|
||||
|
||||
use app\common\helper\SeoCopySchema;
|
||||
|
||||
function printUsage(): void
|
||||
{
|
||||
echo "Usage:\n";
|
||||
echo " php scripts/seo_copy_published_audit.php [--root=/abs/path] [--host=host-dir] [--format=json|text] [--sample-detail=<videoId>] [--sample-play=<videoId>:<playType>:<episode>]\n\n";
|
||||
echo "Examples:\n";
|
||||
echo " php scripts/seo_copy_published_audit.php\n";
|
||||
echo " php scripts/seo_copy_published_audit.php --host=lmjcg-com --format=text\n";
|
||||
echo " php scripts/seo_copy_published_audit.php --host=lmjcg-com --sample-detail=76310 --sample-play=76310:default:1 --format=text\n";
|
||||
}
|
||||
|
||||
function parseArgs(array $argv): array
|
||||
{
|
||||
$arrOptions = [
|
||||
'root' => dirname(__DIR__) . '/data/seo_copy_published',
|
||||
'host' => '',
|
||||
'format' => 'json',
|
||||
'sample_detail' => '',
|
||||
'sample_play' => '',
|
||||
];
|
||||
|
||||
array_shift($argv);
|
||||
|
||||
foreach ($argv as $strArg) {
|
||||
if (str_starts_with($strArg, '--root=')) {
|
||||
$arrOptions['root'] = trim(substr($strArg, strlen('--root=')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($strArg, '--host=')) {
|
||||
$arrOptions['host'] = trim(substr($strArg, strlen('--host=')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($strArg, '--format=')) {
|
||||
$arrOptions['format'] = strtolower(trim(substr($strArg, strlen('--format='))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($strArg, '--sample-detail=')) {
|
||||
$arrOptions['sample_detail'] = trim(substr($strArg, strlen('--sample-detail=')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($strArg, '--sample-play=')) {
|
||||
$arrOptions['sample_play'] = trim(substr($strArg, strlen('--sample-play=')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($strArg, ['-h', '--help'], true)) {
|
||||
printUsage();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return $arrOptions;
|
||||
}
|
||||
|
||||
function listHostDirs(string $strRoot, string $strHostFilter): array
|
||||
{
|
||||
if (!is_dir($strRoot)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$arrHosts = [];
|
||||
$arrEntries = scandir($strRoot);
|
||||
if (!is_array($arrEntries)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($arrEntries as $strEntry) {
|
||||
if ($strEntry === '.' || $strEntry === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strPath = $strRoot . '/' . $strEntry;
|
||||
if (!is_dir($strPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($strHostFilter !== '' && $strHostFilter !== $strEntry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrHosts[] = $strEntry;
|
||||
}
|
||||
|
||||
sort($arrHosts);
|
||||
return $arrHosts;
|
||||
}
|
||||
|
||||
function collectSceneSummary(string $strRoot, string $strHost, string $strScene): array
|
||||
{
|
||||
$strSceneDir = $strRoot . '/' . $strHost . '/' . $strScene;
|
||||
$arrSummary = [
|
||||
'exists' => is_dir($strSceneDir),
|
||||
'total_files' => 0,
|
||||
'default_exists' => false,
|
||||
'non_default_count' => 0,
|
||||
'sample_non_default_keys' => [],
|
||||
];
|
||||
|
||||
if (!is_dir($strSceneDir)) {
|
||||
return $arrSummary;
|
||||
}
|
||||
|
||||
$arrFiles = glob($strSceneDir . '/*.json') ?: [];
|
||||
sort($arrFiles);
|
||||
|
||||
foreach ($arrFiles as $strFile) {
|
||||
if (!is_file($strFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrSummary['total_files']++;
|
||||
$strPageKey = preg_replace('/\.json$/i', '', basename($strFile));
|
||||
if ($strPageKey === 'default') {
|
||||
$arrSummary['default_exists'] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrSummary['non_default_count']++;
|
||||
if (count($arrSummary['sample_non_default_keys']) < 10) {
|
||||
$arrSummary['sample_non_default_keys'][] = $strPageKey;
|
||||
}
|
||||
}
|
||||
|
||||
return $arrSummary;
|
||||
}
|
||||
|
||||
function buildExpectedChecks(string $strRoot, string $strHost, string $strSampleDetail, string $strSamplePlay): array
|
||||
{
|
||||
$arrChecks = [];
|
||||
|
||||
if ($strSampleDetail !== '') {
|
||||
$strPageKey = SeoCopySchema::buildScenePageKey('detail', [$strSampleDetail]);
|
||||
$strPath = $strRoot . '/' . $strHost . '/detail/' . $strPageKey . '.json';
|
||||
$arrChecks[] = [
|
||||
'scene' => 'detail',
|
||||
'input' => $strSampleDetail,
|
||||
'expected_page_key' => $strPageKey,
|
||||
'exists' => is_file($strPath),
|
||||
'path' => $strPath,
|
||||
];
|
||||
}
|
||||
|
||||
if ($strSamplePlay !== '') {
|
||||
$arrParts = explode(':', $strSamplePlay);
|
||||
$strVideoId = trim((string)($arrParts[0] ?? ''));
|
||||
$strPlayType = trim((string)($arrParts[1] ?? 'default'));
|
||||
$strEpisode = trim((string)($arrParts[2] ?? '1'));
|
||||
$strPageKey = SeoCopySchema::buildScenePageKey('play', [$strVideoId, $strPlayType, $strEpisode]);
|
||||
$strPath = $strRoot . '/' . $strHost . '/play/' . $strPageKey . '.json';
|
||||
$arrChecks[] = [
|
||||
'scene' => 'play',
|
||||
'input' => $strSamplePlay,
|
||||
'expected_page_key' => $strPageKey,
|
||||
'exists' => is_file($strPath),
|
||||
'path' => $strPath,
|
||||
];
|
||||
}
|
||||
|
||||
return $arrChecks;
|
||||
}
|
||||
|
||||
function summarizeHost(string $strRoot, string $strHost, string $strSampleDetail, string $strSamplePlay): array
|
||||
{
|
||||
$arrScenes = [];
|
||||
foreach (SeoCopySchema::getSupportedScenes() as $strScene) {
|
||||
$arrScenes[$strScene] = collectSceneSummary($strRoot, $strHost, $strScene);
|
||||
}
|
||||
|
||||
$arrDetail = $arrScenes['detail'] ?? [];
|
||||
$arrPlay = $arrScenes['play'] ?? [];
|
||||
|
||||
return [
|
||||
'host' => $strHost,
|
||||
'detail_default_only' => !empty($arrDetail['default_exists']) && (int)($arrDetail['non_default_count'] ?? 0) === 0,
|
||||
'play_default_only' => !empty($arrPlay['default_exists']) && (int)($arrPlay['non_default_count'] ?? 0) === 0,
|
||||
'detail_has_non_default' => (int)($arrDetail['non_default_count'] ?? 0) > 0,
|
||||
'play_has_non_default' => (int)($arrPlay['non_default_count'] ?? 0) > 0,
|
||||
'scenes' => $arrScenes,
|
||||
'expected_checks' => buildExpectedChecks($strRoot, $strHost, $strSampleDetail, $strSamplePlay),
|
||||
];
|
||||
}
|
||||
|
||||
function renderText(array $arrSummary): string
|
||||
{
|
||||
$arrLines = [
|
||||
'root: ' . $arrSummary['root'],
|
||||
'total_hosts: ' . $arrSummary['total_hosts'],
|
||||
'hosts_detail_default_only: ' . $arrSummary['hosts_detail_default_only'],
|
||||
'hosts_play_default_only: ' . $arrSummary['hosts_play_default_only'],
|
||||
'hosts_detail_has_non_default: ' . $arrSummary['hosts_detail_has_non_default'],
|
||||
'hosts_play_has_non_default: ' . $arrSummary['hosts_play_has_non_default'],
|
||||
'',
|
||||
];
|
||||
|
||||
foreach ($arrSummary['hosts'] as $arrHost) {
|
||||
$arrLines[] = '[host] ' . $arrHost['host'];
|
||||
$arrLines[] = ' detail_default_only: ' . ($arrHost['detail_default_only'] ? 'yes' : 'no');
|
||||
$arrLines[] = ' play_default_only: ' . ($arrHost['play_default_only'] ? 'yes' : 'no');
|
||||
|
||||
foreach (['detail', 'play', 'home', 'category_index', 'category_list', 'search', 'rank_index', 'rank_list', 'forge'] as $strScene) {
|
||||
$arrScene = $arrHost['scenes'][$strScene] ?? [];
|
||||
if (empty($arrScene)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrLines[] = sprintf(
|
||||
' %s: total=%d, default=%s, non_default=%d',
|
||||
$strScene,
|
||||
(int)($arrScene['total_files'] ?? 0),
|
||||
!empty($arrScene['default_exists']) ? 'yes' : 'no',
|
||||
(int)($arrScene['non_default_count'] ?? 0)
|
||||
);
|
||||
|
||||
if (!empty($arrScene['sample_non_default_keys'])) {
|
||||
$arrLines[] = ' sample_non_default: ' . implode(', ', $arrScene['sample_non_default_keys']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($arrHost['expected_checks'])) {
|
||||
$arrLines[] = ' expected_checks:';
|
||||
foreach ($arrHost['expected_checks'] as $arrCheck) {
|
||||
$arrLines[] = sprintf(
|
||||
' %s | key=%s | exists=%s',
|
||||
$arrCheck['scene'],
|
||||
$arrCheck['expected_page_key'],
|
||||
$arrCheck['exists'] ? 'yes' : 'no'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$arrLines[] = '';
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $arrLines) . PHP_EOL;
|
||||
}
|
||||
|
||||
$arrOptions = parseArgs($argv);
|
||||
$strRoot = rtrim($arrOptions['root'], '/');
|
||||
|
||||
if (!is_dir($strRoot)) {
|
||||
fwrite(STDERR, "Root not found: {$strRoot}\n");
|
||||
printUsage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$arrHosts = listHostDirs($strRoot, $arrOptions['host']);
|
||||
$arrSummary = [
|
||||
'root' => realpath($strRoot) ?: $strRoot,
|
||||
'total_hosts' => count($arrHosts),
|
||||
'hosts_detail_default_only' => 0,
|
||||
'hosts_play_default_only' => 0,
|
||||
'hosts_detail_has_non_default' => 0,
|
||||
'hosts_play_has_non_default' => 0,
|
||||
'hosts' => [],
|
||||
];
|
||||
|
||||
foreach ($arrHosts as $strHost) {
|
||||
$arrHostSummary = summarizeHost($strRoot, $strHost, $arrOptions['sample_detail'], $arrOptions['sample_play']);
|
||||
if ($arrHostSummary['detail_default_only']) {
|
||||
$arrSummary['hosts_detail_default_only']++;
|
||||
}
|
||||
if ($arrHostSummary['play_default_only']) {
|
||||
$arrSummary['hosts_play_default_only']++;
|
||||
}
|
||||
if ($arrHostSummary['detail_has_non_default']) {
|
||||
$arrSummary['hosts_detail_has_non_default']++;
|
||||
}
|
||||
if ($arrHostSummary['play_has_non_default']) {
|
||||
$arrSummary['hosts_play_has_non_default']++;
|
||||
}
|
||||
|
||||
$arrSummary['hosts'][] = $arrHostSummary;
|
||||
}
|
||||
|
||||
if ($arrOptions['format'] === 'text') {
|
||||
echo renderText($arrSummary);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo json_encode($arrSummary, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
|
||||
@@ -162,6 +162,7 @@ function startTempServer(string $strCodeRoot, string $strSourceRoot, int $intPor
|
||||
|
||||
$Process = proc_open($strCommand, $arrDescriptors, $arrPipes, $strCodeRoot, array_merge($_ENV, [
|
||||
'SEO_COPY_ROOT_OVERRIDE' => $strSourceRoot,
|
||||
'SEO_COPY_PUBLISHED_ROOT' => $strSourceRoot,
|
||||
]));
|
||||
|
||||
if (!is_resource($Process)) {
|
||||
|
||||
184
code/scripts/seo_copy_restore_from_publish_logs.php
Normal file
184
code/scripts/seo_copy_restore_from_publish_logs.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use app\common\helper\SeoCopyStore;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if (!function_exists('base_path')) {
|
||||
function base_path(string $path = ''): string
|
||||
{
|
||||
$base = dirname(__DIR__);
|
||||
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||
}
|
||||
}
|
||||
|
||||
function usage(): void
|
||||
{
|
||||
$script = basename(__FILE__);
|
||||
echo <<<TXT
|
||||
Usage:
|
||||
php code/scripts/{$script} --host=chuanjiafeng-net [--source=/abs/log/root] [--target=/abs/target/root] [--dry-run]
|
||||
|
||||
Options:
|
||||
--host Host directory name under seo_copy, e.g. chuanjiafeng-net
|
||||
--source Publish log root. Defaults to code/storage/seo_copy_publish_logs
|
||||
--target Target root. Defaults to code/data/seo_copy_published
|
||||
--dry-run Preview only, do not write files
|
||||
|
||||
Examples:
|
||||
php code/scripts/{$script} --host=chuanjiafeng-net --dry-run
|
||||
php code/scripts/{$script} --host=liangzuan-net
|
||||
|
||||
TXT;
|
||||
}
|
||||
|
||||
function resolve_path(string $path): string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str_starts_with($path, '/')) {
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
|
||||
$arrCandidates = [
|
||||
getcwd() . '/' . ltrim($path, '/'),
|
||||
base_path($path),
|
||||
];
|
||||
|
||||
foreach ($arrCandidates as $strCandidate) {
|
||||
if (file_exists($strCandidate) || is_dir($strCandidate)) {
|
||||
return rtrim($strCandidate, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim($arrCandidates[0], '/');
|
||||
}
|
||||
|
||||
$arrArgs = [];
|
||||
foreach (array_slice($argv, 1) as $strArg) {
|
||||
if (strncmp($strArg, '--', 2) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strArg = substr($strArg, 2);
|
||||
if ($strArg === 'dry-run') {
|
||||
$arrArgs['dry-run'] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrParts = explode('=', $strArg, 2);
|
||||
$arrArgs[$arrParts[0]] = $arrParts[1] ?? '';
|
||||
}
|
||||
|
||||
$strHost = trim((string)($arrArgs['host'] ?? ''));
|
||||
$strSource = trim((string)($arrArgs['source'] ?? base_path('storage/seo_copy_publish_logs')));
|
||||
$strTarget = trim((string)($arrArgs['target'] ?? base_path('data/seo_copy_published')));
|
||||
$boolDryRun = !empty($arrArgs['dry-run']);
|
||||
|
||||
if ($strHost === '') {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$strSource = resolve_path($strSource);
|
||||
$strTarget = resolve_path($strTarget);
|
||||
|
||||
if (!is_dir($strSource)) {
|
||||
fwrite(STDERR, "publish log root not found: {$strSource}\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
$arrScenes = ['home', 'category_index', 'category_list', 'search', 'rank_index', 'rank_list', 'detail', 'forge', 'play'];
|
||||
$arrFilesByScene = [];
|
||||
$arrMatchedPaths = [];
|
||||
|
||||
$it = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($strSource, FilesystemIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($it as $file) {
|
||||
if (!$file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strPath = $file->getPathname();
|
||||
if (!str_ends_with($strPath, '.json')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!preg_match(
|
||||
'#/backups/[^/]+/' . preg_quote($strHost, '#') . '/([^/]+)/([^/]+)\.json$#',
|
||||
$strPath,
|
||||
$arrMatch
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strScene = $arrMatch[1];
|
||||
$strPageKey = $arrMatch[2];
|
||||
if (!in_array($strScene, $arrScenes, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrFilesByScene[$strScene][$strPageKey] = $strPath;
|
||||
$arrMatchedPaths[] = $strPath;
|
||||
}
|
||||
|
||||
if (empty($arrFilesByScene)) {
|
||||
fwrite(STDERR, "no backup files found for host: {$strHost}\n");
|
||||
exit(3);
|
||||
}
|
||||
|
||||
ksort($arrFilesByScene);
|
||||
|
||||
$arrSummary = [];
|
||||
foreach ($arrScenes as $strScene) {
|
||||
$arrPageMap = $arrFilesByScene[$strScene] ?? [];
|
||||
ksort($arrPageMap);
|
||||
|
||||
$arrSummary[$strScene] = [
|
||||
'count' => count($arrPageMap),
|
||||
'files' => array_keys($arrPageMap),
|
||||
];
|
||||
|
||||
if ($boolDryRun) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($arrPageMap as $strPageKey => $strFile) {
|
||||
$strPageKey = (string)$strPageKey;
|
||||
$strJson = @file_get_contents($strFile);
|
||||
if ($strJson === false || trim($strJson) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrData = json_decode($strJson, true);
|
||||
if (!is_array($arrData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SeoCopyStore::writePageDataToRoot($strTarget, $strHost, $strScene, $strPageKey, $arrData);
|
||||
}
|
||||
}
|
||||
|
||||
echo "host: {$strHost}\n";
|
||||
echo "source: {$strSource}\n";
|
||||
echo "target: {$strTarget}\n";
|
||||
echo "mode: " . ($boolDryRun ? 'dry-run' : 'write') . "\n";
|
||||
echo "matched_paths: " . count($arrMatchedPaths) . "\n\n";
|
||||
|
||||
foreach ($arrSummary as $strScene => $arrInfo) {
|
||||
if ($arrInfo['count'] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "[{$strScene}] count=" . $arrInfo['count'] . "\n";
|
||||
foreach ($arrInfo['files'] as $strName) {
|
||||
echo " - {$strName}.json\n";
|
||||
}
|
||||
}
|
||||
152
code/scripts/seo_copy_restore_from_source.php
Normal file
152
code/scripts/seo_copy_restore_from_source.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use app\common\helper\SeoCopyStore;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
if (!function_exists('base_path')) {
|
||||
function base_path(string $path = ''): string
|
||||
{
|
||||
$base = dirname(__DIR__);
|
||||
return $path !== '' ? $base . '/' . ltrim($path, '/') : $base;
|
||||
}
|
||||
}
|
||||
|
||||
function usage(): void
|
||||
{
|
||||
$script = basename(__FILE__);
|
||||
echo <<<TXT
|
||||
Usage:
|
||||
php code/scripts/{$script} --host=chuanjiafeng-net --source=/abs/source/path [--target=/abs/target/path] [--dry-run]
|
||||
|
||||
Options:
|
||||
--host Host directory name under seo_copy, e.g. chuanjiafeng-net
|
||||
--source Source root that contains <host>/detail/*.json and <host>/play/*.json
|
||||
--target Target root. Defaults to code/data/seo_copy_published
|
||||
--dry-run Preview only, do not write files
|
||||
|
||||
Examples:
|
||||
php code/scripts/{$script} \\
|
||||
--host=chuanjiafeng-net \\
|
||||
--source=code/storage/domain_bootstrap_bundles/chuanjiafeng-compact/data/seo_copy \\
|
||||
--dry-run
|
||||
|
||||
TXT;
|
||||
}
|
||||
|
||||
function resolve_path(string $path): string
|
||||
{
|
||||
$path = trim($path);
|
||||
if ($path === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str_starts_with($path, '/')) {
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
|
||||
$arrCandidates = [
|
||||
getcwd() . '/' . ltrim($path, '/'),
|
||||
base_path($path),
|
||||
];
|
||||
|
||||
foreach ($arrCandidates as $strCandidate) {
|
||||
if (file_exists($strCandidate) || is_dir($strCandidate)) {
|
||||
return rtrim($strCandidate, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim($arrCandidates[0], '/');
|
||||
}
|
||||
|
||||
$arrArgs = [];
|
||||
foreach (array_slice($argv, 1) as $strArg) {
|
||||
if (strncmp($strArg, '--', 2) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strArg = substr($strArg, 2);
|
||||
if ($strArg === 'dry-run') {
|
||||
$arrArgs['dry-run'] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrParts = explode('=', $strArg, 2);
|
||||
$arrArgs[$arrParts[0]] = $arrParts[1] ?? '';
|
||||
}
|
||||
|
||||
$strHost = trim((string)($arrArgs['host'] ?? ''));
|
||||
$strSource = trim((string)($arrArgs['source'] ?? ''));
|
||||
$strTarget = trim((string)($arrArgs['target'] ?? base_path('data/seo_copy_published')));
|
||||
$boolDryRun = !empty($arrArgs['dry-run']);
|
||||
|
||||
if ($strHost === '' || $strSource === '') {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$strSource = resolve_path($strSource);
|
||||
$strTarget = resolve_path($strTarget);
|
||||
|
||||
$strHostSource = $strSource . '/' . $strHost;
|
||||
if (!is_dir($strHostSource)) {
|
||||
fwrite(STDERR, "source host dir not found: {$strHostSource}\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
$arrScenes = ['detail', 'play'];
|
||||
$arrSummary = [];
|
||||
|
||||
foreach ($arrScenes as $strScene) {
|
||||
$strSceneDir = $strHostSource . '/' . $strScene;
|
||||
$arrFiles = [];
|
||||
|
||||
if (is_dir($strSceneDir)) {
|
||||
foreach (glob($strSceneDir . '/*.json') ?: [] as $strFile) {
|
||||
if (!is_file($strFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrFiles[] = $strFile;
|
||||
}
|
||||
}
|
||||
|
||||
sort($arrFiles);
|
||||
$arrSummary[$strScene] = [
|
||||
'count' => count($arrFiles),
|
||||
'files' => array_map('basename', $arrFiles),
|
||||
];
|
||||
|
||||
if ($boolDryRun) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($arrFiles as $strFile) {
|
||||
$strPageKey = basename($strFile, '.json');
|
||||
$strJson = file_get_contents($strFile);
|
||||
if ($strJson === false || trim($strJson) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrData = json_decode($strJson, true);
|
||||
if (!is_array($arrData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SeoCopyStore::writePageDataToRoot($strTarget, $strHost, $strScene, $strPageKey, $arrData);
|
||||
}
|
||||
}
|
||||
|
||||
echo "host: {$strHost}\n";
|
||||
echo "source: {$strSource}\n";
|
||||
echo "target: {$strTarget}\n";
|
||||
echo "mode: " . ($boolDryRun ? 'dry-run' : 'write') . "\n\n";
|
||||
|
||||
foreach ($arrSummary as $strScene => $arrInfo) {
|
||||
echo "[{$strScene}] count=" . $arrInfo['count'] . "\n";
|
||||
foreach ($arrInfo['files'] as $strName) {
|
||||
echo " - {$strName}\n";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user