整体UI调整
This commit is contained in:
@@ -8,7 +8,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: "关于我们",
|
||||
description: "了解海角社区的品牌、产品与服务。",
|
||||
description: "了解黄果黑料的品牌、产品与服务。",
|
||||
},
|
||||
canonical: "/about",
|
||||
});
|
||||
@@ -22,7 +22,7 @@ export default async function Page() {
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/about",
|
||||
title: "关于我们",
|
||||
description: "了解海角社区的品牌、产品与服务。",
|
||||
description: "了解黄果黑料的品牌、产品与服务。",
|
||||
type: "AboutPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import { FeedbackPage } from "@/components/feedback/feedback-page";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { buildWebPageJSONLD, getHomeBaseSEOCached } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "问题反馈 - 海角社区",
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
export default async function Page() {
|
||||
const siteSeo = await getHomeBaseSEOCached();
|
||||
return (
|
||||
<>
|
||||
<JsonLd
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/feedback",
|
||||
title: "问题反馈",
|
||||
description: "向海角社区提交问题反馈与建议。",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
["问题反馈", "/feedback"],
|
||||
],
|
||||
siteName: siteSeo.siteName,
|
||||
})}
|
||||
/>
|
||||
<FeedbackPage gmailAddr={siteSeo.config?.gmailAddr} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { PageLoading } from "@/components/common/page-loading";
|
||||
import { HelpPage } from "@/components/help/help-page";
|
||||
import { HELP_RICHTEXT_TITLES, loadRichTextFirst } from "@/components/richtext/load-richtext";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { buildWebPageJSONLD, getHomeBaseSEOCached, mergeSEO, resolvePageMetadata } from "@/lib/seo";
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const data = await loadRichTextFirst([...HELP_RICHTEXT_TITLES]);
|
||||
return resolvePageMetadata({
|
||||
seoInfo: mergeSEO(data?.seoInfo, {
|
||||
title: "帮助中心",
|
||||
description:
|
||||
data?.seoInfo?.description?.trim() ||
|
||||
data?.desc?.trim() ||
|
||||
"查看海角社区常见问题、播放帮助与客服支持信息。",
|
||||
}),
|
||||
canonical: "/help",
|
||||
});
|
||||
}
|
||||
|
||||
async function HelpRouteContent() {
|
||||
const data = await loadRichTextFirst([...HELP_RICHTEXT_TITLES]);
|
||||
const siteSeo = await getHomeBaseSEOCached();
|
||||
const seo = mergeSEO(data?.seoInfo, siteSeo.seo);
|
||||
return (
|
||||
<>
|
||||
<JsonLd
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/help",
|
||||
title: seo.title || "帮助中心",
|
||||
description:
|
||||
seo.description ||
|
||||
data?.desc ||
|
||||
data?.title ||
|
||||
"查看海角社区常见问题、播放帮助与客服支持信息。",
|
||||
type: "WebPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
["帮助中心", "/help"],
|
||||
],
|
||||
siteName: siteSeo.siteName,
|
||||
})}
|
||||
/>
|
||||
<HelpPage data={data} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Suspense fallback={<PageLoading />}>
|
||||
<HelpRouteContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
+4
-27
@@ -5,7 +5,6 @@ import { Suspense } from "react";
|
||||
import { AdDialogSlot } from "@/components/ad/ad-dialog-slot";
|
||||
import { AddressDialogSlot } from "@/components/address/address-dialog-slot";
|
||||
import { AgeGateDialogHost } from "@/components/age-gate/age-gate-dialog";
|
||||
import { TrackerHost } from "@/components/analytics/tracker-host";
|
||||
import { RequestHeadersPrime } from "@/components/api/request-headers-prime";
|
||||
import { AuthUnauthorizedBridge } from "@/components/auth/auth-unauthorized-bridge";
|
||||
import { GuestAuthBootstrap } from "@/components/auth/guest-auth-bootstrap";
|
||||
@@ -40,20 +39,17 @@ const pageScaleScript = `(function () {
|
||||
window.addEventListener("resize", update);
|
||||
})();`;
|
||||
|
||||
/** GA4 测量 ID(全站 head 注入) */
|
||||
const GA4_MEASUREMENT_ID = "G-748CPNEVP5";
|
||||
|
||||
/** metadataBase 随请求 Host 变化,保证 rel=canonical 与访问地址一致 */
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const origin = await getRequestOrigin();
|
||||
return {
|
||||
metadataBase: new URL(origin),
|
||||
title: {
|
||||
default: "海角社区",
|
||||
default: "黄果黑料",
|
||||
template: "%s",
|
||||
},
|
||||
description: "发现好故事,开启追剧时光",
|
||||
applicationName: "海角社区",
|
||||
applicationName: "黄果黑料",
|
||||
verification: {
|
||||
google: "0Bby47n1AEIB3W8wtLZ-oFQ3l8xRqRT78oA0JVRJ5pM",
|
||||
yandex: "69b55a6aa9ee9a5f",
|
||||
@@ -64,8 +60,8 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "zh_CN",
|
||||
siteName: "海角社区",
|
||||
images: [{ url: OG_DEFAULT_IMAGE, alt: "海角社区" }],
|
||||
siteName: "黄果黑料",
|
||||
images: [{ url: OG_DEFAULT_IMAGE, alt: "黄果黑料" }],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
@@ -93,29 +89,10 @@ export default function RootLayout({ children }: LayoutProps<"/">) {
|
||||
// beforeInteractive 会给 html 加 is-cn-browser 等 class,需压制告警
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
{/* Google Analytics (GA4) */}
|
||||
<Script
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${GA4_MEASUREMENT_ID}`}
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
<Script id="ga4-gtag" strategy="afterInteractive">
|
||||
{`
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${GA4_MEASUREMENT_ID}');
|
||||
`.trim()}
|
||||
</Script>
|
||||
</head>
|
||||
<body className="flex min-h-full flex-col">
|
||||
<Suspense fallback={null}>
|
||||
<RequestHeadersPrime />
|
||||
</Suspense>
|
||||
<Suspense fallback={null}>
|
||||
<TrackerHost />
|
||||
</Suspense>
|
||||
<Suspense fallback={null}>
|
||||
<HomeBaseDebugSlot />
|
||||
</Suspense>
|
||||
|
||||
@@ -30,8 +30,8 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: data.title,
|
||||
description: data.subtitle || `浏览海角社区「${data.title}」更多精选内容。`,
|
||||
keyworld: ["海角社区", data.title],
|
||||
description: data.subtitle || `浏览黄果黑料「${data.title}」更多精选内容。`,
|
||||
keyworld: ["黄果黑料", data.title],
|
||||
},
|
||||
page: pageNum,
|
||||
canonical: moreListHref(pathKey, pageNum),
|
||||
@@ -72,7 +72,7 @@ export default async function Page({ params }: { params: Params }) {
|
||||
data={buildWebPageJSONLD({
|
||||
path: moreListHref(pathKey, page),
|
||||
title: data.title,
|
||||
description: data.subtitle || `浏览海角社区「${data.title}」更多精选内容。`,
|
||||
description: data.subtitle || `浏览黄果黑料「${data.title}」更多精选内容。`,
|
||||
type: "CollectionPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -9,7 +9,7 @@ import { moreListHref } from "@/components/more/route";
|
||||
|
||||
/** 无 id 的空壳页;正式内容在 /more/{id},本页不索引 */
|
||||
export const metadata: Metadata = {
|
||||
title: "更多内容 - 海角社区",
|
||||
title: "更多内容 - 黄果黑料",
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NavPage } from "@/components/nav/nav-page";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { getHomeBaseCached } from "@/lib/api/home";
|
||||
import type { AppTranslationInfo, ConfigInfo } from "@/lib/api/types";
|
||||
import { buildWebPageJSONLD, mergeSEO, resolvePageMetadata } from "@/lib/seo";
|
||||
import { buildWebPageJSONLD, mergeSEO, resolvePageMetadata, SITE_NAME } from "@/lib/seo";
|
||||
|
||||
async function loadNavBase(): Promise<{
|
||||
config?: ConfigInfo;
|
||||
@@ -31,17 +31,17 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
description: config?.dhDescription,
|
||||
keyworld: config?.dhKeyworld,
|
||||
},
|
||||
siteName: config?.dhSiteName?.trim() || undefined,
|
||||
fallbackTitle: `${config?.dhSiteName?.trim() || "海角社区"} · 官方导航`,
|
||||
fallbackDescription: `${config?.dhSiteName?.trim() || "海角社区"}官方导航页,提供最新访问线路、永久地址、APP 下载与官方联系方式。`,
|
||||
fallbackKeywords: [config?.dhSiteName || "海角社区", "官方导航", "最新地址"].filter(Boolean) as string[],
|
||||
siteName: SITE_NAME,
|
||||
fallbackTitle: `${SITE_NAME} · 官方导航`,
|
||||
fallbackDescription: `${SITE_NAME}官方导航页,提供最新访问线路、永久地址、APP 下载与官方联系方式。`,
|
||||
fallbackKeywords: [SITE_NAME, "官方导航", "最新地址"],
|
||||
canonical: "/nav",
|
||||
});
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
const { config, bottonTranslations } = await loadNavBase();
|
||||
const siteName = config?.dhSiteName?.trim() || config?.webSiteName?.trim() || "海角社区";
|
||||
const siteName = SITE_NAME;
|
||||
const seo = mergeSEO(
|
||||
{ title: config?.dhTitle, description: config?.dhDescription, keyworld: config?.dhKeyworld },
|
||||
{ title: config?.webTitle, description: config?.webDescription, keyworld: config?.webKeyworld },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { GoBackButton } from "@/components/not-found/go-back-button";
|
||||
import { SeoBreadcrumb } from "@/components/seo/seo-breadcrumb";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "页面不存在 - 海角社区",
|
||||
title: "页面不存在 - 黄果黑料",
|
||||
robots: { index: false, follow: false },
|
||||
};
|
||||
|
||||
|
||||
+5
-5
@@ -48,9 +48,9 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
// seoInfo → home/base 公共 web* → 代码兜底
|
||||
return resolvePageMetadata({
|
||||
seoInfo: home?.seoInfo,
|
||||
fallbackTitle: "海角社区 - 热门短剧在线看",
|
||||
fallbackDescription: "海角社区汇集热门短剧、最近上新、排行榜与精选专题推荐,支持高清在线播放。",
|
||||
fallbackKeywords: ["海角社区", "短剧", "在线短剧", "热门短剧"],
|
||||
fallbackTitle: "黄果黑料 - 热门短剧在线看",
|
||||
fallbackDescription: "黄果黑料汇集热门短剧、最近上新、排行榜与精选专题推荐,支持高清在线播放。",
|
||||
fallbackKeywords: ["黄果黑料", "短剧", "在线短剧", "热门短剧"],
|
||||
canonical: "/",
|
||||
});
|
||||
}
|
||||
@@ -64,9 +64,9 @@ export default async function Home() {
|
||||
: null;
|
||||
|
||||
const seo = mergeSEO(home?.seoInfo, siteSeo.seo);
|
||||
const homeTitle = seo.title || "海角社区 - 热门短剧在线看";
|
||||
const homeTitle = seo.title || "黄果黑料 - 热门短剧在线看";
|
||||
const homeDescription =
|
||||
seo.description || "海角社区汇集热门短剧、最近上新、排行榜与精选专题推荐,支持高清在线播放。";
|
||||
seo.description || "黄果黑料汇集热门短剧、最近上新、排行榜与精选专题推荐,支持高清在线播放。";
|
||||
const itemListUrls =
|
||||
home?.homeBannerData
|
||||
?.filter((item): item is PlayletData & { id: number } => Boolean(item?.id))
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: "隐私政策",
|
||||
description: "查看海角社区隐私政策与个人信息保护说明。",
|
||||
description: "查看黄果黑料隐私政策与个人信息保护说明。",
|
||||
},
|
||||
canonical: "/privacy",
|
||||
});
|
||||
@@ -22,7 +22,7 @@ export default async function Page() {
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/privacy",
|
||||
title: "隐私政策",
|
||||
description: "查看海角社区隐私政策与个人信息保护说明。",
|
||||
description: "查看黄果黑料隐私政策与个人信息保护说明。",
|
||||
type: "WebPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -39,8 +39,8 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: `${tabText}排行榜`,
|
||||
description: `查看海角社区实时更新的${tabText}排行榜,浏览热播内容与精选推荐。`,
|
||||
keyworld: ["海角社区", "排行榜", tabText],
|
||||
description: `查看黄果黑料实时更新的${tabText}排行榜,浏览热播内容与精选推荐。`,
|
||||
keyworld: ["黄果黑料", "排行榜", tabText],
|
||||
},
|
||||
page: pageNum,
|
||||
canonical: rankingHref(tab, pageNum),
|
||||
@@ -100,7 +100,7 @@ async function RankingContent({ params }: { params: Params }) {
|
||||
data={buildWebPageJSONLD({
|
||||
path: rankingHref(tab, page),
|
||||
title: `${tabText}排行榜`,
|
||||
description: `查看海角社区实时更新的${tabText}排行榜,浏览热播内容与精选推荐。`,
|
||||
description: `查看黄果黑料实时更新的${tabText}排行榜,浏览热播内容与精选推荐。`,
|
||||
type: "CollectionPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
+2
-22
@@ -1,37 +1,17 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
import { getHomeBaseCached } from "@/lib/api/home";
|
||||
import { getRequestOrigin, getSiteOrigin } from "@/lib/seo/site";
|
||||
|
||||
/** 从地址字符串解析 origin(支持无协议域名) */
|
||||
function originFromAddr(value?: string | null) {
|
||||
const text = value?.trim();
|
||||
if (!text) return undefined;
|
||||
try {
|
||||
const withProto = /^https?:\/\//i.test(text) ? text : `https://${text}`;
|
||||
return new URL(withProto).origin;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/** 基础 robots:私有页 / 搜索 disallow,并声明 sitemap(永久域名取 config.foreverUrlAddr) */
|
||||
/** 基础 robots:私有页 / 搜索 disallow,并声明 sitemap(取本站域名) */
|
||||
export default async function robots(): Promise<MetadataRoute.Robots> {
|
||||
const forever = originFromAddr((await getHomeBaseCached()).config?.foreverUrlAddr);
|
||||
const configured = process.env.NEXT_PUBLIC_SITE_URL?.trim();
|
||||
const origin = forever || (configured ? getSiteOrigin() : await getRequestOrigin());
|
||||
const origin = configured ? getSiteOrigin() : await getRequestOrigin();
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
disallow: [
|
||||
"/profile",
|
||||
"/history",
|
||||
"/favorites",
|
||||
"/likes",
|
||||
"/settings",
|
||||
"/feedback",
|
||||
// 前缀匹配:/search、/search/短剧/... 均不抓
|
||||
"/search",
|
||||
],
|
||||
|
||||
@@ -27,8 +27,8 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: `搜索「${parsed.query}」`,
|
||||
description: `在海角社区搜索「${parsed.query}」的${parsed.type}结果。`,
|
||||
keyworld: ["海角社区", "搜索", parsed.query, parsed.type],
|
||||
description: `在黄果黑料搜索「${parsed.query}」的${parsed.type}结果。`,
|
||||
keyworld: ["黄果黑料", "搜索", parsed.query, parsed.type],
|
||||
},
|
||||
page: parsed.page,
|
||||
canonical: searchListHref(parsed.query, parsed.type, parsed.page),
|
||||
|
||||
@@ -17,8 +17,8 @@ function first(value: string | string[] | undefined) {
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return resolvePageMetadata({
|
||||
fallbackTitle: "搜索",
|
||||
fallbackDescription: "搜索海角社区短剧与视频。",
|
||||
fallbackKeywords: ["海角社区", "搜索"],
|
||||
fallbackDescription: "搜索黄果黑料短剧与视频。",
|
||||
fallbackKeywords: ["黄果黑料", "搜索"],
|
||||
canonical: "/search",
|
||||
// 搜索入口不收录;robots.txt 同步 Disallow /search
|
||||
robots: { index: false, follow: false },
|
||||
|
||||
@@ -13,12 +13,12 @@ import {
|
||||
playHref,
|
||||
RECOMMEND_DISPLAY_COUNT,
|
||||
resolvePlayURL,
|
||||
totalEpisodeOf,
|
||||
type DramaDetail,
|
||||
type DramaPlayView,
|
||||
type RelatedDrama,
|
||||
} from "@/components/drama-detail/data";
|
||||
import { DramaDetailPage } from "@/components/drama-detail/drama-detail-page";
|
||||
import { RelatedAside } from "@/components/drama-detail/related-aside";
|
||||
import { DramaPlayPage } from "@/components/drama-play/drama-play-page";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import {
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
import { ShortDramaPage } from "@/components/short-drama/short-drama-page";
|
||||
import {
|
||||
getPlayletChapterInfoCached,
|
||||
getPlayletGuessListCached,
|
||||
getPlayletInfoCached,
|
||||
getPlayletListCached,
|
||||
getPlayletRecommendListCached,
|
||||
@@ -52,6 +53,7 @@ import {
|
||||
buildPlayletDetailJSONLD,
|
||||
buildPlayletPlayJSONLD,
|
||||
buildWebPageJSONLD,
|
||||
clipText,
|
||||
getHomeBaseSEOCached,
|
||||
mergeSEO,
|
||||
resolveContentMetadata,
|
||||
@@ -80,7 +82,10 @@ function playletLookup(idOrSlug: string): { id?: string; slug?: string } {
|
||||
return { slug: idOrSlug };
|
||||
}
|
||||
|
||||
/** 更多好剧:GET /api/app/playlet/recommend/list(sortList=1 推荐) */
|
||||
/**
|
||||
* 同类短剧推荐:先取 GET /api/app/playlet/recommend/list(sortList=1 推荐);
|
||||
* 后台未配置推荐时为空,回退「猜你喜欢」(按当前短剧取相似)。
|
||||
*/
|
||||
async function loadPlayletRecommend(excludeId?: number | string | null): Promise<RelatedDrama[]> {
|
||||
try {
|
||||
const data = await getPlayletRecommendListCached({
|
||||
@@ -88,9 +93,20 @@ async function loadPlayletRecommend(excludeId?: number | string | null): Promise
|
||||
pageSize: RECOMMEND_PAGE_SIZE,
|
||||
sortList: 1,
|
||||
});
|
||||
return mapPlayletRecommendList(data, { excludeId, limit: RECOMMEND_DISPLAY_COUNT });
|
||||
const list = mapPlayletRecommendList(data, { excludeId, limit: RECOMMEND_DISPLAY_COUNT });
|
||||
if (list.length) return list;
|
||||
} catch (error) {
|
||||
console.error("[short-drama/recommend]", error);
|
||||
}
|
||||
try {
|
||||
const guess = await getPlayletGuessListCached({
|
||||
id: excludeId ?? undefined,
|
||||
pageNum: 1,
|
||||
pageSize: RECOMMEND_PAGE_SIZE,
|
||||
});
|
||||
return mapPlayletRecommendList(guess, { excludeId, limit: RECOMMEND_DISPLAY_COUNT });
|
||||
} catch (error) {
|
||||
console.error("[short-drama/guess]", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -140,9 +156,9 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
seoInfo: theme?.seoInfo,
|
||||
fallbackTitle: themeName ? `短剧·${themeName}·${sortText}` : `短剧·${sortText}`,
|
||||
fallbackDescription: themeName
|
||||
? `浏览海角社区短剧「${themeName}」题材,按${sortText}发现精彩作品。`
|
||||
: `浏览海角社区短剧列表,按${sortText}发现精彩作品。`,
|
||||
fallbackKeywords: ["海角社区", "短剧", themeName, sortText].filter(Boolean),
|
||||
? `浏览黄果黑料短剧「${themeName}」题材,按${sortText}发现精彩作品。`
|
||||
: `浏览黄果黑料短剧列表,按${sortText}发现精彩作品。`,
|
||||
fallbackKeywords: ["黄果黑料", "短剧", themeName, sortText].filter(Boolean),
|
||||
page: pageNum,
|
||||
canonical: shortDramaListHref(sortKey, pageNum, themeSlug),
|
||||
});
|
||||
@@ -151,20 +167,23 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
// 播放页 title:短剧标题 + 第几集 + 网页标题(站点名由 withSiteSuffix 追加)
|
||||
if (page?.length === 1) {
|
||||
const play = await loadPlayletPlay(decodeSegment(sort), page[0]);
|
||||
if (!play) return resolveContentMetadata({ fallbackTitle: "海角社区" });
|
||||
if (!play) return resolveContentMetadata({ fallbackTitle: "黄果黑料" });
|
||||
const dramaTitle = play.detail.title?.trim() || "短剧";
|
||||
const playTitle = `${dramaTitle} 第${play.episode}集`;
|
||||
const { title: _seoTitle, ...playSeoRest } = play.detail.seoInfo ?? {};
|
||||
// 播放页不用后台 title / description:每集都要带集数,避免同剧所有分集 TDK 完全相同
|
||||
const { title: _seoTitle, description: seoDescription, ...playSeoRest } = play.detail.seoInfo ?? {};
|
||||
void _seoTitle;
|
||||
return resolveContentMetadata({
|
||||
seoInfo: play.detail.seoInfo ? playSeoRest : null,
|
||||
fallbackTitle: playTitle,
|
||||
fallbackDescription:
|
||||
play.detail.desc || `在线观看《${dramaTitle}》第${play.episode}集`,
|
||||
fallbackDescription: [
|
||||
`《${dramaTitle}》第${play.episode}集(共${totalEpisodeOf(play.detail)}集)`,
|
||||
clipText(seoDescription || play.detail.desc, 70) || "按集收看,分集目录与同类短剧推荐见本页。",
|
||||
].join(":"),
|
||||
fallbackKeywords: [
|
||||
"海角社区",
|
||||
"黄果黑料",
|
||||
"短剧",
|
||||
"在线播放",
|
||||
"分集",
|
||||
dramaTitle,
|
||||
playTitle,
|
||||
...(play.detail.tagNames ?? []),
|
||||
@@ -173,16 +192,19 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
canonical: playHref(play.detail.id, play.episode, play.detail.slug),
|
||||
});
|
||||
}
|
||||
if (page?.length) return resolvePageMetadata({ fallbackTitle: "海角社区" });
|
||||
if (page?.length) return resolvePageMetadata({ fallbackTitle: "黄果黑料" });
|
||||
|
||||
// 详情页:TDK 用短剧详情(playlet/info)
|
||||
const detail = await loadPlayletDetail(decodeSegment(sort));
|
||||
if (!detail) return resolveContentMetadata({ fallbackTitle: "海角社区" });
|
||||
if (!detail) return resolveContentMetadata({ fallbackTitle: "黄果黑料" });
|
||||
return resolveContentMetadata({
|
||||
seoInfo: detail.seoInfo,
|
||||
fallbackTitle: detail.title,
|
||||
fallbackDescription: detail.desc || `在线观看《${detail.title}》,查看选集、剧情信息与相关推荐。`,
|
||||
fallbackKeywords: ["海角社区", "短剧", detail.title || "", ...(detail.tagNames ?? [])].filter(
|
||||
fallbackTitle: detail.title ? `${detail.title}(全${totalEpisodeOf(detail)}集)` : undefined,
|
||||
fallbackDescription: [
|
||||
`《${detail.title || "短剧"}》全${totalEpisodeOf(detail)}集${detail.themeNames?.[0] ? `·${detail.themeNames[0]}` : ""}`,
|
||||
clipText(detail.desc, 90) || "剧情介绍、分集目录与同类短剧推荐。",
|
||||
].join("|"),
|
||||
fallbackKeywords: ["黄果黑料", "短剧", detail.title || "", ...(detail.tagNames ?? [])].filter(
|
||||
Boolean,
|
||||
),
|
||||
canonical: dramaDetailHref(detail),
|
||||
@@ -317,8 +339,8 @@ async function ShortDramaRouteContent({ params }: { params: Params }) {
|
||||
const listDescription =
|
||||
listSeo.description ||
|
||||
(themeName
|
||||
? `浏览海角社区短剧「${themeName}」题材,按${sortText}发现精彩作品。`
|
||||
: `浏览海角社区短剧列表,按${sortText}发现精彩作品。`);
|
||||
? `浏览黄果黑料短剧「${themeName}」题材,按${sortText}发现精彩作品。`
|
||||
: `浏览黄果黑料短剧列表,按${sortText}发现精彩作品。`);
|
||||
return (
|
||||
<>
|
||||
<JsonLd
|
||||
@@ -365,7 +387,6 @@ async function ShortDramaRouteContent({ params }: { params: Params }) {
|
||||
<DramaDetailPage
|
||||
detail={mapped.detail}
|
||||
related={mapped.related}
|
||||
relatedAside={<RelatedAside items={mapped.related} />}
|
||||
adIcons={<AdIconsSlot variant="pc" />}
|
||||
promo={<AdBannersSlot variant="pc" />}
|
||||
adGrid={<AdIconsSlot variant="h5" />}
|
||||
@@ -407,7 +428,6 @@ async function ShortDramaRouteContent({ params }: { params: Params }) {
|
||||
episode={play.episode}
|
||||
src={play.src}
|
||||
related={play.related}
|
||||
relatedAside={<RelatedAside items={play.related} />}
|
||||
adIcons={<AdIconsSlot variant="pc" />}
|
||||
promo={<AdBannersSlot variant="pc" />}
|
||||
/>
|
||||
|
||||
@@ -87,7 +87,7 @@ export async function generateMetadata({
|
||||
const query = await searchParams;
|
||||
const parsed = parseTagSlug((await params).slug, query.kind, query.page);
|
||||
if (!parsed) {
|
||||
return resolvePageMetadata({ seoInfo: { title: "标签", keyworld: ["海角社区", "标签"] } });
|
||||
return resolvePageMetadata({ seoInfo: { title: "标签", keyworld: ["黄果黑料", "标签"] } });
|
||||
}
|
||||
|
||||
const { key, page, kind: preferredKind } = parsed;
|
||||
@@ -98,15 +98,15 @@ export async function generateMetadata({
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: `${data.name}标签`,
|
||||
description: `浏览海角社区「${data.name}」标签相关${unit}。`,
|
||||
keyworld: ["海角社区", "标签", data.name, unit],
|
||||
description: `浏览黄果黑料「${data.name}」标签相关${unit}。`,
|
||||
keyworld: ["黄果黑料", "标签", data.name, unit],
|
||||
},
|
||||
page,
|
||||
canonical: tagListHref(pathKey, page, data.kind),
|
||||
});
|
||||
} catch {
|
||||
return resolvePageMetadata({
|
||||
seoInfo: { title: key, keyworld: ["海角社区", "标签", key] },
|
||||
seoInfo: { title: key, keyworld: ["黄果黑料", "标签", key] },
|
||||
page,
|
||||
canonical: tagListHref(key, page, preferredKind ?? "playlet"),
|
||||
});
|
||||
@@ -163,7 +163,7 @@ export default async function Page({
|
||||
data={buildWebPageJSONLD({
|
||||
path: tagListHref(pathKey, page, data.kind),
|
||||
title: `${data.name}标签`,
|
||||
description: `浏览海角社区「${data.name}」标签相关${unit}。`,
|
||||
description: `浏览黄果黑料「${data.name}」标签相关${unit}。`,
|
||||
type: "CollectionPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: "用户协议",
|
||||
description: "查看海角社区用户服务条款及平台使用规则。",
|
||||
description: "查看黄果黑料用户服务条款及平台使用规则。",
|
||||
},
|
||||
canonical: "/terms",
|
||||
});
|
||||
@@ -22,7 +22,7 @@ export default async function Page() {
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/terms",
|
||||
title: "用户协议",
|
||||
description: "查看海角社区用户服务条款及平台使用规则。",
|
||||
description: "查看黄果黑料用户服务条款及平台使用规则。",
|
||||
type: "WebPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -166,8 +166,8 @@ export async function generateMetadata({
|
||||
return resolvePageMetadata({
|
||||
seoInfo: data.seoInfo ?? {
|
||||
title: name,
|
||||
description: (data.desc || "").trim() || `浏览海角社区专题「${name}」。`,
|
||||
keyworld: ["海角社区", name],
|
||||
description: (data.desc || "").trim() || `浏览黄果黑料专题「${name}」。`,
|
||||
keyworld: ["黄果黑料", name],
|
||||
},
|
||||
page: parsed.page,
|
||||
canonical,
|
||||
@@ -229,7 +229,7 @@ export default async function Page({
|
||||
data={buildWebPageJSONLD({
|
||||
path: topicListHref(pathKey, { dataType: resolvedDataType, page, sortType }),
|
||||
title: `${name} · ${topicSortLabel(sortType)}`,
|
||||
description: desc || `浏览海角社区专题「${name}」。`,
|
||||
description: desc || `浏览黄果黑料专题「${name}」。`,
|
||||
type: "CollectionPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PageLoading } from "@/components/common/page-loading";
|
||||
import { sortLabels } from "@/components/short-drama/data";
|
||||
import {
|
||||
buildVideoList,
|
||||
mapVideoListItem,
|
||||
VIDEO_PAGE_SIZE,
|
||||
videoSorts,
|
||||
videoSortType,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
VIDEO_RECOMMEND_DISPLAY_COUNT,
|
||||
type VideoPlayView,
|
||||
} from "@/components/video-play/data";
|
||||
import { VideoRelatedAside } from "@/components/video-play/related-aside";
|
||||
import { VideoPlayPage } from "@/components/video-play/video-play-page";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import {
|
||||
@@ -41,6 +41,7 @@ import { primeRequestForwardHeaders } from "@/lib/api/request-context";
|
||||
import {
|
||||
buildVideoPlayJSONLD,
|
||||
buildWebPageJSONLD,
|
||||
clipText,
|
||||
getHomeBaseSEOCached,
|
||||
mergeSEO,
|
||||
resolveContentMetadata,
|
||||
@@ -98,7 +99,28 @@ async function loadVideoDetailWithRecommend(idOrSlug: string): Promise<VideoPlay
|
||||
const mapped = await loadVideoDetail(idOrSlug);
|
||||
if (!mapped) return null;
|
||||
const related = await loadVideoRecommend(mapped.detail.id);
|
||||
return { ...mapped, related: related.length ? related : mapped.related };
|
||||
if (related.length) return { ...mapped, related };
|
||||
if (mapped.related.length) return mapped;
|
||||
return { ...mapped, related: await loadHotVideoFallback(mapped.detail.id) };
|
||||
}
|
||||
|
||||
/** 推荐与 hotVideoList 都为空时:取视频库热度列表兜底,排除当前视频 */
|
||||
async function loadHotVideoFallback(excludeId: number | string): Promise<ShortDramaItem[]> {
|
||||
try {
|
||||
const data = await getVideoListCached({
|
||||
pageNum: 1,
|
||||
pageSize: VIDEO_RECOMMEND_DISPLAY_COUNT + 4,
|
||||
sortType: videoSortType("hot"),
|
||||
});
|
||||
return (data.list ?? [])
|
||||
.filter((item) => item.type !== 2 && item.id && Number(item.id) !== Number(excludeId))
|
||||
.map(mapVideoListItem)
|
||||
.filter((item): item is ShortDramaItem => Boolean(item))
|
||||
.slice(0, VIDEO_RECOMMEND_DISPLAY_COUNT);
|
||||
} catch (error) {
|
||||
console.error("[video/hot-fallback]", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: Params }): Promise<Metadata> {
|
||||
@@ -119,23 +141,27 @@ export async function generateMetadata({ params }: { params: Params }): Promise<
|
||||
seoInfo: category?.seoInfo,
|
||||
fallbackTitle: categoryName ? `视频·${categoryName}·${sortText}` : `视频·${sortText}`,
|
||||
fallbackDescription: categoryName
|
||||
? `浏览海角社区视频「${categoryName}」分类,按${sortText}发现精彩内容。`
|
||||
: `浏览海角社区视频列表,按${sortText}发现精彩内容。`,
|
||||
fallbackKeywords: ["海角社区", "视频", categoryName, sortText].filter(Boolean),
|
||||
? `浏览黄果黑料视频「${categoryName}」分类,按${sortText}发现精彩内容。`
|
||||
: `浏览黄果黑料视频列表,按${sortText}发现精彩内容。`,
|
||||
fallbackKeywords: ["黄果黑料", "视频", categoryName, sortText].filter(Boolean),
|
||||
page: pageNum,
|
||||
canonical: videoListHref(sortKey, pageNum, categorySlug),
|
||||
});
|
||||
}
|
||||
if (page?.length) return resolvePageMetadata({ fallbackTitle: "海角社区" });
|
||||
if (page?.length) return resolvePageMetadata({ fallbackTitle: "黄果黑料" });
|
||||
// 视频播放页:TDK 用视频详情(video/info)
|
||||
const mapped = await loadVideoDetail(decodeSegment(sort));
|
||||
const detail = mapped?.detail;
|
||||
if (!detail) return resolveContentMetadata({ fallbackTitle: "海角社区" });
|
||||
if (!detail) return resolveContentMetadata({ fallbackTitle: "黄果黑料" });
|
||||
const tagNames = (detail.tags ?? []).map((tag) => tag.name?.trim() || "").filter(Boolean);
|
||||
return resolveContentMetadata({
|
||||
fallbackTitle: detail.title,
|
||||
fallbackDescription: detail.desc || (detail.title ? `在线观看《${detail.title}》` : undefined),
|
||||
fallbackKeywords: ["海角社区", "视频", detail.title || "", ...tagNames].filter(Boolean),
|
||||
// 描述带发布日期与标签,避免与其它站点同文案
|
||||
fallbackDescription: [
|
||||
`视频《${detail.title || "未命名"}》${/^\d{4}-\d{2}-\d{2}/.test(detail.addedTime || "") ? `·${(detail.addedTime || "").slice(0, 10)}发布` : ""}${tagNames.length ? `·${tagNames.slice(0, 3).join("/")}` : ""}`,
|
||||
clipText(detail.desc, 90) || "视频介绍与相关视频推荐。",
|
||||
].join("|"),
|
||||
fallbackKeywords: ["黄果黑料", "视频库", detail.title || "", ...tagNames].filter(Boolean),
|
||||
ogType: "video.episode",
|
||||
canonical: videoHref(String(detail.id), detail.slug),
|
||||
});
|
||||
@@ -231,8 +257,8 @@ async function VideoRouteContent({ params }: { params: Params }) {
|
||||
const listDescription =
|
||||
listSeo.description ||
|
||||
(categoryName
|
||||
? `浏览海角社区视频「${categoryName}」分类,按${sortText}发现精彩内容。`
|
||||
: `浏览海角社区视频列表,按${sortText}发现精彩内容。`);
|
||||
? `浏览黄果黑料视频「${categoryName}」分类,按${sortText}发现精彩内容。`
|
||||
: `浏览黄果黑料视频列表,按${sortText}发现精彩内容。`);
|
||||
return (
|
||||
<>
|
||||
<JsonLd
|
||||
@@ -281,7 +307,6 @@ async function VideoRouteContent({ params }: { params: Params }) {
|
||||
detail={mapped.detail}
|
||||
src={mapped.src}
|
||||
related={mapped.related}
|
||||
relatedAside={<VideoRelatedAside items={mapped.related} />}
|
||||
adIcons={<AdIconsSlot variant="pc" labels={false} />}
|
||||
promo={<AdBannersSlot variant="pc" />}
|
||||
adGrid={<AdIconsSlot variant="h5" />}
|
||||
|
||||
@@ -4,16 +4,13 @@ import { cache } from "react";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { WayHomePage } from "@/components/way-home/way-home-page";
|
||||
import { getHomeBaseCached } from "@/lib/api/home";
|
||||
import type { AnnouncementInfo, ConfigInfo } from "@/lib/api/types";
|
||||
import { buildWebPageJSONLD, resolvePageMetadata } from "@/lib/seo";
|
||||
import type { AnnouncementInfo } from "@/lib/api/types";
|
||||
import { buildWebPageJSONLD, resolvePageMetadata, SITE_NAME } from "@/lib/seo";
|
||||
|
||||
const loadWayHomeBase = cache(async (): Promise<{
|
||||
announcement?: AnnouncementInfo;
|
||||
config?: ConfigInfo;
|
||||
}> => {
|
||||
const loadWayHomeBase = cache(async (): Promise<{ announcement?: AnnouncementInfo }> => {
|
||||
try {
|
||||
const base = await getHomeBaseCached();
|
||||
return { announcement: base.announcement, config: base.config };
|
||||
return { announcement: base.announcement };
|
||||
} catch (error) {
|
||||
console.warn("[way-home] home/base failed", error);
|
||||
return {};
|
||||
@@ -21,8 +18,7 @@ const loadWayHomeBase = cache(async (): Promise<{
|
||||
});
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const { config } = await loadWayHomeBase();
|
||||
const siteName = config?.webSiteName?.trim() || config?.dhSiteName?.trim() || "海角社区";
|
||||
const siteName = SITE_NAME;
|
||||
return resolvePageMetadata({
|
||||
siteName,
|
||||
seoInfo: {
|
||||
@@ -35,8 +31,8 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
const { announcement, config } = await loadWayHomeBase();
|
||||
const siteName = config?.webSiteName?.trim() || config?.dhSiteName?.trim() || "海角社区";
|
||||
const { announcement } = await loadWayHomeBase();
|
||||
const siteName = SITE_NAME;
|
||||
return (
|
||||
<>
|
||||
<JsonLd
|
||||
@@ -52,7 +48,7 @@ export default async function Page() {
|
||||
siteName,
|
||||
})}
|
||||
/>
|
||||
<WayHomePage announcement={announcement} config={config} />
|
||||
<WayHomePage announcement={announcement} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||
return resolvePageMetadata({
|
||||
seoInfo: {
|
||||
title: "福利应用",
|
||||
description: "海角社区福利应用与精选推荐。",
|
||||
keyworld: ["海角社区", "福利应用"],
|
||||
description: "黄果黑料福利应用与精选推荐。",
|
||||
keyworld: ["黄果黑料", "福利应用"],
|
||||
},
|
||||
canonical: "/welfare",
|
||||
});
|
||||
@@ -35,7 +35,7 @@ async function WelfareContent() {
|
||||
data={buildWebPageJSONLD({
|
||||
path: "/welfare",
|
||||
title: "福利应用",
|
||||
description: "海角社区福利应用与精选推荐。",
|
||||
description: "黄果黑料福利应用与精选推荐。",
|
||||
type: "CollectionPage",
|
||||
breadcrumbs: [
|
||||
["首页", "/"],
|
||||
|
||||
Reference in New Issue
Block a user