From a7b82175aaf8fbc7c4171b6664032c887b1ebee6 Mon Sep 17 00:00:00 2001 From: sopi <53455523+sopisoft@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:14:01 +0900 Subject: [PATCH] Re:turn (#90) --- src/@types/types.d.ts | 136 +++++++++++++++-------------- src/background.ts | 63 ++----------- src/config.ts | 4 - src/content_scripts/export.ts | 91 ------------------- src/content_scripts/index.ts | 10 +-- src/how_to_use/how_to_use.md | 41 +++------ src/options/components/options.tsx | 10 --- src/popup/components/inputs.tsx | 7 -- 8 files changed, 90 insertions(+), 272 deletions(-) delete mode 100644 src/content_scripts/export.ts diff --git a/src/@types/types.d.ts b/src/@types/types.d.ts index f1fb0fb..3d17190 100644 --- a/src/@types/types.d.ts +++ b/src/@types/types.d.ts @@ -38,56 +38,59 @@ type SearchErrorResponse = { type SearchResult = { meta: { status: number; - totalCount: number; - id: string; + code: string; // "HTTP_200" }; data: { - ads: null; - category: null; - channel: { - id: string; - name: string; - isOfficialAnime: boolean; - isDisplayAdBanner: boolean; - thumbnail: { - url: string; - smallUrl: string; - }; - viewer: { - follow: { - isFollowed: boolean; - isBookmarked: boolean; - token: string; - tokenTimestamp: number; + response: { + channel: { + id: string; + name: string; + isOfficialAnime: boolean; + isDisplayAdBanner: boolean; + thumbnail: { + url: string; + smallUrl: string; }; + viewer: { + follow: { + isFollowed: boolean; + isBookmarked: boolean; + token: string; + tokenTimestamp: number; + }; + }; + } | null; + client: { + nicosid: string; + watchId: string; + watchTrackId: string; }; - } | null; - client: { - nicosid: string; - watchId: string; - watchTrackId: string; - }; - comment: { - server: { - url: string; - }; - keys: { - userKey: string; - }; - layers: [ - { + comment: { + server: { + url: string; + }; + keys: { + userKey: string; + }; + layers: { index: number; isTranslucent: boolean; threadIds: thread[]; - }[], - ]; - threads: [ - { + }[]; + threads: { id: thread["id"]; fork: thread["fork"]; forkLabel: thread["forkLabel"]; videoId: string; // contentId isOwnerThread: boolean; + isActive: boolean; + isDefaultPostTarget: boolean; + isEasyCommentPostTarget: boolean; + isLeafRequired: boolean; + isThreadkeyRequired: boolean; + threadkey: string; + is184Forced: boolean; + hasNicoscript: boolean; label: | "owner" | "default" @@ -95,40 +98,39 @@ type SearchResult = { | "easy" | "extra-community" | "extra-easy"; + postKeyStatus: number; + server: string; + }[]; + nvComment: { + threadKey: string; server: string; - }[], - ]; - nvComment: { - threadKey: string; - server: "https://nv-comment.nicovideo.jp"; - params: { - targets: [ - { + params: { + targets: { id: string; // thread["id"] to stringify fork: thread["forkLabel"]; - }[], - ]; - language: "ja-jp"; + }[]; + language: "ja-jp"; + }; }; }; - }; - video: { - id: string; // contentId - title: string; - description: string; - count: { - view: number; - comment: number; - mylist: number; - like: number; - }; - duration: number; - thumbnail: { - url: string; - middleUrl: string; - largeUrl: string; - player: string; - ogp: string; + video: { + id: string; // contentId + title: string; + description: string; // HTML + count: { + view: number; + comment: number; + mylist: number; + like: number; + }; + duration: number; + thumbnail: { + url: string; + middleUrl: string; + largeUrl: string; + player: string; + ogp: string; + }; }; }; }; diff --git a/src/background.ts b/src/background.ts index b4e3656..3763558 100644 --- a/src/background.ts +++ b/src/background.ts @@ -15,76 +15,31 @@ along with d-comments. If not, see . */ -import { getConfig } from "@/config"; import browser from "webextension-polyfill"; import { openHowToUseIfNotRead } from "./how_to_use/how_to_use"; -/** - * 任意の範囲のランダムな整数を返す - * @param min 最小値 - * @param max 最大値 - * @returns min 以上 max 以下のランダムな整数 - */ -const getRandomInt = (min: number, max: number) => { - const minNum = Math.ceil(min); - const maxNum = Math.floor(max); - return Math.floor(Math.random() * (maxNum - minNum) + minNum); -}; - -function getActionTrackId() { - const f = Math.random().toString(36).slice(-10); - const b = getRandomInt(10 ** 12, 10 ** 13); - return `${f}_${b}`; -} - /** * 動画情報を取得する * @param videoId ニコニコ動画の動画ID - * @param sendResponse (response) => void */ - -const getVideoData = async (videoId: string) => { - const config: boolean = await getConfig("allow_login_to_nicovideo"); - const url = `https://www.nicovideo.jp/api/watch/${ - config ? "v3" : "v3_guest" - }/${videoId}`; - const params = { - _frontendId: "6", - _frontendVersion: "0", - actionTrackId: getActionTrackId(), - }; - const fetch_options: RequestInit = { - credentials: config ? "include" : "omit", - headers: { - "x-frontend-id": "6", - "x-frontend-version": "0", - }, - }; - if (config) - browser.cookies - .get({ url: "https://www.nicovideo.jp/", name: "user_session" }) - .then((cookie) => { - if (!cookie) return; - Object.assign(fetch_options.headers as HeadersInit, { - Cookie: `user_session=${cookie.value}`, - }); - }); - - return await fetch(`${url}?${new URLSearchParams(params)}`, fetch_options) +async function getVideoData(videoId: string): Promise { + const url = `https://www.nicovideo.jp/watch/${videoId}?responseType=json`; + const res = await fetch(url) .then(async (res) => { - const json = (await res.json()) as SearchResponse; - return json; + const json = await res.json(); + return json as SearchResponse; }) .catch((e) => { return e as Error; }); -}; + return res; +} /** * コメントスレッドの情報とコメントを取得 */ const getThreadComments = async ( - nvComment: SearchResult["data"]["comment"]["nvComment"] + nvComment: SearchResult["data"]["response"]["comment"]["nvComment"] ) => { const { server, threadKey, params } = nvComment; const serverUrl = `${server}/v1/threads`; @@ -158,9 +113,7 @@ const search = async ( /** * * @param type "user" | "channel" - * @param videoId 動画ID * @param ownerId ユーザーID または チャンネルID - * @returns Owner */ const get_user_info = async ( type: "user" | "channel", diff --git a/src/config.ts b/src/config.ts index be25f89..444dac9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -84,10 +84,6 @@ function get_default_configs() { value: true as boolean, type: "checkbox", }, - allow_login_to_nicovideo: { - value: false as boolean, - type: "switch", - }, comment_ng_words: { value: [] as { key: string; value: string; enabled: boolean }[], type: "text_list", diff --git a/src/content_scripts/export.ts b/src/content_scripts/export.ts deleted file mode 100644 index ee1be11..0000000 --- a/src/content_scripts/export.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of d-comments. - - d-comments is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - d-comments is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with d-comments. If not, see . -*/ - -import get_threads from "./api/thread_data"; -import get_video_data from "./api/video_data"; -import { push_message, set_messages } from "./state"; - -const exportJson = async (videoId: VideoId) => { - set_messages([]); - push_message({ - title: "コメントを取得しています", - description: "動画情報を取得しています", - }); - - const video_data = await get_video_data(videoId); - if (video_data instanceof Error) { - push_message(video_data); - return; - } - console.log("video_data", video_data); - - if (!(video_data as SearchResult).data?.comment) { - const res = video_data as SearchErrorResponse; - const error_code = res.meta.errorCode; - const error_reason = res.data.reasonCode; - const message = { - title: "動画情報の取得に失敗しました", - description: error_reason ?? error_code, - }; - push_message(message); - return; - } - - const { data } = video_data as SearchResult; - const threads = await get_threads(data.comment.nvComment); - if (threads instanceof Error) { - push_message(threads); - return; - } - console.log("threads", threads); - - push_message({ - title: "コメントの取得に成功しました", - description: "ファイルに保存しています", - }); - - const fileName = `${data.video.title}.json`; - const _data: comments_json = { - version: 1, - movieData: video_data as SearchResult, - threadData: threads, - }; - - return await saveFile(fileName, JSON.stringify(_data)); -}; - -/** - * ファイルを保存する - * @param fileName ファイル名 - * @param data 内容 - */ -const saveFile = async (fileName: string, data: string) => { - try { - const blob = new Blob([data], { type: "application/json" }); - const link = document.createElement("a"); - document.body.appendChild(link); - link.href = window.URL.createObjectURL(blob); - link.download = fileName; - link.click(); - document.body.removeChild(link); - } catch (e) { - return e as Error; - } - return true as const; -}; - -export default exportJson; diff --git a/src/content_scripts/index.ts b/src/content_scripts/index.ts index 7685f67..fe04add 100644 --- a/src/content_scripts/index.ts +++ b/src/content_scripts/index.ts @@ -30,7 +30,6 @@ import initRenderer from "./components/canvas"; import overlay from "./components/overlay"; import wrap from "./components/wrapper"; import { setWorkInfo } from "./danime/watch"; -import exportJson from "./export"; import { partId as getPartId, threads as getThreads, @@ -158,7 +157,7 @@ async function render_comments(videoId: VideoId) { } console.log("video_data", video_data); - if (!(video_data as SearchResult).data?.comment) { + if (!(video_data as SearchResult).data?.response.comment) { const res = video_data as SearchErrorResponse; const error_code = res.meta.errorCode; const error_reason = res.data.reasonCode; @@ -171,7 +170,7 @@ async function render_comments(videoId: VideoId) { } const { data } = video_data as SearchResult; - const threads = await get_threads(data.comment.nvComment); + const threads = await get_threads(data.response.comment.nvComment); if (threads instanceof Error) { push_message(threads); return; @@ -218,10 +217,5 @@ browser.runtime.onMessage.addListener(async (message) => { await render_comments(videoId); break; } - case "export_comments_json": { - console.log("export_comments_json", msg.data.videoId); - await exportJson(msg.data.videoId); - break; - } } }); diff --git a/src/how_to_use/how_to_use.md b/src/how_to_use/how_to_use.md index 3fc0ed1..1850b99 100644 --- a/src/how_to_use/how_to_use.md +++ b/src/how_to_use/how_to_use.md @@ -10,15 +10,6 @@ d-anime comments viewer は、dアニメストアの映像作品視聴ページ 本ソフトウェアの使用により、「つかいかた」の内容に同意したものとみなします。 -## 困ったときは - -### チャンネルのコメントが表示されない - -チャンネルのコメントが表示されない場合、以下の手順をお試しください。 - -1. ニコニコ動画にログインする。 -2. 設定「ニコニコ動画へのログインを許可する」を有効にする。 - ## 視聴を開始する dアニメストアの任意のアニメ作品ページにアクセスして作品の視聴を開始します。 @@ -48,11 +39,13 @@ dアニメストアの任意のアニメ作品ページにアクセスして作 動画検索に失敗する場合は、動画検索を中止し、ご連絡ください。 4. 動画リストからコメントを表示したい動画を選択し、クリックします。 + 5. 動画 ID 欄に選択したニコニコ動画の動画 ID が入力されます。動画 ID は手動でも入力できます。 ### コメントの表示 1. ポップアップページの動画 ID 欄に動画 ID が入力されていることを確認します。 + 2. 動画 ID 欄すぐ横の「表示」ボタンをクリックするとコメントが表示されます。 ![コメントが表示された様子](/img/comments.jpg) @@ -64,32 +57,17 @@ dアニメストアの任意のアニメ作品ページにアクセスして作 作品の視聴を開始するときに、自動で動画検索を開始するか選択できます。 -### コメントをチャンネルからのみ取得 - -設定「コメントをチャンネルからのみ取得」を有効にすると、自動でコメントを取得するとき、チャンネルからのコメントのみを取得します。 - -#### ニコニコ動画へのログイン +設定「連続再生時に自動で次の動画のコメントを読み込む」とは独立して動作します。 -設定「ニコニコ動画へのログインを許可する」を有効にすると、この拡張機能は、コメントの取得に際してニコニコ動画の動画の情報を取得するとき、[ニコニコ動画](https://www.nicovideo.jp)の[クッキー](https://www.soumu.go.jp/main_sosiki/joho_tsusin/security_previous/yougo/eiji.htm#cookie)の値を取得します。 +### 連続再生時に自動で次の動画のコメントを読み込む -設定「ニコニコ動画へのログインを許可する」はデフォルトで無効です。 +連続再生時に、自動で次の動画のコメントを読み込むか選択できます。 -取得するクッキーの情報 +設定「自動で動画検索/再生を開始する」とは独立して動作します。 -| 名前 | Domain | HttpOnly | Secure | -| ------------ | ------------- | -------- | ------ | -| user_session | .nicovideo.jp | true | true | - -取得するクッキーにこの拡張機能の開発者・その他の第三者が個人を特定できる情報は含まれていません。 - -設定「ニコニコ動画へのログインを許可する」を有効であり、かつニコニコ動画の動画の情報を取得するとき、この拡張機能はクッキーの値を '' 以下に送信します。 - -この拡張機能は、ドメインが `nicovideo.jp` でないアドレス先に取得したクッキーの値を送信することはありません。 -また、取得したクッキーの情報は、この拡張機能に保存されません。 - -また、設定を有効にするときは[ニコニコ規約](https://account.nicovideo.jp/rules/account)もご確認ください。 +### コメントをチャンネルからのみ取得 -この拡張機能によって、ニコニコ動画のアカウントやそれに関連するものに何らかの損害が生じても、この拡張機能の開発者は一切責任を負わないものとします。 +設定「コメントをチャンネルからのみ取得」を有効にすると、自動でコメントを取得するとき、チャンネルからのコメントのみを取得します。 ### みため / スタイル @@ -124,12 +102,15 @@ dアニメストアの任意のアニメ作品ページにアクセスして作 #### 作品ページに「新しいタブで再生」「現在のタブで再生」ボタンを追加する 作品ページに「新しいタブで再生」もしくは「現在のタブで再生」ボタンを追加します。 + 設定「新しいウィンドウを作らない設定が有効のとき、同じタブで再生する」で「新しいタブで再生」と「現在のタブで再生」を切り替えることができます。 #### 作品の視聴を開始するときに新しいウィンドウが作成されるのを防ぐ 作品ページのそれぞれのパートのサムネイル、もしくはタイトルがクリックされたとき、「 詳しく見る」を開かずに再生を開始します。 + 再生を開始するときに新しいウィンドウが作成されるのを防ぐことができます。 + 設定「新しいウィンドウを作らない設定が有効のとき、同じタブで再生する」で新規タブで再生するか、既存のタブで再生するかを切り替えることができます。 ## 最後に diff --git a/src/options/components/options.tsx b/src/options/components/options.tsx index cfa36da..31e531f 100644 --- a/src/options/components/options.tsx +++ b/src/options/components/options.tsx @@ -91,16 +91,6 @@ function Options() { text="連続再生時に自動で次の動画のコメントを読み込む" /> - - - - diff --git a/src/popup/components/inputs.tsx b/src/popup/components/inputs.tsx index 1d0a361..d8fa5b5 100644 --- a/src/popup/components/inputs.tsx +++ b/src/popup/components/inputs.tsx @@ -27,7 +27,6 @@ import { useToast } from "@/components/ui/use-toast"; import api from "@/lib/api"; import { ExternalLink, Play } from "lucide-react"; import { useContext } from "react"; -import export_comments_json from "../../content_scripts/export"; import { VideoIdContext } from "../popup"; import { ErrorMessage, isVideoId } from "../utils"; @@ -73,12 +72,6 @@ function Inputs() { }); } - async function on_save_json_button_clicked() { - check_video_id(videoId).then((id) => { - if (id) export_comments_json(id); - }); - } - return ( <>