AnimeWorld Scrobbling

Segna automaticamente gli episodi visualizzati su Trakt.TV

目前为 2020-07-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AnimeWorld Scrobbling
  3. // @namespace https://www.pizidavi.altervista.org/
  4. // @description Segna automaticamente gli episodi visualizzati su Trakt.TV
  5. // @author pizidavi
  6. // @version 1.4.0
  7. // @copyright 2020, PIZIDAVI
  8. // @license MIT
  9. // @homepageURL https://www.pizidavi.altervista.org/AnimeWoldScrobbling/
  10. // @require https://greasyfork.org/scripts/401626-notify-library/code/Notify%20Library.js
  11. // @include https://*.animeworld.*/play/*
  12.  
  13. // @connect https://api.trakt.tv/
  14. // @grant GM_addStyle
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_deleteValue
  18. // @grant GM_listValues
  19. // @grant GM_xmlhttpRequest
  20.  
  21. // ==/UserScript==
  22.  
  23. (function($) {
  24. 'use strict';
  25.  
  26. // USER INFO
  27. const CLIENT_ID = '';
  28. const ACCESS_TOKEN = '';
  29. const EXPIRES = '';
  30.  
  31. // SETTINGS
  32. const SHOW_HELPER = true;
  33. const AUTO_NEXT_EPISODE = false;
  34.  
  35.  
  36. /* ----------- CODE ----------- */
  37. const css = '#body .sidebar { float: right; width: 300px; position: relative; z-index: 1; }';
  38. const template = '<div id="trakt" class="sidebar"><div class="widget"><div class="widget-title"><div class="title">Trakt</div></div><div class="widget-body"><div class="row" style="margin-bottom:1em;"><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 class="btn btn-success btn-block">Salva</button></div></div></div></div></div>';
  39.  
  40. const AnimeID = getAnimeID();
  41. var Trakt = GM_getValue(AnimeID, {});
  42. var section = $(template);
  43.  
  44. GM_addStyle(css);
  45. $('#body #body-container').append(section);
  46.  
  47. if(!CLIENT_ID || !ACCESS_TOKEN || !EXPIRES) {
  48. section.find('div.widget-body').html('Dati Trakt mancanti. Segui la <a href="https://www.pizidavi.altervista.org/AnimeWoldScrobbling/" target="_blank">guida</a>');
  49. return; }
  50. if(new Date() >= new Date(EXPIRES)) {
  51. section.find('div.widget-body').html('Access Token Trakt Scaduto. <a href="https://www.pizidavi.altervista.org/AnimeWoldScrobbling/#login" target="_blank">Aggiorna</a>');
  52. return; }
  53. if(AnimeID == undefined) {
  54. section.find('div.widget-body').html('Errore. AnimeID non trovato');
  55. return; }
  56.  
  57. section.find('input[type="text"]').val(Trakt.slug);
  58. section.find('input[type="number"]').val((Trakt.season || '1'));
  59. if(AUTO_NEXT_EPISODE)
  60. section.find('input[type="checkbox"]').attr('checked', '');
  61. if(SHOW_HELPER)
  62. section.find('#helper').css('display', 'block');
  63.  
  64. section.find('button.btn:not(#watched)').on('click', function() {
  65. var title = $('#main div.widget.info div.info > div.head h2').text();
  66. var slug = $(this).parent().parent().find('input[type="text"]').val();
  67. var season = $(this).parent().parent().find('input[type="number"]').val();
  68.  
  69. if(slug != '' && season != '') {
  70. Trakt.title = title.trim();
  71. Trakt.slug = slug.trim();
  72. Trakt.season = season;
  73. GM_setValue(AnimeID, Trakt);
  74.  
  75. section.find('button#watched').removeAttr('disabled');
  76.  
  77. new Notify({
  78. text: 'Dati salvati',
  79. type: 'success'
  80. }).show();
  81. } else {
  82. new Notify({
  83. text: 'Completa tutti i campi',
  84. type: 'warn'
  85. }).show();
  86. }
  87. });
  88.  
  89. console.log(Trakt);
  90. if(Trakt.slug == undefined || Trakt.season == undefined) {
  91. console.log('no')
  92. section.find('button#watched').attr('disabled', ''); }
  93. $('button#watched').on('click', function() {
  94. var _this = $(this);
  95. var episode = $('div.server ul a.active').attr('data-base');
  96. if(Trakt.slug == undefined || Trakt.season == undefined || episode == undefined) {
  97. new Notify({
  98. text: 'Errore',
  99. type: 'error'
  100. }).show();
  101. return;
  102. }
  103. _this.attr('disabled', '');
  104.  
  105. var joData = {
  106. 'shows': [
  107. {
  108. 'ids': {
  109. 'slug': Trakt.slug,
  110. },
  111. 'seasons': [
  112. {
  113. 'number': parseInt(Trakt.season),
  114. 'episodes': [
  115. {
  116. 'watched_at': new Date().toJSON(),
  117. 'number': parseFloat(episode)
  118. }
  119. ]
  120. }
  121. ]
  122. }
  123. ]
  124. };
  125. request(joData, function(data) {
  126. if(data.added.episodes > 0) {
  127. new Notify({
  128. text: 'Episodio '+episode+' salvato',
  129. type: 'success'
  130. }).show();
  131.  
  132. if(AUTO_NEXT_EPISODE || section.find('#autoNext').prop('checked'))
  133. $('#controls > div.prevnext[data-value="next"]').click();
  134. } else {
  135. new Notify({
  136. text: 'Errore. Episodio non trovato',
  137. type: 'error'
  138. }).show();
  139. }
  140. _this.removeAttr('disabled');
  141. });
  142. });
  143. $(document).on('click', '#controls > div.userbookmark > ul > li:nth-child(2), #controls > div.userbookmark > ul > li:nth-child(7)', function() {
  144. var deleted = deleteOne(AnimeID);
  145. if(deleted) {
  146. section.find('input[type="text"]').val('');
  147. section.find('input[type="number"]').val('1');
  148. section.find('button#watched').attr('disabled', '');
  149. Trakt = {};
  150.  
  151. new Notify({
  152. text: 'Dati Trakt rimossi',
  153. type: 'success'
  154. }).show();
  155. }
  156. });
  157.  
  158.  
  159. // Functions
  160. function request(data, success) {
  161. var r = new XMLHttpRequest();
  162. r.open('POST', 'https://api.trakt.tv/sync/history');
  163.  
  164. r.setRequestHeader('Content-Type', 'application/json');
  165. r.setRequestHeader('Authorization', 'Bearer '+ACCESS_TOKEN);
  166. r.setRequestHeader('trakt-api-version', '2');
  167. r.setRequestHeader('trakt-api-key', CLIENT_ID);
  168.  
  169. r.onreadystatechange = function () {
  170. if (this.readyState === 4 && (this.status == 200 || this.status == 201)) {
  171. success(JSON.parse(this.responseText));
  172. } else if (this.readyState === 4) {
  173. new Notify({
  174. text: 'Errore nella richiesta. Ricarica la pagina',
  175. type: 'error'
  176. }).show();
  177. }
  178. };
  179. r.send(JSON.stringify(data));
  180. }
  181.  
  182. function getAnimeID() {
  183. var url = location.pathname;
  184. var start = url.indexOf('.')+1;
  185. var end = start + (url.substring(start).indexOf('/') >= 0 ? url.substring(start).indexOf('/') : url.substring(start).length);
  186. return url.substring(start, end) || undefined;
  187. }
  188.  
  189. function deleteOne(key) {
  190. if(GM_listValues().includes(key)) {
  191. GM_deleteValue(key);
  192. return true;
  193. } else {
  194. return false;
  195. }
  196. }
  197. function deleteAll() {
  198. GM_listValues().forEach(function(key) {
  199. GM_deleteValue(key);
  200. });
  201. return true;
  202. }
  203.  
  204. })(jQuery);