// ==UserScript==
// @name AnimeWorld Scrobbling
// @namespace https://www.pizidavi.altervista.org/
// @description Segna automaticamente gli episodi visualizzati su Trakt.TV
// @author pizidavi
// @version 1.5.0
// @copyright 2020, PIZIDAVI
// @license MIT
// @homepageURL https://www.pizidavi.altervista.org/AnimeWoldScrobbling/
// @require https://greasyfork.org/scripts/401626-notify-library/code/Notify%20Library.js
// @include https://*.animeworld.*/play/*
// @connect https://api.trakt.tv/
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
(function($) {
'use strict';
// USER INFO
const CLIENT_ID = '';
const ACCESS_TOKEN = '';
const EXPIRES = '';
// SETTINGS
const SHOW_HELPER = true;
const AUTO_NEXT_EPISODE = false;
/* ----------- CODE ----------- */
const CSS = '#body .sidebar { float: right; width: 300px; position: relative; z-index: 1; } #trakt-results .item .info a.name::after { font-family: "Font Awesome 5 Free"; font-size: 9px; font-weight: 900; content: "\\f35d"; margin-left: 5px; vertical-align: super; }';
const TEMPLATE = '<div id="trakt" class="sidebar"><div class="widget simple-film-list"><div class="widget-title"><div class="title">Trakt</div></div><div class="widget-body"><div class="row mb-3"><div class="col-sm-10" style=" padding-right: 0; "><button class="btn btn-primary btn-block" id="watched">Episodio Visto</button></div><div class="col-sm-2"><input type="checkbox" id="autoNext" style="margin-top: 8px;" title="Prossimo episodio automatico"></div></div><div class="row"><div class="col-sm-6" style=" padding-right: 0; "><input type="text" class="form-control" placeholder="Trakt Slug"><small id="helper" style="display:none;margin:0.3em 0px -5px 0.5em;"><a href="https://www.pizidavi.altervista.org/AnimeWoldScrobbling/#trakt" target="_blank" style="color:grey;">Dove lo trovo?</a></small></div><div class="col-sm-2" style=" padding-right: 0; "><input type="number" class="form-control" value="1" min="0" placeholder="Season"></div><div class="col-sm-4"><button id="save-trakt" class="btn btn-success btn-block">Salva</button></div></div><div id="trakt-results" class="mt-3" style="display:none;"><hr class="my-3"/><h5 class="mb-1">Risultati di ricerca su Trakt.TV</h5></div></div></div></div>';
const TEMPLATE_ITEM = '<div class="item" role="button" title="Click per selezionare questo risultato"><img src="#" class="thumb" style="opacity:0;"><div class="info"><a class="name" href="#" target="_blank"></a><p class="year mb-0"></p></div></div>';
const AnimeID = getAnimeID();
const Trakt = GM_getValue(AnimeID, {});
var section = $(TEMPLATE);
$('#body #body-container').append(section);
var style = document.createElement('style');
style.innerText = CSS;
document.head.appendChild(style);
if(!CLIENT_ID || !ACCESS_TOKEN || !EXPIRES) {
section.find('div.widget-body').html('Dati Trakt mancanti. Segui la <a href="https://www.pizidavi.altervista.org/AnimeWoldScrobbling/" target="_blank">guida</a>');
return; }
if(new Date() >= new Date(EXPIRES)) {
section.find('div.widget-body').html('Access Token Trakt Scaduto. <a href="https://www.pizidavi.altervista.org/AnimeWoldScrobbling/#login" target="_blank">Aggiorna</a>');
return; }
if(AnimeID == undefined) {
section.find('div.widget-body').html('Errore. AnimeID non trovato');
return; }
section.find('input[type="text"]').val(Trakt.slug);
section.find('input[type="number"]').val((Trakt.season || '1'));
if(AUTO_NEXT_EPISODE) {
section.find('input[type="checkbox"]').attr('checked', ''); }
if(SHOW_HELPER) {
section.find('#helper').css('display', 'block'); }
section.find('#save-trakt').on('click', function() {
var title = $('#main div.widget.info div.info > div.head h2').text();
var slug = $(this).parent().parent().find('input[type="text"]').val();
var season = $(this).parent().parent().find('input[type="number"]').val();
if(slug != '' && season != '') {
Trakt.title = title.trim();
Trakt.slug = slug.trim();
Trakt.season = season;
GM_setValue(AnimeID, Trakt);
section.find('button#watched').removeAttr('disabled');
new Notify({
text: 'Dati salvati',
type: 'success'
}).show();
} else {
new Notify({
text: 'Completa tutti i campi',
type: 'warn'
}).show();
}
});
section.find('#watched').on('click', function() {
var _this = $(this);
var episode = $('div.server ul a.active').attr('data-base');
if(Trakt.slug == undefined || Trakt.season == undefined || episode == undefined) {
new Notify({
text: 'Errore',
type: 'error'
}).show();
return;
}
_this.attr('disabled', '');
var joData = {
'shows': [
{
'ids': {
'slug': Trakt.slug,
},
'seasons': [
{
'number': parseInt(Trakt.season),
'episodes': [
{
'watched_at': new Date().toJSON(),
'number': parseFloat(episode)
}
]
}
]
}
]
};
request('POST', '/sync/history', joData, function(data) {
if(data.added.episodes > 0) {
new Notify({
text: 'Episodio '+episode+' salvato',
type: 'success'
}).show();
if(AUTO_NEXT_EPISODE || section.find('#autoNext').prop('checked'))
$('#controls > div.prevnext[data-value="next"]').click();
} else {
new Notify({
text: 'Errore. Episodio non trovato',
type: 'error'
}).show();
}
_this.removeAttr('disabled');
});
});
$(document).on('click', 'div.userbookmark li:nth-child(2), div.userbookmark li:nth-child(7)', function() {
var deleted = deleteOne(AnimeID);
if(deleted) {
section.find('input[type="text"]').val('');
section.find('input[type="number"]').val('1');
section.find('button#watched').attr('disabled', '');
Trakt.title = null;
Trakt.slug = null;
Trakt.season = null;
new Notify({
text: 'Dati Trakt rimossi',
type: 'success'
}).show();
}
});
if(Trakt.slug == undefined || Trakt.season == undefined) {
section.find('#watched').attr('disabled', '');
var type = $('#main div.widget.info div.info > div.row > .meta:nth-child(1) dd:nth-child(2)').text().trim();
if(type == 'Special' || type == 'OVA') {
return; }
type = (type == 'Movie' ? 'movie' : 'show');
var title = $('#main div.widget.info div.info > div.head h2').text();
var season = parseInt(title.split(' ')[title.split(' ').length - 1]);
title = title.replace('(ITA)', '').replace('(TV)', '');
title = title.replace( (!isNaN(season) ? season : ''), '');
title = title.trim();
season = (isNaN(season) ? 1 : season);
request('GET', '/search/'+type+'?query='+encodeURI(title), null, function(data) {
section.find('#trakt-results').show();
$.each(data, function(index) {
if(index >=3) { return; }
var item = $(TEMPLATE_ITEM);
item.attr('data-slug', this[this.type].ids.slug);
item.find('.info .name').text( (this[this.type].title.length > 45 ? this[this.type].title.substring(0, 45)+'...' : this[this.type].title)).attr('title', this[this.type].title);
item.find('.info .name').attr('href', 'https://trakt.tv/'+this.type+'s/'+this[this.type].ids.slug);
item.find('.info .year').text(this[this.type].year);
item.on('click', function(e) {
if(e.target.tagName == 'A') { return; }
var slug = $(this).attr('data-slug');
section.find('input[type="text"]').val(slug);
section.find('input[type="number"]').val(season);
section.find('#save-trakt').click();
});
section.find('#trakt-results').append(item);
if(this[this.type].ids.tmdb != null) {//
item.attr('data-tmdb-id', this[this.type].ids.tmdb);
$.ajax({
url: 'https://api.themoviedb.org/3/'+ (this.type == 'show' ? 'tv' : 'movie') +'/'+ this[this.type].ids.tmdb +'/images?api_key=52a23d06812ad987218e2e41ec6eb79c',
success: function(data) {
if(data.posters.length > 0) {
var image = section.find('#trakt-results').find('[data-tmdb-id="'+data.id+'"] > img');
image.attr('src', 'https://image.tmdb.org/t/p/w92'+data.posters[0].file_path).css('opacity', '1'); }
}
});
}
});
});
}
// Functions
function request(method, url, data, success) {
var r = new XMLHttpRequest();
r.open(method, 'https://api.trakt.tv'+url);
r.setRequestHeader('Content-Type', 'application/json');
r.setRequestHeader('Authorization', 'Bearer '+ACCESS_TOKEN);
r.setRequestHeader('trakt-api-version', '2');
r.setRequestHeader('trakt-api-key', CLIENT_ID);
r.onreadystatechange = function() {
if (this.readyState === 4 && (this.status == 200 || this.status == 201)) {
success(JSON.parse(this.responseText));
} else if (this.readyState === 4) {
new Notify({
text: 'Errore nella richiesta. Ricarica la pagina',
type: 'error'
}).show();
}
};
r.send( (data ? JSON.stringify(data) : null ) );
}
function getAnimeID() {
var url = location.pathname;
var start = url.indexOf('.')+1;
var end = start + (url.substring(start).indexOf('/') >= 0 ? url.substring(start).indexOf('/') : url.substring(start).length);
return url.substring(start, end) || undefined;
}
function deleteOne(key) {
if(GM_listValues().includes(key)) {
GM_deleteValue(key);
return true;
} else {
return false;
}
}
function deleteAll() {
GM_listValues().forEach(function(key) {
GM_deleteValue(key);
});
return true;
}
})(jQuery);