diff --git a/src/background/service-worker.ts b/src/background/service-worker.ts index 18befc3..5554541 100644 --- a/src/background/service-worker.ts +++ b/src/background/service-worker.ts @@ -67,6 +67,9 @@ async function fetchVideoDetails( : null, publishedAt: item.snippet?.publishedAt?.slice(0, 10) ?? null, category: CATEGORY_MAP[item.snippet?.categoryId] ?? null, + likeCount: item.statistics?.likeCount + ? parseInt(item.statistics.likeCount, 10) + : null, })); browser.tabs.sendMessage(tabId, { diff --git a/src/content/extractor.ts b/src/content/extractor.ts index 8757d77..9f6d81a 100644 --- a/src/content/extractor.ts +++ b/src/content/extractor.ts @@ -180,6 +180,7 @@ function parseVideo(renderer: any): PlaylistVideo | null { category: null, addedBy: null, voteCount: null, + likeCount: null, }; } diff --git a/src/content/index.ts b/src/content/index.ts index 27b7e35..37caa35 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -37,6 +37,7 @@ function mapRawVideo(v: any): PlaylistVideo { category: v.category ?? null, addedBy: v.addedBy ?? null, voteCount: v.voteCount ?? null, + likeCount: v.likeCount ?? null, } satisfies PlaylistVideo; } diff --git a/src/content/ui/i18n.ts b/src/content/ui/i18n.ts index 4eb8389..3f07339 100644 --- a/src/content/ui/i18n.ts +++ b/src/content/ui/i18n.ts @@ -6,6 +6,7 @@ type MessageKey = | "colViews" | "colPublished" | "colCategory" + | "colLikes" | "colAddedBy" | "colVotes" | "filterTitle" @@ -41,6 +42,7 @@ const messages: Record> = { colViews: "再生数", colPublished: "公開日", colCategory: "カテゴリ", + colLikes: "高評価", colAddedBy: "追加者", colVotes: "投票", filterTitle: "タイトル検索...", @@ -75,6 +77,7 @@ const messages: Record> = { colViews: "Views", colPublished: "Published", colCategory: "Category", + colLikes: "Likes", colAddedBy: "Added by", colVotes: "Votes", filterTitle: "Search title...", diff --git a/src/content/ui/styles.ts b/src/content/ui/styles.ts index 7c0a51e..1b1e013 100644 --- a/src/content/ui/styles.ts +++ b/src/content/ui/styles.ts @@ -513,6 +513,7 @@ html[dark] .ytpf-tr:hover { .ytpf-col-views { width: 120px; text-align: right; font-family: "Roboto Mono", monospace; } .ytpf-col-published { width: 110px; font-family: "Roboto Mono", monospace; } .ytpf-col-category { width: 120px; } +.ytpf-col-likes { width: 90px; text-align: right; font-family: "Roboto Mono", monospace; } .ytpf-col-addedby { width: 140px; } .ytpf-col-votes { width: 60px; text-align: center; font-family: "Roboto Mono", monospace; } diff --git a/src/content/ui/table-renderer.ts b/src/content/ui/table-renderer.ts index 77b3e35..5d187f7 100644 --- a/src/content/ui/table-renderer.ts +++ b/src/content/ui/table-renderer.ts @@ -3,7 +3,7 @@ import type { PlaylistData, PlaylistVideo, DetailUpdate } from "../../types/play import type { Message } from "../../shared/messages"; import { t } from "./i18n"; -type SortKey = "index" | "title" | "channel" | "duration" | "views" | "published" | "category" | "addedBy" | "votes"; +type SortKey = "index" | "title" | "channel" | "duration" | "views" | "likes" | "published" | "category" | "addedBy" | "votes"; type SortDir = "asc" | "desc"; interface Column { @@ -22,6 +22,7 @@ function getAllColumns(hasDetails: boolean): Column[] { { label: t("colChannel"), cls: "ytpf-col-channel", key: "channel", defaultWidth: 20 }, { label: t("colDuration"), cls: "ytpf-col-duration", key: "duration", defaultWidth: 8 }, { label: t("colViews"), cls: "ytpf-col-views", key: "views", defaultWidth: 12 }, + { label: t("colLikes"), cls: "ytpf-col-likes", key: "likes", detail: true, defaultWidth: 9 }, { label: t("colPublished"), cls: "ytpf-col-published", key: "published", detail: true, defaultWidth: 10 }, { label: t("colCategory"), cls: "ytpf-col-category", key: "category", detail: true, defaultWidth: 10 }, { label: t("colAddedBy"), cls: "ytpf-col-addedby", key: "addedBy", collab: true, defaultWidth: 14 }, @@ -272,6 +273,8 @@ function compareFn(key: SortKey, dir: SortDir) { return ((a.durationSeconds ?? -1) - (b.durationSeconds ?? -1)) * m; case "views": return (parseViewCount(a.viewCountText) - parseViewCount(b.viewCountText)) * m; + case "likes": + return ((a.likeCount ?? -1) - (b.likeCount ?? -1)) * m; case "published": return (a.publishedAt ?? "").localeCompare(b.publishedAt ?? "") * m; case "category": @@ -325,6 +328,9 @@ function buildCell(video: PlaylistVideo, col: Column, playlistId: string): HTMLT case "views": td.textContent = video.viewCountText ?? "--"; break; + case "likes": + td.textContent = video.likeCount != null ? video.likeCount.toLocaleString() : "--"; + break; case "published": td.textContent = video.publishedAt ?? "--"; break; @@ -970,6 +976,7 @@ export function renderPlaylistTable(data: PlaylistData, columnPrefs: ColumnPrefs v.viewCountText = u.viewCountText ?? v.viewCountText; v.publishedAt = u.publishedAt; v.category = u.category; + v.likeCount = u.likeCount ?? v.likeCount; } } diff --git a/src/types/playlist.ts b/src/types/playlist.ts index 5bd38e6..15b83ae 100644 --- a/src/types/playlist.ts +++ b/src/types/playlist.ts @@ -31,6 +31,8 @@ export interface PlaylistVideo { addedBy: string | null; /** Vote count / approvals (collaborative playlists only) */ voteCount: number | null; + /** Like count from Data API */ + likeCount: number | null; } export interface PlaylistMetadata { @@ -55,6 +57,7 @@ export interface DetailUpdate { viewCountText: string | null; publishedAt: string | null; category: string | null; + likeCount: number | null; } export interface PlaylistData {