// ==UserScript==
// @name The Pirate Calendar (for trakt.tv)
// @version 0.4.3
// @description Adds RARBG links to trakt.tv (now with a settings menu!)
// @author luffier
// @namespace PirateCalendar
// @license MIT
// @match *://trakt.tv/calendars/my/shows
// @match *://trakt.tv/calendars/my/shows/*
// @match *://trakt.tv/shows/*
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
// @require https://code.jquery.com/jquery-3.6.0.slim.min.js
// @grant GM_addStyle
// @grant GM_listValues
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @run-at document-idle
// @homepageURL https://github.com/Luffier/the-pirate-calendar
// @supportURL https://github.com/Luffier/the-pirate-calendar/issues
// @downloadURL none
// ==/UserScript==
/* globals $, GM_config */
/* jshint esversion: 6 */
(() => {
'use strict';
/* GLOBAL STYLES */
$(
`
`
).appendTo($('head'));
/* GLOBAL VARIABLES */
const regex = {
calendar: /^\/calendars\/my\/shows/,
show: /^\/shows\/([^\/]+)(\/)?$/,
season: /^\/shows\/([^\/]+)\/seasons\/([^\/]+)(\/)?$/,
episode: /^\/shows\/([^\/]+)\/seasons\/([^\/]+)\/episodes\/([^\/]+)(\/)?$/,
};
const intervals = {};
var urlRarbg;
/* SETTINGS MENU */
GM_config.init({
'id': 'PirateCalendarConfig',
'title': 'The Pirate Calendar Settings',
'fields': {
'autoscrollToday': {
'label': 'Auto scroll to current day',
'type': 'checkbox',
'default': true
},
'hideCollectIcon': {
'label': 'Hide collect icon',
'type': 'checkbox',
'default': false
},
'hideListIcon': {
'label': 'Hide list icon',
'type': 'checkbox',
'default': false
},
'hideWatchtIcon': {
'label': 'Hide watch-now icon',
'type': 'checkbox',
'default': false
},
'openInNewTab': {
'label': 'Open links in new tab',
'type': 'checkbox',
'default': true
},
'customUrlRarbg': {
'label': 'RARBG URL',
'type': 'text',
'default': 'https://rarbg.to/'
}
},
'css':
`
body#PirateCalendarConfig {
position: relative !important;
font-family: 'proxima nova', 'Helvetica', 'Arial', 'sans-serif' !important;
margin: 0 !important;
}
#PirateCalendarConfig .config_var {
margin: 8px 8px 8px 12px !important;
}
#PirateCalendarConfig .config_var input[type="text"] {
border: 2px inset black !important;
}
#PirateCalendarConfig_buttons_holder {
position: relative !important;
}
#PirateCalendarConfig_header {
background-color: #f7f7f7;
border-bottom: 1px solid #ebebeb;
padding: 20px 0 10px 0;
}
#PirateCalendarConfig_buttons_holder {
right: 20px;
}
#PirateCalendarConfig_buttons_holder button {
color: #fff;
font-size: 12px;
padding: 4px 9px !important;
height: auto !important;
cursor: pointer;
border: 1px solid transparent;
}
#PirateCalendarConfig_buttons_holder .reset_holder {
position: absolute;
right: 12px;
bottom: -20px;
}
#PirateCalendarConfig_saveBtn {
background-color: #ed1c24;
border-color: #de1219;
}
#PirateCalendarConfig_closeBtn {
background-color: #aaa;
border: 1px solid transparent;
}
`,
'events': {
'init': () => applySettings(),
'save': function() {
applySettings();
GM_config.close();
}
}
});
/* FUNCTIONS */
// Pad number with leading zeros
function zeroPad (number, places) {
return String(number).padStart(places, '0');
}
// Executes the callback after the calendar finishes loading
function whenCalendarReady(callback, intervalName) {
setTimeout(() => {
intervals[intervalName] = setInterval(() => {
// If the loading indicator and the progress bar aren't visible, the calendar is ready
if (!$('#loading-bg').is(':visible') && $('.turbolinks-progress-bar').length === 0) {
clearInterval(intervals[intervalName]);
callback();
}
}, 100);
}, 200);
}
// Apply settings from the setting's menu
function applySettings() {
// Hide unwanted icons
$('div.quick-icons.smaller a.collect').toggle(!GM_config.get('hideCollectIcon'));
$('div.quick-icons.smaller a.list').toggle(!GM_config.get('hideListIcon'));
$('div.quick-icons.smaller a.watch-now').toggle(!GM_config.get('hideWatchtIcon'));
// Set RARBG URL
urlRarbg = GM_config.get('customUrlRarbg') + 'torrents.php?order=size&by=DESC&search=';
}
function addLinkToGridItem(gridItem, type) {
let item = $(gridItem);
let actions = item.find('> div.quick-icons > div.actions').first();
let itemLink = item.find('a').first().attr('href');
let itemLinkMatches = itemLink.match(regex[type]);
let showTitle = itemLinkMatches[1].replace(/-/g, ' ');
let seasonNumber = itemLinkMatches[2];
let codeNumber = '';
if (type === 'season') {
codeNumber = `S${zeroPad(seasonNumber,2)}`;
} else if (type === 'episode') {
let episodeNumber = itemLinkMatches[3];
codeNumber = `S${zeroPad(seasonNumber, 2)}E${zeroPad(episodeNumber, 2)}`;
}
let urlSearch = urlRarbg + encodeURIComponent(showTitle + ' ' + codeNumber).replace(/%20/g, '+');
let target = GM_config.get('openInNewTab') ? '_blank' : '_self';
actions.append(
`
`
);
}
function addLinkToActionList(actionList, type) {
let itemLink = location.pathname;
let itemLinkMatches = itemLink.match(regex[type]);
let showTitle = itemLinkMatches[1].replace(/-/g, ' ');
let seasonNumber = itemLinkMatches[2];
let codeNumber = '';
if (type === 'season') {
codeNumber = `S${zeroPad(seasonNumber,2)}`;
} else if (type === 'episode') {
let episodeNumber = itemLinkMatches[3];
codeNumber = `S${zeroPad(seasonNumber, 2)}E${zeroPad(episodeNumber, 2)}`;
}
let urlSearch = urlRarbg + encodeURIComponent(showTitle + ' ' + codeNumber).replace(/%20/g, '+');
let target = GM_config.get('openInNewTab') ? '_blank' : '_self';
actionList.append(
`
`
);
}
// Process calendar page
function processCalendarPage() {
// RARBG links
$('.grid-item[data-type="episode"]').each(function() {
addLinkToGridItem(this, 'episode');
});
// Autoscroll to current date
if (GM_config.get('autoscrollToday')) {
whenCalendarReady(function() {
// Extract the calendar date from the URL
let today = new Date();
let calendarDate = new Date(window.location.href.substring(window.location.href.lastIndexOf('/') + 1));
// If there's no date (current month) or it's current month then autoscroll
if(isNaN(calendarDate) || calendarDate.getMonth() === today.getMonth()) {
let todayCard = $('.date-separator:not(.filler) .date').filter(function () {
return $(this).text() == today.getDate();
}).first().get(0);
if (todayCard) {
todayCard.scrollIntoView(true);
let topNav = $('#top-nav').first().get(0);
let offset = -window.getComputedStyle(topNav).getPropertyValue('height').slice(0, -2);
window.scrollBy(0, offset);
}
}
}, 'autoscroll');
}
// Settings menu icon
$(
`
`
).on('click', function () {
GM_config.open();
}).appendTo('.sidenav-inner');
// Add events to arrows
whenCalendarReady(() =>
// Add events to process page again if the user changes month
$('.prev, .next').on('click', () => whenCalendarReady(() =>
processCalendarPage(), 'processAfterChangingMonth')),
'addArrowsEvents');
applySettings();
}
// Process show page
function processShowPage() {
// RARBG links
$('.grid-item[data-type="season"]').each(function() {
addLinkToGridItem(this, 'season');
});
}
// Process seasons page
function processSeasonsPage() {
$('.grid-item[data-type="episode"]').each(function() {
addLinkToGridItem(this, 'episode');
});
addLinkToActionList($('.action-buttons'), 'season');
}
// Process episode page
function processEpisodePage() {
addLinkToActionList($('.action-buttons'), 'episode');
}
if (regex.calendar.test(location.pathname)) {
processCalendarPage();
}
else if (regex.show.test(location.pathname)) {
processShowPage();
}
else if (regex.season.test(location.pathname)) {
processSeasonsPage();
}
else if (regex.episode.test(location.pathname)) {
processEpisodePage();
}
})();