summaryrefslogtreecommitdiff
path: root/src/app/anime
diff options
context:
space:
mode:
authorFushihara <1039534+fushihara@users.noreply.github.com>2024-09-23 15:19:25 +0900
committerFushihara <1039534+fushihara@users.noreply.github.com>2024-09-23 15:19:25 +0900
commitba0c8d73e585d7ff249dd90684254bc7fa025544 (patch)
tree3b35be04e7bf11eb046246696d45fb8854f01f14 /src/app/anime
parent41eff0687f69539a514cfa3a28a68f89fc8c54fd (diff)
commit
Diffstat (limited to 'src/app/anime')
-rw-r--r--src/app/anime/[animeId]/page.tsx231
-rw-r--r--src/app/anime/[animeId]/style.css3
-rw-r--r--src/app/anime/[animeId]/style.module.css3
-rw-r--r--src/app/anime/all/page.tsx64
-rw-r--r--src/app/anime/all/style.module.css3
5 files changed, 304 insertions, 0 deletions
diff --git a/src/app/anime/[animeId]/page.tsx b/src/app/anime/[animeId]/page.tsx
new file mode 100644
index 0000000..968ce20
--- /dev/null
+++ b/src/app/anime/[animeId]/page.tsx
@@ -0,0 +1,231 @@
+import "./style.css";
+import style from "./style.module.css";
+import dateformat from "dateformat";
+import { animeLoader, AnimeLoaderData } from "../../../util/animeLoader";
+import Link from "next/link";
+dateformat.i18n.dayNames = [
+ '日', '月', '火', '水', '木', '金', '土',
+ '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'
+];
+type PageType = {
+ searchParams: Record<string, string>,
+ params: { animeId: string, }
+}
+export async function generateMetadata(context: PageType) {
+ const loadedData = await animeLoader.loadData().then(d => {
+ const r = d.find(a => a.animeId == Number(context.params.animeId))!;
+ return r;
+ });
+ return {
+ title: `アキバ総研アーカイブ:アニメ : ${loadedData.title}`,
+ }
+}
+export default async function Page(context: PageType) {
+ const loadedData = await animeLoader.loadData().then(d => {
+ const r = d.find(a => a.animeId == Number(context.params.animeId))!;
+ return r;
+ });;
+ return (
+ <div className="p-8 pb-20 gap-16 sm:p-20">
+ <h1>アニメ詳細</h1>
+ <div>
+ <span>タイトル:</span>
+ <span>{loadedData.title}</span>
+ </div>
+ <div>
+ <span>主カテゴリ:</span>
+ <span>{loadedData.primaryCategory}</span>
+ </div>
+ <div>
+ <span>開始時期:</span>
+ <span>{loadedData.startSeason}</span>
+ </div>
+ <div>
+ <span>アキバ総研のアニメ個別URL:</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${loadedData.animeId}/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${loadedData.animeId}/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${loadedData.animeId}`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ {titleReviewScore(loadedData.titleReviewScore)}
+ {titleReviewList(loadedData.animeId, loadedData.titleReviewList)}
+ {titleHitokotoList(loadedData.titleHitokotoList)}
+ {episodeList(loadedData.episodeList)}
+ {episodeHitokotoList(loadedData.episodeHitokotoList)}
+ </div>
+ );
+}
+/** 作品自体のレビューの点数 */
+function titleReviewScore(titleReviewScore: AnimeLoaderData["titleReviewScore"]) {
+ return (
+ <>
+ <h1>作品レビュー</h1>
+ <div>
+ <span>ストーリー:</span>
+ <span>{titleReviewScore.story ?? "なし"}</span>
+ </div>
+ <div>
+ <span>作画:</span>
+ <span>{titleReviewScore.sakuga ?? "なし"}</span>
+ </div>
+ <div>
+ <span>キャラクター:</span>
+ <span>{titleReviewScore.character ?? "なし"}</span>
+ </div>
+ <div>
+ <span>音楽:</span>
+ <span>{titleReviewScore.music ?? "なし"}</span>
+ </div>
+ <div>
+ <span>オリジナリティ:</span>
+ <span>{titleReviewScore.originality ?? "なし"}</span>
+ </div>
+ <div>
+ <span>演出:</span>
+ <span>{titleReviewScore.storyboard ?? "なし"}</span>
+ </div>
+ <div>
+ <span>声優:</span>
+ <span>{titleReviewScore.voice ?? "なし"}</span>
+ </div>
+ <div>
+ <span>歌:</span>
+ <span>{titleReviewScore.song ?? "なし"}</span>
+ </div>
+ <div>
+ <span>満足度:</span>
+ <span>{titleReviewScore.manzokudo ?? "なし"}</span>
+ </div>
+ </>
+ );
+}
+function titleReviewList(animeId: number, list: AnimeLoaderData["titleReviewList"]) {
+ // レビューの一覧
+ const PPV = 20;
+ const reviewListLinks: JSX.Element[] = [];
+ for (let i = 0; i < Math.ceil(list.length / PPV); i++) {
+ const itemFrom = (i * PPV) + 1;
+ const itemTo = Math.min(itemFrom + PPV - 1, list.length);
+ if (i == 0) {
+ // 1ページ目
+ reviewListLinks.push(<h2 key={`review-header`}>レビュー自体の一覧</h2>)
+ reviewListLinks.push(
+ <div key={`review-list-${i}`}>
+ <span>新着 {itemFrom}~{itemTo}件目</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ );
+ } else {
+ // 2ページ目移行
+ const page = i + 1;
+ reviewListLinks.push(
+ <div key={`review-list-${i}`}>
+ <span>新着 {itemFrom}~{itemTo}件目</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/?page=${page}`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/?page=${page}`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review-p${page}`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ );
+ }
+ }
+ const reviewList: JSX.Element[] = [];
+ if (0 < list.length) {
+ reviewList.push(<h2 key="head">個別のレビュー一覧</h2>)
+ reviewList.push(<span key="notice">レビューの一覧だとネタバレのレビューが表示されないので、個別のレビューのリンクを作成します</span>)
+ for (const v of list) {
+ const timestampStr = dateformat(new Date(v.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss");
+ const index = list.indexOf(v);
+ reviewList.push(
+ <div key={`review-list-${v.reviewId}`}>
+ <span className="inline-flex gap-4">
+ <span>{index + 1}/{list.length}</span>
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/${v.reviewId}/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/${v.reviewId}/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review-${v.reviewId}`} className={`${style["a"]}`}>IAをiframe</Link>
+ <span>投稿日時:{timestampStr}</span>
+ <span>スコア:{v.score}</span>
+ <span>ネタバレ:{v.isSpoiler ? "はい" : "いいえ"}</span>
+ <span>投稿者:{v.userName}</span>
+ </span>
+ </div>
+ );
+ }
+ }
+ return (<>
+ <h1>作品のレビュー一覧( {list.length} 件 )</h1>
+ {reviewListLinks}
+ {reviewList}
+ </>);
+}
+function titleHitokotoList(list: AnimeLoaderData["titleHitokotoList"]) {
+ const hitokotoList: JSX.Element[] = [];
+ if (0 < list.length) {
+ hitokotoList.push(<div key={`notice`}>ヒトコトは個別のURLが無いので必要最低限のみテキストを転載します</div>)
+ for (const h of list) {
+ const timestampStr = dateformat(new Date(h.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss")
+ const index = list.indexOf(h);
+ hitokotoList.push(
+ <div key={`h-${h.hitokotoId}`}>
+ {index + 1}/{list.length} {timestampStr} {h.reviewHtml}
+ </div>
+ )
+ }
+ }
+ return (<>
+ <h1>作品のヒトコト一覧( {list.length} 件 )</h1>
+ {hitokotoList}
+ </>);
+}
+function episodeList(list: AnimeLoaderData["episodeList"]) {
+ const episodeElementList: JSX.Element[] = [];
+ if (0 < list.length) {
+ for (const e of list) {
+ const index = list.indexOf(e);
+ episodeElementList.push(
+ <div key={`e-${e.episodeId}`}>
+ {index + 1}/{list.length} 「{e.subTitle}」 ヒトコト数:{e.reviewCount} スコア:{e.score ?? "なし"}
+ </div>
+ )
+ }
+ }
+ return (<>
+ <h1>作品のエピソード ( {list.length} 件 )</h1>
+ {episodeElementList}
+ </>);
+}
+function episodeHitokotoList(list: AnimeLoaderData["episodeHitokotoList"]) {
+ const hitokotoList: JSX.Element[] = [];
+ const totalHitokotoCount = list.map(h => h.hitokotoList.length).reduce((a, b) => a + b, 0)
+ if (0 < list.length) {
+ hitokotoList.push(<div key={`notice`}>ヒトコトは個別のURLが無いので必要最低限のみテキストを転載します</div>)
+ let index = 0;
+ for (const e of list) {
+ index += 1;
+ for (const h of e.hitokotoList) {
+ const timestampStr = dateformat(new Date(h.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss")
+ hitokotoList.push(
+ <div key={`h-${e.episodeId}-${h.hitokotoId}`}>
+ {index}/{totalHitokotoCount} 第{e.episodeId}話のヒトコト. {timestampStr} {h.reviewHtml}
+ </div>
+ )
+ }
+ }
+ }
+ return (<>
+ <h1>作品のエピソードごとのヒトコト ( {totalHitokotoCount} 件 )</h1>
+ {hitokotoList}
+ </>);
+}
+export async function generateStaticParams() {
+ const loadedData = await animeLoader.loadData();
+ return loadedData.map(c => {
+ return { animeId: String(c.animeId) };
+ }) satisfies PageType["params"][];
+} \ No newline at end of file
diff --git a/src/app/anime/[animeId]/style.css b/src/app/anime/[animeId]/style.css
new file mode 100644
index 0000000..b7d67c0
--- /dev/null
+++ b/src/app/anime/[animeId]/style.css
@@ -0,0 +1,3 @@
+h1,h2 {
+ all: revert;
+}
diff --git a/src/app/anime/[animeId]/style.module.css b/src/app/anime/[animeId]/style.module.css
new file mode 100644
index 0000000..fd9a86f
--- /dev/null
+++ b/src/app/anime/[animeId]/style.module.css
@@ -0,0 +1,3 @@
+.a {
+ all: revert;
+} \ No newline at end of file
diff --git a/src/app/anime/all/page.tsx b/src/app/anime/all/page.tsx
new file mode 100644
index 0000000..9c4f4e6
--- /dev/null
+++ b/src/app/anime/all/page.tsx
@@ -0,0 +1,64 @@
+import { animeLoader } from "../../../util/animeLoader";
+import style from "./style.module.css";
+import Link from "next/link";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {}
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:アニメ一覧`,
+ }
+}
+export default async function Page(context: PageType) {
+ const loadedData = await animeLoader.loadData();
+ return (
+ <div className="p-1 gap-16">
+ <div className="text-right">全:{loadedData.length}件</div>
+ <table className="w-full">
+ <thead className="bg-white border-b sticky top-0 text-md font-medium text-gray-900">
+ <tr>
+ <th scope="col" className="px-6 py-4 text-left">
+ No
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ カテゴリ
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ 開始時期
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ 満足度
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ タイトル
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ レビュー件数
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ ヒトコト件数
+ </th>
+ </tr>
+ </thead>
+ <tbody className="">
+ {loadedData.map(d => {
+ return (
+ <tr className={`bg-white border-b transition duration-300 ease-in-out hover:bg-gray-100 text-sm text-gray-900 font-light`}>
+ <td className={`px-6 py-1 whitespace-nowrap ${style["akiba-souken-archive-anchor"]}`}>
+ <Link href={`/anime/${d.animeId}`}>{d.animeId}</Link>
+ </td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.primaryCategory}</td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.startSeason}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleReviewScore.manzokudo == null ? "text-gray-300" : ""}`}>{d.titleReviewScore.manzokudo ?? "なし"}</td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.title}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleReviewList.length == 0 ? "text-gray-300" : ""}`}>{d.titleReviewList.length}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleHitokotoList.length == 0 ? "text-gray-300" : ""}`}>{d.titleHitokotoList.length}</td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ );
+}
diff --git a/src/app/anime/all/style.module.css b/src/app/anime/all/style.module.css
new file mode 100644
index 0000000..20abae7
--- /dev/null
+++ b/src/app/anime/all/style.module.css
@@ -0,0 +1,3 @@
+.akiba-souken-archive-anchor {
+ @apply underline text-blue-600 hover:text-blue-800 visited:text-purple-600
+}