Skip to content

Instantly share code, notes, and snippets.

@rosylilly
Created December 30, 2010 19:23
Show Gist options
  • Save rosylilly/760170 to your computer and use it in GitHub Desktop.
Save rosylilly/760170 to your computer and use it in GitHub Desktop.
ニコニコ動画歌詞メンテ支援Greasemonkey
// ==UserScript==
// @name NicoLyricManager
// @namespace NicoLyricManager
// @description ニコ動の歌詞職人支援ツール
// @include http://www.nicovideo.jp/watch/*
// @include http://nine.nicovideo.jp/watch/*
// @excludes
// ==/UserScript==
(function(){
const GETFLV_URL = 'http://flapi.' + location.host.replace(/^(www|nine)\./, '') + '/api/getflv';
const TABLE_HEAD = '<thead><tr><th>' + [ 'Date', 'No', 'From', 'Pos', 'Cmd', 'Body' ].join('</th><th>') + '</th></thead>';
// All Storage
var storage = {
user_id: 0,
thread_id: 0,
length: 0,
message_server: '',
chats: []
};
// parse URL Query
var parseUrlQuery = function(orig){
var orig_ary = orig.split('&');
var params = {};
for(var i=0,l=orig_ary.length; i<l; i++){
var pair = orig_ary[i].split('=');
params[unescape(pair.shift())] = unescape(pair.join('='));
};
return params;
};
// get video information
var getInfo = function(videoId, callback){
var xhr = new XMLHttpRequest();
var params = {};
xhr.open('GET', GETFLV_URL + '?v='+videoId.toString());
xhr.withCredentials = true;
xhr.addEventListener('readystatechange', function(e){
if(xhr.status == 200 && xhr.readyState == 4){
params = parseUrlQuery(xhr.responseText);
callback(params);
};
}, false);
xhr.send();
};
// message server
var getThread = function(callback){
var xhr = new XMLHttpRequest();
var packet = (
'thread?version=20090904&thread=' +
storage.thread_id.toString() +
'&user_id=' +
storage.user_id.toString()
);
xhr.addEventListener('readystatechange', function(e){
if(xhr.status == 200 && xhr.readyState == 4){
var xhr2 = new XMLHttpRequest();
var leaves_request = '0-' + parseInt((storage.length / 60) + 1).toString() +':100';
var packet = (
'thread_leaves?version=20090904&thread=' +
escape(storage.thread_id.toString()) +
'&user_id=' +
escape(storage.user_id.toString()) +
'&body=' +
escape(leaves_request)
);
xhr2.addEventListener('readystatechange', function(e){
if(xhr2.status == 200 && xhr2.readyState == 4){
callback(xhr2.responseXML);
};
}, false);
xhr2.open('GET', storage.message_server.toString() + packet);
xhr2.send();
};
}, false);
xhr.open('GET', storage.message_server.toString() + packet);
xhr.send();
};
// video id
var videoId = location.pathname.replace(/.*\//,'');
// unix time to Date object
var unixToDate = function(orig){
var date = new Date(orig * 1000);
date.setTime(date.getTime() + (60*60*1000*9));
return date;
};
// 1/100 sec position to 00:00
var vposToString = function(vpos){
vpos = parseInt(vpos / 100);
var ret = [];
var min = parseInt(vpos / 60);
var sec = parseInt(vpos % 60);
ret.push(min);
ret.push(sec < 10 ? '0'+sec.toString() : sec);
return ret.join(':');
};
// format date
var formatDate = function(date){
var days = ([date.getFullYear(), date.getMonth()+1, date.getDate()]).join('/');
var times = ([date.getHours(), date.getMinutes(), date.getSeconds()]).join(':');
return (days+' '+times);
};
// dom ex
var setView = function(){
var view_area = document.getElementById('lyric_view');
if(!view_area){
var tab = document.getElementById('itab_description');
view_area = document.createElement('div');
view_area.id = 'lyric_view';
view_area.setAttribute('style', 'border:3px solid #333;padding:5px;');
tab.appendChild(view_area);
};
var html_content = [];
for(var i=0, l=storage.chats.length; i<l; i++){
var chat = storage.chats[i];
var index = 0;
for(var ii=0,ll=storage.leafIndex[chat.leaf].length; ii<ll; ii++){
if(storage.leafIndex[chat.leaf][ii] == chat.no){
index = ii;
};
};
html_content.push([
formatDate(chat.date),
chat.no,
'-' + index.toString(),
vposToString(chat.vpos),
chat.command.toString(),
chat.body].join('</td><td style="padding:2px;">'));
};
html_content = ('<tr><td style="padding:2px;">' + html_content.join('</td></tr><tr><td style="padding:2px;">') + '</td></tr>');
html_content = '<tbody>' + html_content + '</tbody>';
html_content = '<table style="font:12px monospace;width:100%;">' + TABLE_HEAD + html_content + '</table>';
view_area.innerHTML = html_content;
// reload
var reload = document.createElement('a');
reload.setAttribute('href', '#lyric_view');
reload.addEventListener('click', function(e){
e.preventDefault();
getData();
}, false);
reload.innerHTML = '更新';
view_area.appendChild(reload);
// target change
var t_id_input = document.createElement('input')
t_id_input.setAttribute('type', 'text');
if(storage.prev_target){
t_id_input.value = storage.prev_target;
};
view_area.appendChild(t_id_input);
var changeTarget = document.createElement('a');
changeTarget.setAttribute('href', '#lyric_view');
changeTarget.addEventListener('click', function(e){
e.preventDefault();
var target = 0;
target = t_id_input.value;
getData(target);
}, false);
changeTarget.innerHTML = '監視ユーザー変更(ID)';
view_area.appendChild(changeTarget);
};
var commentSort = function(a, b){
if(a.vpos != b.vpos){
return a.vpos - b.vpos;
} else {
return a.no - b.no;
};
};
var getLeafIndex = function(chats){
var leafs = [];
chats.sort(commentSort);
for(var i=0, l=chats.length; i<l; i++){
var chat = chats[i];
if(leafs[chat.leaf] == undefined || leafs[chat.leaf] == null){
leafs[chat.leaf] = [];
};
leafs[chat.leaf].push(chat.no);
};
for(var i=0, l=leafs.length; i<l; i++){
if(leafs[i] == undefined || leafs[i] == null){
leafs[i] = [];
};
};
return leafs;
};
var getData = function(target){
var target_id = target != null ? target : storage.prev_target;
if(!target_id){
target_id = storage.user_id;
};
getThread(function(xml){
var nodes = xml.firstChild.childNodes;
var chats = [];
storage.chats = [];
for(var i=0,l=nodes.length; i<l; i++){
var node = nodes[i];
if(node.nodeName.toString().toLowerCase() == 'chat'){
if(node.getAttribute('deleted') != null){
continue;
};
if(node.getAttribute('user_id').toString() == target_id){
storage.chats.push({
body: node.textContent.toString(),
vpos: parseInt(node.getAttribute('vpos').toString()),
command: node.getAttribute('mail') ? node.getAttribute('mail').toString() : '',
no: parseInt(node.getAttribute('no').toString()),
leaf: parseInt(parseInt(node.getAttribute('vpos').toString())/(100 * 60)),
date: unixToDate(parseInt(node.getAttribute('date')))
});
};
chats.push({
vpos: parseInt(node.getAttribute('vpos').toString()),
no: parseInt(node.getAttribute('no').toString()),
leaf: parseInt(parseInt(node.getAttribute('vpos').toString())/(100 * 60))
});
};
};
chats.sort(commentSort);
storage.chats.sort(commentSort);
storage.leafIndex = getLeafIndex(chats);
storage.prev_target = target_id;
setView();
});
};
getInfo(videoId, function(params){
storage.user_id = params['user_id'].toString();
storage.thread_id = params['thread_id'];
storage.length = params['l'];
storage.message_server = params['ms'];
getData();
});
})();
@rosylilly
Copy link
Author

  • 2011/1/7 2:50
    • コメントリストを表示するタブを変更

@rosylilly
Copy link
Author

  • 2011/1/7 3:58
    • 自分のユーザーID以外のコメントのリストを取得できるよう変更
    • 匿名のユーザーIDでも対象指定できるように変更
    • 最新インデックスの計算方法にバグがあったのを修正
    • 表示順を動画位置順に変更

@rosylilly
Copy link
Author

  • 2011/1/9 2:33
    • コメントのソートがおかしいバグを修正
    • 日付のカラム表示を追加

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment