// ==UserScript==
// @name Translate Trakt
// @name:it Traduci Trakt
// @author Felix
// @namespace https://github.com/iFelix18
// @description Translates titles, plots, taglines and posters of movies, TV series and episodes in the choice language
// @description:it Traduce titoli, trame, tagline e poster di film, serie TV ed episodi nella lingua scelta
// @copyright 2019, Felix (https://github.com/iFelix18)
// @license MIT
// @version 2.0.4
// @homepageURL https://git.io/Trakt-Userscripts
// @homepageURL https://greasyfork.org/scripts/377969-translate-trakt
// @homepageURL https://openuserjs.org/scripts/iFelix18/Translate_Trakt
// @supportURL https://github.com/iFelix18/Trakt-Userscripts/issues
// @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js#sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=
// @require https://cdn.jsdelivr.net/gh/soufianesakhi/node-creation-observer-js@edabdee1caaee6af701333a527a0afd95240aa3b/release/node-creation-observer-latest.min.js
// @require https://cdn.jsdelivr.net/gh/sizzlemctwizzle/GM_config@a4a49b47ecfb1d8fcd27049cc0e8114d05522a0f/gm_config.min.js
// @require https://cdn.jsdelivr.net/gh/cvzi/RequestQueue@e4297b3c2e11761d69858bad4746832ea412b571/RequestQueue.min.js
// @match *://trakt.tv/*
// @connect api.themoviedb.org
// @grant GM_info
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @run-at document-start
// @inject-into page
// ==/UserScript==
//
// Recommended in combination with Darkt, my darker theme for Trakt.
// More info on: https://git.io/Darkt
/* global $, NodeCreationObserver, GM_config, RequestQueue */
(() => {
'use strict'
console.log(`${GM_info.script.name} v${GM_info.script.version} by Felix is running!`)
// configuration
GM_config.init({
id: 'trakt-config',
title: `${GM_info.script.name} Settings`,
fields: {
apikey: {
label: 'TMDb API Key',
section: ['Enter your TMDb API Key', 'Get one at: https://developers.themoviedb.org/3/'],
type: 'text',
default: ''
},
language: {
label: 'Language',
section: ['Enter the code of your language, for example: en-US, it-IT, fr-FR.', 'More info at: https://developers.themoviedb.org/3/getting-started/languages'],
type: 'text',
default: 'en-US'
},
logging: {
label: 'Logging',
labelPos: 'above',
type: 'checkbox',
default: false
}
},
css: '#trakt-config{background-color:#343434;color:#fff}#trakt-config *{font-family:varela round,helvetica neue,Helvetica,Arial,sans-serif}#trakt-config .section_header{background-color:#282828;border:1px solid #282828;border-bottom:none;color:#fff;font-size:10pt}#trakt-config .section_desc{background-color:#282828;border:1px solid #282828;border-top:none;color:#fff;font-size:10pt}#trakt-config .reset{color:#fff}',
events: {
save: () => {
alert(`${GM_info.script.name} : Settings saved`)
location.reload(false)
}
}
})
// menu command to open configuration
GM_registerMenuCommand(`${GM_info.script.name} - Configure`, () => {
GM_config.open()
})
// logs
const log = message => {
if (GM_config.get('logging') === true) {
console.log(`${GM_info.script.name}: ${message}`)
}
}
// NodeCreationObserver
NodeCreationObserver.init('observed-translate')
NodeCreationObserver.onCreation('.movies .external a[href*="themoviedb"]', () => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.themoviedb.org/3/movie/${TMDbID()}?api_key=${apikey()}&language=${language()}`,
onload: response => {
const data = JSON.parse(response.responseText)
if (data && data.status_message) {
error(data.status_message)
} else {
if (data && data.poster_path && data.poster_path !== null) {
translatePoster(`https://image.tmdb.org/t/p/w185${data.poster_path}`)
}
if (data && data.title) {
translateTitle(data.title)
}
if (data && data.tagline && data.tagline !== '') {
translateTagline(data.tagline)
}
if (data && data.overview && data.overview !== '') {
translateOverview(data.overview)
}
}
}
})
})
NodeCreationObserver.onCreation('.shows:not(.season):not(.episode) .external a[href*="themoviedb"]', () => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.themoviedb.org/3/tv/${TMDbID()}?api_key=${apikey()}&language=${language()}`,
onload: response => {
const data = JSON.parse(response.responseText)
if (data && data.status_message) {
error(data.status_message)
} else {
if (data && data.poster_path && data.poster_path !== null) {
translatePoster(`https://image.tmdb.org/t/p/w185${data.poster_path}`)
}
if (data && data.name) {
translateTitle(data.name)
}
if (data && data.overview && data.overview !== '') {
translateOverview(data.overview)
}
}
}
})
})
NodeCreationObserver.onCreation('.season .external a[href*="themoviedb"]', () => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://api.themoviedb.org/3/tv/${TMDbID()}?api_key=${apikey()}&language=${language()}`,
onload: response => {
const data = JSON.parse(response.responseText)
if (data && data.status_message) {
error(data.status_message)
} else {
const sn = season()
if (data && data.seasons[sn].poster_path && data.seasons[sn].poster_path !== null) {
translatePoster(`https://image.tmdb.org/t/p/w185${data.seasons[sn].poster_path}`)
}
if (data && data.name) {
translateSeriesTitle(data.name)
}
if (data && data.seasons[sn].name) {
translateTitle(data.seasons[sn].name)
}
if (data && data.seasons[sn].overview && data.seasons[sn].overview !== '') {
translateOverview(data.seasons[sn].overview)
}
}
}
})
})
NodeCreationObserver.onCreation('.episode .external a[href*="themoviedb"]', () => {
const rq = new RequestQueue(1)
const id = TMDbID()
const ak = apikey()
const ln = language()
const sn = season()
const en = episode()
rq.add({
method: 'GET',
url: `https://api.themoviedb.org/3/tv/${id}?api_key=${ak}&language=${ln}`,
onload: response => {
const data = JSON.parse(response.responseText)
if (data && data.status_message) {
log(`error is "${data.status_message}"`)
} else {
if (data && data.seasons[sn].poster_path && data.seasons[sn].poster_path !== null) {
translatePoster(`https://image.tmdb.org/t/p/w185${data.seasons[sn].poster_path}`)
}
if (data && data.name && data.seasons[sn].name) {
translateSeasonTitle(data.name, data.seasons[sn].name)
}
}
}
})
rq.add({
method: 'GET',
url: `https://api.themoviedb.org/3/tv/${id}/season/${sn}/episode/${en}?api_key=${ak}&language=${ln}`,
onload: response => {
const data = JSON.parse(response.responseText)
if (data && data.status_message) {
error(data.status_message)
} else {
if (data && data.name) {
translateEpisodeTitle(data.name)
}
if (data && data.overview && data.overview !== '') {
translateOverview(data.overview)
}
}
}
})
})
// translate overview
function translateOverview (overview) {
log(`overview is "${short(overview)}"`)
$('#info-wrapper .info #overview p').text(overview)
log('overview translated')
}
// translate tagline
function translateTagline (tagline) {
log(`tagline is "${short(tagline)}"`)
$('#info-wrapper .info #tagline').text(tagline)
log('tagline translated')
}
// translate title
function translateTitle (title) {
log(`title is "${title}"`)
const container = $('#summary-wrapper .summary .container h1')
const year = container.find('.year')
const certification = container.find('.certification')
container.text(title).append(' ').append(year).append(certification)
log('title translated')
}
function translateSeriesTitle (seriesTitle) {
log(`series title is "${seriesTitle}"`)
$('#summary-wrapper .summary .container h2 a').text(seriesTitle)
log('series title translated')
}
function translateSeasonTitle (title, seasonTitle) {
log(`season title is "${title}: ${seasonTitle}"`)
$('#summary-wrapper .summary .container h2 a:first-child').text(title).append(': ')
$('#summary-wrapper .summary .container h2 a:last-child').text(seasonTitle)
log('season title translated')
}
function translateEpisodeTitle (episodeTitle) {
log(`episode title is "${episodeTitle}"`)
$('#summary-wrapper .summary .container h1 .main-title').text(episodeTitle)
log('episode title translated')
}
// translate poster
function translatePoster (poster) {
log(`poster url is "${poster}"`)
$('#info-wrapper .sidebar .poster .real').removeAttr('data-original').removeAttr('src').attr('src', poster)
$('#summary-wrapper .mobile-poster .poster .real').removeAttr('data-original').removeAttr('src').attr('src', poster)
log('poster translated')
}
// error
function error (error) {
const log = `error is "${error}"`
if (error === 'Invalid API key: You must be granted a valid key.') {
alert(`${GM_info.script.name}: ${log}`)
GM_config.open()
} else {
log(log)
}
}
// shorten logs
const short = log => {
return log.split(/\s+/).slice(0, 6).join(' ').concat(' [...]')
}
// get language
function language () {
const language = GM_config.get('language')
if (language === '') {
alert(`${GM_info.script.name}: error "Language not set"`)
GM_config.open()
} else {
log(`language is "${language}"`)
return language
}
}
// get API Key
function apikey () {
const apikey = GM_config.get('apikey')
if (apikey === '') {
GM_config.open()
} else {
log(`TMDb API Key is "${apikey}"`)
return apikey
}
}
// get episode
function episode () {
const link = $('.external a[href*="themoviedb"]')
if (link.length) {
const episode = link.attr('href').match(/(episode)\/(\d+)/)[2]
log(`episode is "${episode}"`)
return episode
}
}
// get season
function season () {
const link = $('.external a[href*="themoviedb"]')
if (link.length) {
const season = link.attr('href').match(/(season)\/(\d+)/)[2]
log(`season is "${season}"`)
return season
}
}
// get TMDb ID
function TMDbID () {
const link = $('.external a[href*="themoviedb"]')
if (link.length) {
const TMDbID = link.attr('href').match(/(movie|tv)\/(\d+)/)[2]
log(`TMDb ID is "${TMDbID}"`)
return TMDbID
}
}
})()