// ==UserScript==
// @name Bangumi Bgmlist Integrator
// @description 将你的"在看"与 bgmlist.com 的放送数据优雅整合!
// @namespace bangumi.scripts.prevails.bgmlistintegrator
// @version 1.3.1
// @author "Donuts."
// @require https://code.jquery.com/jquery-2.2.4.min.js
// @include /^https?:\/\/(bgm\.tv|bangumi\.tv|chii\.in)\/$/
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect bgmlist.com
// @grant GM_addStyle
// ==/UserScript==
const addOnSources = {
////////////////////////////////////////////////////////////////
// 可自行添加播放源超链接 以京吹2(http://bgm.tv/subject/152091)为例
////////////////////////////////////////////////////////////////
// 152091: [
// "https://www.biliplus.com/video/av6556317/",
// "http://www.dilidili.com/anime/euphonium2/",
// ],
////////////////////////////////////////////////////////////////
};
const TIME_ZONE = 'CN';
// valid value: 'CN', 'JP'
// if not login, exit
if (!document.getElementById('badgeUserPanel')) {
return;
}
function getOneWeekRange(lastDayDate, endTime = '2359') {
const end = new Date(lastDayDate);
end.setHours(endTime.substr(0, 2));
end.setMinutes(endTime.substr(2, 2), 59, 999);
const begin = new Date(end.getTime());
begin.setTime(begin.getTime() - 1000 * 60 * 60 * 24 * 7 + 1);
return [begin, end];
}
const now = new Date();
const lastWeekRange = getOneWeekRange(now);
const WEEK_DAY = [
'Sun',
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat',
];
const TB_WINDOW_WIDTH = 250;
const bgmlist = GM_getValue('bgmlist') || {};
class Bangumi {
constructor(id, a) {
this.id = Number(id);
this.bgm = bgmlist[this.id];
this.a = a;
if (addOnSources && addOnSources[this.id]) {
this.bgm.onAirSite = addOnSources[this.id].concat(this.bgm.onAirSite);
}
}
getTime() {
const isSameDay = this.bgm.weekDayJP === this.bgm.weekDayCN;
let time = '';
const tcn = this.bgm.timeCN && this.bgm.timeCN.slice(0, 2) + ':' + this.bgm.timeCN.slice(2);
const tjp = this.bgm.timeJP.slice(0, 2) + ':' + this.bgm.timeJP.slice(2);
if (TIME_ZONE === 'JP') {
time += tjp;
} else {
time += 'CN ' + (tcn || '无');
time += '\n';
time += 'JP ' + (isSameDay ? tjp : ('前日' + tjp));
}
return time;
}
get$Html() {
const $re = $(this.a).clone();
$re.find('img').removeAttr('class');
$re.find('span').remove();
$re.attr('title', this.bgm.titleCN + '\n'+ this.bgm.titleJP + '\n播放时间:\n' + this.getTime());
$re.attr('alt', this.bgm.titleCN + '<br>' + this.bgm.titleJP);
$re.data('onAirSite', this.bgm.onAirSite);
return $re;
}
getShowDate() {
return new Date(this.bgm.showDate || 0);
}
getEndDate() {
return new Date(this.bgm.endDate || 0xfffffffffffff);
}
isInRange([begin, end]) {
const showBegin = this.getShowDate();
const showEnd = this.getEndDate();
if (showBegin <= begin && showEnd >= end) {
return true;
}
if (begin <= showBegin && showBegin <= end) {
return true;
}
if (begin <= showEnd && showEnd <= end) {
return true;
}
return false;
}
}
const myBangumis = $('#prgSubjectList > [subject_type=2] > .thumbTip')
.toArray().map(i => new Bangumi(i.getAttribute('subject_id'), i)).filter(i => i.bgm);
$('.tooltip').hide();
$('.week:eq(1)').remove();
for (let i = 1; i < 7; i++) {
const day = WEEK_DAY[(now.getDay() - i + 7) % 7];
const html = `
<li class="clearit week ${day}">
<h3><p><small>${day}</small></p></h3>
<div class="coverList clearit"></div>
</li>
`;
const $li = $(html);
$('.calendarMini .tip').before($li);
}
const $week = $('.week');
$week.each(function () {
const $div = $('div', this);
$div.html('');
const weekDay = WEEK_DAY.indexOf(this.classList[2]); // <li class="clearit week Sat">
myBangumis.filter(i => i.bgm['weekDay' + TIME_ZONE] === weekDay && i.isInRange(lastWeekRange))
.forEach(i => $div.append(i.get$Html()));
});
function rmTbWindow() {
$('#TB_window.userscript_bgmlist_integrator').fadeOut('fast', function () {
$(this).remove();
});
}
function showTbWindow(html, style) {
rmTbWindow();
$('body').append(`
<div id="TB_window" class="userscript_bgmlist_integrator"${style ? ` style="${style}"` : ''}>
${html}
<small class="grey">本插件放送数据由 <a href="http://bgmlist.com">bgmlist.com</a> 提供</small>
</div>`);
$('#TB_window.userscript_bgmlist_integrator').mouseleave(rmTbWindow);
}
$week.find('.thumbTip').click(function () {
const $this = $(this);
const $img = $this.find('img');
const style = `position:absolute;top:${$img.offset().top}px;left:${$img.offset().left - TB_WINDOW_WIDTH - 10}px;`;
const onAirSite = $this.data('onAirSite');
showTbWindow(`
<small class="grey"><a href="${$this.attr('href')}">${$this.attr('alt')}</a></small>
<ul class="line_list">
${onAirSite.map((v, i) => `
<li class="line_${i % 2 ? 'odd' : 'even'}">
<h6><a target="_blank" href="${v}">${v.replace(/https?:\/\/.+?\./, '').split('/')[0]}</a></h6>
</li>
`.trim()).join('')}
</ul>`, style);
return false;
});
GM_addStyle('#TB_window.userscript_bgmlist_integrator{display:block;width:' + TB_WINDOW_WIDTH + 'px;}');
const CHECK_UPDATE_INTERVAL = 1000 * 60 * 60 * 8; // 8h
function getLast(obj) {
let last;
for (let i in obj) {
last = i;
}
return obj[last];
}
function createIndexOnBgmId(bgmlistOriginJson) {
const origin = JSON.parse(bgmlistOriginJson);
const bgmlist = {};
for (let i in origin) {
bgmlist[origin[i].bgmId] = origin[i];
}
return bgmlist;
}
function update({path, version}) {
GM_xmlhttpRequest({
method: 'GET',
url: path,
data: {"__t": Date.now()},
onload: function(response) {
if (response.status === 200) {
GM_setValue('bgmlist', createIndexOnBgmId(response.responseText));
GM_setValue('path', path);
GM_setValue('version', version);
showTbWindow('bgmlist 数据更新成功! 请<a class="l" href="javascript:location.reload();">刷新页面</a><br>',
'left:80%;top:20px;width:18%;');
setTimeout(rmTbWindow, 5000);
} else {
showTbWindow(`Error, status code: ${response.status}<br>`,
'left:80%;top:20px;width:18%;');
setTimeout(rmTbWindow, 5000);
}
}
});
}
function checkUpdate() {
const lastCheckUpdate = GM_getValue('lastCheckUpdate') || 0;
if (new Date().getTime() - lastCheckUpdate < CHECK_UPDATE_INTERVAL) {
return;
}
GM_xmlhttpRequest({
method: 'GET',
url: 'https://bgmlist.com/tempapi/archive.json',
data: {"__t": Date.now()},
onload: function (response) {
if (response.status === 200) {
const archive = JSON.parse(response.responseText);
const data = archive.data;
const last = getLast(getLast(data));
const oldPath = GM_getValue('path');
const oldVersion = GM_getValue('version');
if (!oldPath || !oldVersion || last.path > oldPath || last.version > oldVersion) {
update(last);
}
GM_setValue('lastCheckUpdate', new Date().getTime());
} else {
showTbWindow(`Error, status code: ${response.status}<br>`,
'left:80%;top:20px;width:18%;');
setTimeout(rmTbWindow, 5000);
}
}
});
}
setTimeout(checkUpdate, 500);