// ==UserScript== // @name ニコニコ静画NGスクリプト // @match *://seiga.nicovideo.jp/search/* // @match *://seiga.nicovideo.jp/tag/* // @match *://seiga.nicovideo.jp/illust/* // @match *://seiga.nicovideo.jp/my/personalize* // @description ニコニコ静画でNGユーザの投稿を非表示にする // @license MIT License Copyright (c) 2020 Tennosuke Tokoro // @license このスクリプトはkengo312氏のNico Nico Ranking NG (MIT License)を流用しています。 // @version 20200614 // @namespace https://greasyfork.org/users/585074 // @downloadURL none // ==/UserScript== var i; var parent_element, mode; if (location.href.match(/seiga.nicovideo.jp\/search/)){ parent_element = document.getElementById("usearch_form"); mode = "タグ検索・ランキング"; }else{ parent_element = document.getElementsByClassName("sg_pankuzu")[0]; if(location.href.match(/seiga.nicovideo.jp\/search/)) mode = "キーワード検索"; if(location.href.match(/seiga.nicovideo.jp\/my\/personalize/)) mode = "定点観測"; } const ng_config_button = document.createElement("input"); ng_config_button.id = "ng_config_button"; ng_config_button.type = "button"; ng_config_button.value = "静画NG設定"; ng_config_button.style.cssText = "background-color:red; padding: 1px; font-size: 12px; margin-left:1em;"; const view_config = document.createElement("select"); view_config.style.cssText = "font-size: 12px; vertical-align:middle; margin-left:1em;"; view_config.innerHTML = ``; const enable_keyword_search = document.createElement("select"); enable_keyword_search.style.cssText = "font-size: 12px; vertical-align:middle; margin-left:1em;"; enable_keyword_search.innerHTML = ``; parent_element.appendChild(ng_config_button); parent_element.appendChild(view_config); parent_element.appendChild(enable_keyword_search); var ng_list_db = []; var id_name_list = []; var id_query_flag = 0; var view_config_db = {}; loadNgData(); loadViewConfig(); //NG設定画面 const ng_dialog = document.createElement("div"); ng_dialog.id = "ng_dialog"; ng_dialog.style.cssText = "margin-bottom:1em; position:fixed; width:40%; height:40%; line-height:1.5em; display:none; flex-direction:column; justify-content:center; background-color:gray; z-index:1"; const ng_dialog_top = document.createElement("div"); ng_dialog_top.style.cssText = "display:flex; flex-direction:row;"; const ng_types = document.createElement("select"); ng_types.style.cssText = "font-size:14px; margin-top:1em; margin-bottom:1em; margin-left:1em;"; ng_types.innerHTML = ` `; const ng_durations = document.createElement("select"); ng_durations.style.cssText = "font-size:14px; margin-top:1em; margin-bottom:1em; margin-left:4em;"; ng_durations.innerHTML = ` `; const ng_close_button = document.createElement("input"); ng_close_button.type = "button"; ng_close_button.style.cssText = "font-size:18px; margin-top:0.5em; margin-bottom:0.5em; margin-left:4em; padding-left:1em; padding-right:1em;"; ng_close_button.value = "閉じる"; ng_close_button.onclick = function () { ng_dialog.style.display = "none"; } const ng_dialog_middle = document.createElement("div"); ng_dialog_middle.style.cssText = "width:100%; height:100%; margin-bottom:1em; display:flex; flex:auto; flex-direction:row;"; const ng_list = document.createElement("select"); ng_list.style.cssText = "width:90%; margin-left:1em; margin-right:1em;"; ng_list.multiple = "multiple"; ng_list.size = 14; updateNgList(); const ng_operation_buttons = document.createElement("p"); ng_operation_buttons.style.cssText = "display:flex; flex:auto; flex-direction:column;"; const ng_add_button = document.createElement("input"); ng_add_button.style.cssText = "font-size:18px; width:4em; margin-right:1em;"; ng_add_button.type = "button"; ng_add_button.value = "追加"; const ng_delete_button = document.createElement("input"); ng_delete_button.style.cssText = "font-size:18px; width:4em; margin-right:1em; margin-top:1em;"; ng_delete_button.type = "button"; ng_delete_button.value = "削除"; ng_dialog_top.appendChild(ng_types); ng_dialog_top.appendChild(ng_durations); ng_dialog_top.appendChild(ng_close_button); ng_dialog.appendChild(ng_dialog_top); ng_dialog_middle.appendChild(ng_list); ng_operation_buttons.appendChild(ng_add_button); ng_operation_buttons.appendChild(ng_delete_button); ng_dialog_middle.appendChild(ng_operation_buttons); ng_dialog.appendChild(ng_dialog_middle); parent_element.appendChild(ng_dialog); ng_config_button.onclick = function(){ ng_dialog.style.display = "flex"; }; ng_add_button.onclick = function(){ var ng_user = window.prompt(ng_types.value + "を入力してください", ""); var ng_duration = ng_durations.value; var end_date = new Date(); if (ng_duration == "設定期間:永久"){ end_date.setFullYear(4000); }else if (ng_duration == "設定期間:1週間"){ end_date.setDate(end_date.getDate() + 7); }else if (ng_duration == "設定期間:1ヶ月間"){ end_date.setDate(end_date.getDate() + 30); }else if (ng_duration == "設定期間:3ヶ月間"){ end_date.setMonth(end_date.getMonth() + 3); }else if (ng_duration == "設定期間:6ヶ月間"){ end_date.setMonth(end_date.getMonth() + 6); }else if (ng_duration == "設定期間:1年間"){ end_date.setFullYear(end_date.getFullYear() + 1); }else if (ng_duration == "設定期間:3年間"){ end_date.setFullYear(end_date.getFullYear() + 3); } var opt = document.createElement("option"); opt.text = formatNgInfo(ng_types.value, dateString(end_date), ng_user); var ng_info = { user : ng_user, type : ng_types.value, date : dateString(end_date) }; ng_list_db.push(ng_info); if (ng_types.value == "NGユーザーID"){ var ng_user_name = getUserName(ng_user); var id_name_obj = { id : ng_user, name : ng_user_name }; id_name_list.push(id_name_obj); } updateNgList(); saveNgData(); }; ng_delete_button.onclick = function(){ var ng_info = parseNgInfo(ng_list.value); ng_list_db.splice(ngListIndexOf(ng_info.user), 1); updateNgList(); saveNgData(); }; view_config.onchange = function(){ view_config_db.ng_item_filling =view_config.value; saveViewConfig(); } enable_keyword_search.onchange = function(){ view_config_db.at_keyword_search = enable_keyword_search.value; saveViewConfig(); } function dateString(date){ var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var toofar = new Date(3000, 11, 31, 23, 59, 59, 999); if (date > toofar) return "9999/12/31"; return year + "/" + month + "/" + day; } function formatNgInfo(type, date, user){ return "[" + type + "]" + date + "まで" + user; } function parseNgInfo(nginfo_text){ var type, date, user; type = nginfo_text.substr(1, nginfo_text.indexOf("]") - 1); date = nginfo_text.substr(nginfo_text.indexOf("]") + 1, nginfo_text.indexOf("まで") - 1 - nginfo_text.indexOf("]")); user = nginfo_text.substr(nginfo_text.indexOf("まで") + "まで".length); return { user : user, type : type, date : date }; } function saveNgData(){ localStorage.setItem("nicovideo_seiga_ng", JSON.stringify(ng_list_db)); } function loadNgData(){ //ブラウザに保存したNGリストデータをロード var db = localStorage.getItem("nicovideo_seiga_ng"); if (db == null){ ng_list_db = []; }else{ ng_list_db = JSON.parse(localStorage.getItem("nicovideo_seiga_ng")); } //期限切れのNG指定があったら削除 var today = new Date(); for (var i = ng_list_db.length - 1; i >= 0; i--){ var yy_mm_dd = ng_list_db[i].date.split("/"); var end_date = new Date(yy_mm_dd[0], yy_mm_dd[1], yy_mm_dd[2]); if (today > end_date){ ng_list_db.splice(i, 1); } } //ユーザID形式の項目があったら対応するユーザ名を取得 if (id_query_flag == 0){ for (var j = 0; j < ng_list_db.length; j++){ if (ng_list_db[j].type == "NGユーザーID"){ var name = getUserName(ng_list_db[j].user); var obj = { id : ng_list_db[j].user, name : name }; id_name_list.push(obj); } } id_query_flag = 1; } } function updateNgList(){ ng_list.innerHTML = ""; for (var i = 0; i < ng_list_db.length; i++) { var opt = document.createElement("option"); opt.text = formatNgInfo(ng_list_db[i].type, ng_list_db[i].date, ng_list_db[i].user); ng_list.appendChild(opt); } } function loadViewConfig(){ //ブラウザに保存した表示設定をロード var db = localStorage.getItem("nicovideo_seiga_ng_view_config"); if (db == null){ view_config_db.push({ ng_item_filling: "削除した枠を:詰める"}); view_config_db.push({ at_keyword_search: "キーワード・定点観測時:無効"}); }else{ view_config_db = JSON.parse(db); } view_config.value = view_config_db.ng_item_filling; enable_keyword_search.value = view_config_db.at_keyword_search; } function saveViewConfig(){ //表示設定をブラウザに保存 localStorage.setItem("nicovideo_seiga_ng_view_config", JSON.stringify(view_config_db)); } function ngListIndexOf(user){ for(var i = 0; i < ng_list_db.length; i++){ if (ng_list_db[i].user == user) return i; } return -1; } function request(url){ var xhr = new XMLHttpRequest(); var recv_flag = 0; xhr.onreadystatechange = function (){ switch(xhr.readyState){ case 4: if(xhr.status == 0) alert("XHR 通信失敗"); else{ if((200 <= xhr.status && xhr.status < 300) || (xhr.status == 304))recv_flag = 1; else alert("その他の応答:" + xhr.status); } break; } }; xhr.open("GET", url, false); xhr.send(null); var cnt = 0; while(recv_flag == 0){ if (recv_flag == 1) break; cnt +=1; setTimeout(() => {;}, 100); if (cnt > 10) break; } return xhr; } function requestXml(url){ var xhr = request(url); var resp = xhr.responseXML; xhr.abort(); return resp; } function requestText(url){ var xhr = request(url); var resp = xhr.responseText; xhr.abort(); return resp; } function getUserName(id){ var username_query_url = "https://seiga.nicovideo.jp/api/user/info?id=" + id; var xml = requestXml(username_query_url); var nickname = xml.documentElement.getElementsByTagName("nickname")[0]; return nickname.textContent; } function getIllustInfo(id){ var illustinfo_query_url = "https://seiga.nicovideo.jp/api/illust/info?id=" + id; var xml = requestXml(illustinfo_query_url); var title = xml.documentElement.getElementsByTagName("title")[0]; var user_id = xml.documentElement.getElementsByTagName("user_id")[0]; return {title: title.textContent, user_id: user_id.textContent}; } function isNgUser(user_name, user_id){ for (var i = 0; i < ng_list_db.length; i++){ if (ng_list_db[i].type == "NGユーザー名") { if (user_name != null){ if (user_name == ng_list_db[i].user) return true; }else{ if (getUserName(user_id) == ng_list_db[i].user) return true; } }else if(ng_list_db[i].type == "NGユーザーID"){ if (user_id != null){ if (user_id == ng_list_db[i].user) return true; }else{ for (var j = 0; j < id_name_list.length; j++){ if (user_name == id_name_list[j].name) return true; } } } } return false; } //ページロード時の該当項目非表示処理 var matched_ng_items = []; var ngElement; if ((mode == "キーワード検索") || (mode == "定点観測")){ //キーワード検索や定点観測の場合 if(enable_keyword_search.value == "キーワード・定点観測時:無効") return; var image_frames = Array.prototype.slice.call(document.getElementsByClassName("center_img_inner ")); //<=!注意!このクラス名の最後にスペースが入っている var image_ids; if (mode == "キーワード検索") image_ids = image_frames.map(elem => elem.href.substring("https://seiga.nicovideo.jp/seiga/im".length, elem.href.indexOf("?"))); else if(mode == "定点観測") image_ids = image_frames.map(elem => elem.href.replace("https://seiga.nicovideo.jp/seiga/im", "")); for (i = 0; i < image_frames.length; i++){ var user_id = getIllustInfo(image_ids[i]).user_id; if (isNgUser(null, user_id)){ ngElement = image_frames[i].parentNode.parentNode; matched_ng_items.push(ngElement); } } }else{ //キーワード検索以外の場合 var users = document.getElementsByClassName('user'); for (i = 0; i < users.length; i++) { var user_name = users[i].innerHTML; if (isNgUser(user_name, null)) { ngElement = users[i].parentNode.parentNode.parentNode; matched_ng_items.push(ngElement); } } } for (i = matched_ng_items.length-1; i >= 0; i--){ if (view_config.value == "削除した枠を:詰める"){ matched_ng_items[i].remove(); }else{ matched_ng_items[i].style.visibility = 'hidden'; } }