diff --git a/README.md b/README.md
index 537a345..4731e74 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# NCOverlay
+# NCOverlay
+
+
+
+## 概要
+
動画配信サービスの再生画面にニコニコのコメントを表示する拡張機能です。
今現在はアニメのみ対応。
@@ -11,7 +16,12 @@
コメントは自動で取得・表示されるので何もしなくてOK。
取得したコメント数は拡張機能のアイコンに表示されます。
-ニコニコにログインしているとdアニメストア ニコニコ支店のコメントも取得できます。
+ニコニコにログインしていると、dアニメストア ニコニコ支店のコメントも取得・表示されます。
+
+#### ポップアップ
+- コメントの表示/非表示
+- 不透明度の設定
+- 表示中のコメントの確認
## インストール
@@ -20,4 +30,10 @@
### 手動
1. [Releases](https://github.com/Midra429/NCOverlay/releases) から最新バージョンの `extension.zip` をダウンロード
-2. ダウンロードしたファイルを `chrome://extensions` (デベロッパー モードON) にドラッグ&ドロップ
+2. ダウンロードしたファイルを `chrome://extensions` (デベロッパー モード: ON) にドラッグ&ドロップ
+
+## 不具合報告・機能提案など
+
+- [GitHub](https://github.com/Midra429/NCOverlay/issues)
+
+- [X (@Midra429)](https://x.com/Midra429) に [メンション](https://x.com/intent/tweet?screen_name=Midra429) や [DM](https://x.com/messages/compose?recipient_id=1052566817279864837)
diff --git a/assets/icon.png b/assets/icon.png
new file mode 100644
index 0000000..8859b26
Binary files /dev/null and b/assets/icon.png differ
diff --git a/assets/promo_440x280.png b/assets/promo_440x280.png
new file mode 100644
index 0000000..bf78ffc
Binary files /dev/null and b/assets/promo_440x280.png differ
diff --git a/build.js b/build.js
index 32b6631..b182e92 100644
--- a/build.js
+++ b/build.js
@@ -29,6 +29,7 @@ const build = async () => {
fs.mkdirSync(outputDir, { recursive: true })
fs.copySync(`${inputDir}/manifest.json`, `${outputDir}/manifest.json`)
+ fs.copySync(`${inputDir}/assets`, `${outputDir}/assets`)
fs.copySync(`${inputDir}/styles`, `${outputDir}/styles`)
fs.copySync(`${inputDir}/popup/index.html`, `${outputDir}/popup/index.html`)
diff --git a/src/assets/github-mark.svg b/src/assets/github-mark.svg
new file mode 100644
index 0000000..37fa923
--- /dev/null
+++ b/src/assets/github-mark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/images/icon_128.png b/src/assets/images/icon_128.png
new file mode 100644
index 0000000..3fe1eb7
Binary files /dev/null and b/src/assets/images/icon_128.png differ
diff --git a/src/assets/images/icon_16.png b/src/assets/images/icon_16.png
new file mode 100644
index 0000000..dd77665
Binary files /dev/null and b/src/assets/images/icon_16.png differ
diff --git a/src/assets/images/icon_32.png b/src/assets/images/icon_32.png
new file mode 100644
index 0000000..9585448
Binary files /dev/null and b/src/assets/images/icon_32.png differ
diff --git a/src/assets/images/icon_48.png b/src/assets/images/icon_48.png
new file mode 100644
index 0000000..8f4d873
Binary files /dev/null and b/src/assets/images/icon_48.png differ
diff --git a/src/assets/images/icon_gray_128.png b/src/assets/images/icon_gray_128.png
new file mode 100644
index 0000000..e0f4104
Binary files /dev/null and b/src/assets/images/icon_gray_128.png differ
diff --git a/src/assets/images/icon_gray_16.png b/src/assets/images/icon_gray_16.png
new file mode 100644
index 0000000..9aca54c
Binary files /dev/null and b/src/assets/images/icon_gray_16.png differ
diff --git a/src/assets/images/icon_gray_32.png b/src/assets/images/icon_gray_32.png
new file mode 100644
index 0000000..799d451
Binary files /dev/null and b/src/assets/images/icon_gray_32.png differ
diff --git a/src/assets/images/icon_gray_48.png b/src/assets/images/icon_gray_48.png
new file mode 100644
index 0000000..07feaa9
Binary files /dev/null and b/src/assets/images/icon_gray_48.png differ
diff --git a/src/background/index.ts b/src/background/index.ts
index 19dda16..887f6d1 100644
--- a/src/background/index.ts
+++ b/src/background/index.ts
@@ -3,35 +3,34 @@ import {
isChromeMessageSearch,
isChromeMessageVideo,
isChromeMessageThreads,
- isChromeMessageBadge,
+ isChromeMessageAction,
+ isChromeMessageActionBadge,
+ isChromeMessageActionTitle,
} from '@/types/chrome/message'
+import {
+ ACTION_ICONS_ENABLE,
+ ACTION_ICONS_DISABLE,
+ GITHUB_URL,
+} from '@/constants'
import { NiconicoApi } from './api/niconico'
console.log('[NCOverlay] background.js')
-chrome.action.setBadgeBackgroundColor({
- color: '#2389FF',
-})
+chrome.action.disable()
+chrome.action.setIcon({ path: ACTION_ICONS_DISABLE })
+chrome.action.setBadgeBackgroundColor({ color: '#2389FF' })
+chrome.action.setBadgeTextColor({ color: '#FFF' })
-chrome.action.setBadgeTextColor({
- color: '#ffffff',
-})
+chrome.runtime.onInstalled.addListener((details) => {
+ const { version } = chrome.runtime.getManifest()
+
+ if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
+ chrome.tabs.create({ url: `${GITHUB_URL}/blob/v${version}/README.md` })
+ }
-chrome.runtime.onInstalled.addListener(() => {
- chrome.action.disable()
-
- chrome.declarativeContent.onPageChanged.removeRules(() => {
- chrome.declarativeContent.onPageChanged.addRules([
- {
- conditions: [
- new chrome.declarativeContent.PageStateMatcher({
- css: ['html.NCOverlay'],
- }),
- ],
- actions: [new chrome.declarativeContent.ShowAction()],
- },
- ])
- })
+ if (details.reason === chrome.runtime.OnInstalledReason.UPDATE) {
+ chrome.tabs.create({ url: `${GITHUB_URL}/releases/tag/v${version}` })
+ }
})
chrome.runtime.onMessage.addListener(
@@ -42,6 +41,7 @@ chrome.runtime.onMessage.addListener(
) => {
let promise: Promise | null = null
+ // ニコニコ 検索
if (isChromeMessageSearch(message)) {
const minLength = message.body.duration ? message.body.duration - 30 : -1
const maxLength = message.body.duration ? message.body.duration + 30 : -1
@@ -66,18 +66,43 @@ chrome.runtime.onMessage.addListener(
})
}
+ // ニコニコ 動画情報
if (isChromeMessageVideo(message)) {
promise = NiconicoApi.video(message.body.videoId, message.body.guest)
}
+ // ニコニコ コメント
if (isChromeMessageThreads(message)) {
promise = NiconicoApi.threads(message.body.nvComment)
}
- if (isChromeMessageBadge(message)) {
- promise = chrome.action.setBadgeText({
+ // 拡張機能 アクション 有効/無効
+ if (isChromeMessageAction(message)) {
+ if (message.body) {
+ chrome.action.enable(sender.tab?.id)
+ } else {
+ chrome.action.disable(sender.tab?.id)
+ }
+
+ chrome.action.setIcon({
+ tabId: sender.tab?.id,
+ path: message.body ? ACTION_ICONS_ENABLE : ACTION_ICONS_DISABLE,
+ })
+ }
+
+ // 拡張機能 アクション バッジ
+ if (isChromeMessageActionBadge(message)) {
+ chrome.action.setBadgeText({
+ tabId: sender.tab?.id,
text: message.body.toString(),
+ })
+ }
+
+ // 拡張機能 アクション タイトル (ツールチップ)
+ if (isChromeMessageActionTitle(message)) {
+ chrome.action.setTitle({
tabId: sender.tab?.id,
+ title: message.body ? `${message.body} | NCOverlay` : '',
})
}
diff --git a/src/constants.ts b/src/constants.ts
index 23f93e0..0e96336 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,2 +1,21 @@
+/** アイコン (有効) */
+export const ACTION_ICONS_ENABLE = {
+ '16': 'assets/images/icon_16.png',
+ '32': 'assets/images/icon_32.png',
+ '48': 'assets/images/icon_48.png',
+ '128': 'assets/images/icon_128.png',
+}
+
+/** アイコン (無効) */
+export const ACTION_ICONS_DISABLE = {
+ '16': 'assets/images/icon_gray_16.png',
+ '32': 'assets/images/icon_gray_32.png',
+ '48': 'assets/images/icon_gray_48.png',
+ '128': 'assets/images/icon_gray_128.png',
+}
+
+/** GitHub */
+export const GITHUB_URL = 'https://github.com/Midra429/NCOverlay'
+
/** dアニメストア ニコニコ支店のチャンネルID */
export const DANIME_CHANNEL_ID = 2632720
diff --git a/src/content_script/NCOverlay.ts b/src/content_script/NCOverlay.ts
index 388c2e5..a04efc2 100644
--- a/src/content_script/NCOverlay.ts
+++ b/src/content_script/NCOverlay.ts
@@ -9,7 +9,8 @@ import type { VideoData } from '@/types/niconico/video'
import { isChromeMessageGetFromPage } from '@/types/chrome/message'
import NiconiComments from '@xpadev-net/niconicomments'
import { ChromeStorageApi } from '@/utils/storage'
-import { setBadgeText } from '@/content_script/utils/setBadgeText'
+import { setActionBadge } from '@/content_script/utils/setActionBadge'
+import { setActionTitle } from '@/content_script/utils/setActionTitle'
import { sendToPopup } from '@/content_script/utils/sendToPopup'
export class NCOverlay {
@@ -21,6 +22,7 @@ export class NCOverlay {
#commentsData?: InputFormat
#commentsFormat?: InputFormatType
+ #commentsCount: number = 0
#isEmpty: boolean = true
#isPlaying: boolean = false
@@ -147,14 +149,13 @@ export class NCOverlay {
}
)
- let commentsCount = 0
if (NiconiComments.typeGuard.v1.threads(this.#commentsData)) {
for (const data of this.#commentsData) {
- commentsCount += data.comments.length
+ this.#commentsCount += data.comments.length
}
}
- console.log('[NCOverlay] commentsCount', commentsCount)
+ console.log('[NCOverlay] commentsCount', this.#commentsCount)
this.#update()
@@ -163,17 +164,21 @@ export class NCOverlay {
}
let badgeText = ''
- if (0 < commentsCount) {
- if (1000 <= commentsCount) {
- badgeText = `${Math.round((commentsCount / 1000) * 10) / 10}k`
+ if (0 < this.#commentsCount) {
+ if (1000 <= this.#commentsCount) {
+ badgeText = `${Math.round((this.#commentsCount / 1000) * 10) / 10}k`
} else {
- badgeText = commentsCount.toString()
+ badgeText = this.#commentsCount.toString()
}
}
- setBadgeText(badgeText)
+ setActionBadge(badgeText)
+ setActionTitle(
+ `${this.#commentsCount.toLocaleString()}件のコメントを表示中`
+ )
sendToPopup({
+ commentsCount: this.#commentsCount,
videoData: this.#videoData,
})
}
@@ -202,7 +207,10 @@ export class NCOverlay {
this.#canvas.remove()
- setBadgeText('')
+ setActionBadge('')
+ setActionTitle('')
+
+ sendToPopup({})
}
clear() {
@@ -284,6 +292,7 @@ export class NCOverlay {
sendResponse({
type: message.type,
result: {
+ commentsCount: this.#commentsCount,
videoData: this.#videoData,
},
})
diff --git a/src/content_script/api/niconico/search.ts b/src/content_script/api/niconico/search.ts
index a12a12b..d2e84c2 100644
--- a/src/content_script/api/niconico/search.ts
+++ b/src/content_script/api/niconico/search.ts
@@ -5,9 +5,13 @@ import { optimizeTitleForSearch } from '@/utils/optimizeTitleForSearch'
import { isEqualTitle } from '@/utils/isEqualTitle'
export const search = async (info: {
+ /** 検索タイトル */
title: string
+ /** 検索対象の動画の長さ用 */
duration: number
+ /** 作品のタイトル (あいまい検索用) */
workTitle?: string
+ /** エピソードのサブタイトル (あいまい検索用) */
subtitle?: string
}): Promise => {
const optimizedTitle = optimizeTitleForSearch(info.title)
diff --git a/src/content_script/index.ts b/src/content_script/index.ts
index 004ceab..bf450b1 100644
--- a/src/content_script/index.ts
+++ b/src/content_script/index.ts
@@ -1,3 +1,4 @@
+import { setAction } from './utils/setAction'
import vodPrimeVideo from './vod/primeVideo'
import vodDAnime from './vod/dAnime'
import vodAbema from './vod/abema'
@@ -8,6 +9,8 @@ chrome.runtime.onMessage.addListener(() => false)
const init = () => {
document.documentElement.classList.add('NCOverlay')
+
+ setAction(true)
}
const main = () => {
diff --git a/src/content_script/utils/getThreads.ts b/src/content_script/utils/getThreads.ts
index 7640544..e3430bb 100644
--- a/src/content_script/utils/getThreads.ts
+++ b/src/content_script/utils/getThreads.ts
@@ -10,14 +10,14 @@ export const getThreads = async (
const threadsData: ThreadsData[] = []
for (const data of videoData) {
- const result = await NiconicoApi.threads({
+ const res = await NiconicoApi.threads({
additionals: {},
params: data.comment.nvComment.params,
threadKey: data.comment.nvComment.threadKey,
})
- if (result) {
- threadsData.push(result)
+ if (res) {
+ threadsData.push(res)
}
}
diff --git a/src/content_script/utils/getVideoData.ts b/src/content_script/utils/getVideoData.ts
index 5afa30d..ee867c2 100644
--- a/src/content_script/utils/getVideoData.ts
+++ b/src/content_script/utils/getVideoData.ts
@@ -10,10 +10,10 @@ export const getVideoData = async (
const videoData: VideoData[] = []
for (const id of contentIds) {
- const result = await NiconicoApi.video(id)
+ const res = await NiconicoApi.video(id)
- if (result) {
- videoData.push(result)
+ if (res) {
+ videoData.push(res)
}
}
@@ -21,10 +21,10 @@ export const getVideoData = async (
if (videoData.length === 0) {
for (const id of contentIds) {
- const result = await NiconicoApi.video(id, true)
+ const res = await NiconicoApi.video(id, true)
- if (result) {
- videoData.push(result)
+ if (res) {
+ videoData.push(res)
}
}
diff --git a/src/content_script/utils/sendToPopup.ts b/src/content_script/utils/sendToPopup.ts
index b45eac5..f152240 100644
--- a/src/content_script/utils/sendToPopup.ts
+++ b/src/content_script/utils/sendToPopup.ts
@@ -1,31 +1,8 @@
-import type {
- ChromeMessage,
- ChromeMessageBody,
- ChromeResponse,
-} from '@/types/chrome/message'
-
-const queue: ChromeMessage<'chrome:sendToPopup'>[] = []
-let running: Promise | null = null
-
-const run = (message?: ChromeMessage<'chrome:sendToPopup'>) => {
- if (message) {
- running = chrome.runtime
- .sendMessage(message)
- .finally(() => run(queue.shift()))
- } else {
- running = null
- }
-}
+import type { ChromeMessage, ChromeMessageBody } from '@/types/chrome/message'
export const sendToPopup = (body: ChromeMessageBody['chrome:sendToPopup']) => {
- const message: ChromeMessage<'chrome:sendToPopup'> = {
+ chrome.runtime.sendMessage>({
type: 'chrome:sendToPopup',
body: body,
- }
-
- if (running) {
- queue.push(message)
- } else {
- run(message)
- }
+ })
}
diff --git a/src/content_script/utils/setAction.ts b/src/content_script/utils/setAction.ts
new file mode 100644
index 0000000..7469e45
--- /dev/null
+++ b/src/content_script/utils/setAction.ts
@@ -0,0 +1,8 @@
+import type { ChromeMessage, ChromeMessageBody } from '@/types/chrome/message'
+
+export const setAction = (body: ChromeMessageBody['chrome:action']) => {
+ chrome.runtime.sendMessage>({
+ type: 'chrome:action',
+ body: body,
+ })
+}
diff --git a/src/content_script/utils/setActionBadge.ts b/src/content_script/utils/setActionBadge.ts
new file mode 100644
index 0000000..5753652
--- /dev/null
+++ b/src/content_script/utils/setActionBadge.ts
@@ -0,0 +1,10 @@
+import type { ChromeMessage, ChromeMessageBody } from '@/types/chrome/message'
+
+export const setActionBadge = (
+ body: ChromeMessageBody['chrome:action:badge']
+) => {
+ chrome.runtime.sendMessage>({
+ type: 'chrome:action:badge',
+ body: body,
+ })
+}
diff --git a/src/content_script/utils/setActionTitle.ts b/src/content_script/utils/setActionTitle.ts
new file mode 100644
index 0000000..a35635e
--- /dev/null
+++ b/src/content_script/utils/setActionTitle.ts
@@ -0,0 +1,10 @@
+import type { ChromeMessage, ChromeMessageBody } from '@/types/chrome/message'
+
+export const setActionTitle = (
+ body: ChromeMessageBody['chrome:action:title']
+) => {
+ chrome.runtime.sendMessage>({
+ type: 'chrome:action:title',
+ body: body,
+ })
+}
diff --git a/src/content_script/utils/setBadgeText.ts b/src/content_script/utils/setBadgeText.ts
deleted file mode 100644
index 5bc5565..0000000
--- a/src/content_script/utils/setBadgeText.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import type {
- ChromeMessage,
- ChromeMessageBody,
- ChromeResponse,
-} from '@/types/chrome/message'
-
-const queue: ChromeMessage<'chrome:badge'>[] = []
-let running: Promise | null = null
-
-const run = (message?: ChromeMessage<'chrome:badge'>) => {
- if (message) {
- running = chrome.runtime
- .sendMessage(message)
- .finally(() => run(queue.shift()))
- } else {
- running = null
- }
-}
-
-export const setBadgeText = (body: ChromeMessageBody['chrome:badge']) => {
- const message: ChromeMessage<'chrome:badge'> = {
- type: 'chrome:badge',
- body: body,
- }
-
- if (running) {
- queue.push(message)
- } else {
- run(message)
- }
-}
diff --git a/src/content_script/vod/abema.ts b/src/content_script/vod/abema.ts
index c557d2e..a9b7948 100644
--- a/src/content_script/vod/abema.ts
+++ b/src/content_script/vod/abema.ts
@@ -9,16 +9,30 @@ export default async () => {
let nco: NCOverlay | null = null
const getInfo = () => {
- const title = document.querySelector(
+ const titleElem = document.querySelector(
'.com-video-EpisodeTitle__series-info'
)
- const episode = document.querySelector(
+ const episodeElem = document.querySelector(
'.com-video-EpisodeTitle__episode-title'
)
+ let [title, season] =
+ titleElem?.textContent?.split('|').map((v) => v.trim()) ?? []
+
+ let fullTitle = title
+ if (title && season) {
+ if (season.includes(title)) {
+ fullTitle = season
+ } else {
+ fullTitle = `${title} ${season}`
+ }
+ }
+
return {
- title: title?.textContent?.trim(),
- episode: episode?.textContent?.trim(),
+ title: fullTitle,
+ episode: episodeElem?.textContent?.trim(),
+ workTitle: title,
+ season: season,
}
}
@@ -45,6 +59,8 @@ export default async () => {
const searchResults = await NiconicoApi.search({
title: title,
duration: nco?.video.duration ?? 0,
+ workTitle: info.workTitle,
+ subtitle: info.episode,
})
if (searchResults) {
diff --git a/src/content_script/vod/primeVideo.ts b/src/content_script/vod/primeVideo.ts
index 8b6cc12..52e46db 100644
--- a/src/content_script/vod/primeVideo.ts
+++ b/src/content_script/vod/primeVideo.ts
@@ -25,18 +25,18 @@ export default async () => {
let nco: NCOverlay | null = null
const getInfo = () => {
- const title = document.querySelector(
+ const titleElem = document.querySelector(
'.atvwebplayersdk-title-text'
)
- const subtitle = document.querySelector(
+ const subtitleElem = document.querySelector(
'.atvwebplayersdk-subtitle-text'
)
const se_raw =
- subtitle?.firstChild?.textContent?.trim().replace(/\s+/g, '') ?? ''
+ subtitleElem?.firstChild?.textContent?.trim().replace(/\s+/g, '') ?? ''
return {
- title: title?.textContent?.trim(),
- subtitle: subtitle?.lastChild?.textContent?.trim(),
+ title: titleElem?.textContent?.trim(),
+ subtitle: subtitleElem?.lastChild?.textContent?.trim(),
season: Number(se_raw.match(/(?<=(シーズン|season))\d+/i)?.at(0)),
episode: Number(se_raw.match(/(?<=(エピソード|ep\.))\d+/i)?.at(0)),
}
diff --git a/src/manifest.json b/src/manifest.json
index bed4af2..ea87569 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -2,7 +2,14 @@
"manifest_version": 3,
"name": "NCOverlay",
"description": "動画配信サービスの再生画面にニコニコのコメントを表示する拡張機能",
- "version": "0.0.5",
+ "version": "1.0.0",
+
+ "icons": {
+ "16": "assets/images/icon_16.png",
+ "32": "assets/images/icon_32.png",
+ "48": "assets/images/icon_48.png",
+ "128": "assets/images/icon_128.png"
+ },
"action": {
"default_title": "NCOverlay",
@@ -35,7 +42,7 @@
"host_permissions": ["https://*/*"],
- "permissions": ["declarativeContent", "storage", "scripting"],
+ "permissions": ["storage"],
"minimum_chrome_version": "93"
}
diff --git a/src/popup/index.html b/src/popup/index.html
index 7040ae5..ff71caa 100644
--- a/src/popup/index.html
+++ b/src/popup/index.html
@@ -16,7 +16,18 @@