Last active
November 11, 2019 04:33
-
-
Save microraptor/d3eb8fc43dd1087b0eb5b1b40eb346af to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
<meta name="description" content="Quota"> | |
<title>Show Calendar</title> | |
<link href="https://fonts.googleapis.com/css?family=VT323" rel="stylesheet"> | |
<style> | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
font-family: 'VT323', monospace; | |
min-height: 97vh; | |
font-size: 24px; | |
color: #C0C0C0; | |
text-shadow: 0 0 5px #0000001A, 0 1px 3px #00000050, 0 3px 5px #00000030, 0 5px 1px #0000001A, 0 5px 10px #00000040, 0 10px 10px #00000030; | |
background-color: #303030; | |
background-image: url("data:image/svg+xml,%3Csvg width='84' height='48' viewBox='0 0 84 48' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h12v6H0V0zm28 8h12v6H28V8zm14-8h12v6H42V0zm14 0h12v6H56V0zm0 8h12v6H56V8zM42 8h12v6H42V8zm0 16h12v6H42v-6zm14-8h12v6H56v-6zm14 0h12v6H70v-6zm0-16h12v6H70V0zM28 32h12v6H28v-6zM14 16h12v6H14v-6zM0 24h12v6H0v-6zm0 8h12v6H0v-6zm14 0h12v6H14v-6zm14 8h12v6H28v-6zm-14 0h12v6H14v-6zm28 0h12v6H42v-6zm14-8h12v6H56v-6zm0-8h12v6H56v-6zm14 8h12v6H70v-6zm0 8h12v6H70v-6zM14 24h12v6H14v-6zm14-8h12v6H28v-6zM14 8h12v6H14V8zM0 8h12v6H0V8z' fill='%23404040' fill-opacity='0.2' fill-rule='evenodd'/%3E%3C/svg%3E"); | |
/* Background Pattern by Steve Schoger www.heropatterns.com */ | |
} | |
main { | |
width: auto; | |
text-align: center; | |
margin: auto; | |
} | |
footer { | |
justify-self: flex-end; | |
} | |
ul { | |
text-align: left; | |
} | |
td { | |
padding: 8px; | |
} | |
a:link, a:visited { | |
text-decoration: none; | |
} | |
.episodes a:link, .episodes a:visited { | |
color: inherit; | |
} | |
.gray { | |
color: #606060; | |
} | |
.episodes { | |
align-items: flex-start; | |
text-align: right; | |
margin: auto; | |
} | |
.episode-title { | |
text-align: left; | |
} | |
.thisWeek { | |
color: #3090C0; | |
} | |
.upcoming .episode-show { | |
color: #C03030; | |
} | |
.current .episode-show { | |
color: #C0C030; | |
} | |
.past .episode-show { | |
color: #30C060; | |
} | |
.premiere .episode-number { | |
color: #C00090; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
<table class="episodes"></table> | |
</main> | |
<footer class="date-clock gray"></footer> | |
<script type="text/javascript"> | |
const MONDAY_IS_WEEK_START = true; | |
const CAL_PERIOD = '21'; // 3 weeks | |
const EXTENDED_CAL_PERIOD = '42'; // Seems to max out API | |
const MAX_TITLE_LENGTH = 25; | |
const WEEK_DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; | |
const DAY_IN_MS = 1000 * 60 * 60 * 24; | |
let windowUrl = new URL(window.location.href); | |
let tableHtml = ''; | |
let currentDate = new Date(Date.now()); | |
let firstDayLastWeek = new Date(currentDate.getTime() - currentDate.getTime() % DAY_IN_MS + currentDate.getTimezoneOffset() * 1000 * 60); // Discard time of the day | |
firstDayLastWeek.setDate(firstDayLastWeek.getDate() - 7 - (firstDayLastWeek.getDay() + MONDAY_IS_WEEK_START * 6) % 7); // Go to start of the last week | |
function getEpisodes(_startDate, _period) { | |
fetch('https://api.trakt.tv/calendars/my/shows/' + _startDate.toISOString().split('T')[0] + '/' + _period, { | |
method: 'get', | |
mode: 'cors', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': 'Bearer ' + localStorage.getItem('accessToken'), | |
'trakt-api-version': '2', | |
'trakt-api-key': localStorage.getItem('clientId') | |
}, | |
}) | |
.then(response => response.json()).then(newEpisodes => { | |
for (let episode of newEpisodes) { | |
showTitle = episode.show.title; | |
if (showTitle && showTitle.length > MAX_TITLE_LENGTH) { | |
showTitle = showTitle.substring(0, MAX_TITLE_LENGTH - 3) + '...'; | |
} | |
if (episode.episode.title && episode.episode.title.length > MAX_TITLE_LENGTH) { | |
episode.episode.title = episode.episode.title.substring(0, MAX_TITLE_LENGTH - 3) + '...'; | |
} | |
tableHtml += '<tr class="'; | |
airDate = new Date(episode.first_aired); | |
if (airDate.getTime() > firstDayLastWeek.getTime() + DAY_IN_MS * 7 | |
&& airDate.getTime() < firstDayLastWeek.getTime() + DAY_IN_MS * 7 * 2) { // In this week | |
tableHtml += 'thisWeek'; | |
} | |
if (airDate.getTime() - currentDate.getTime() > 0 && airDate.getTime() - currentDate.getTime() < DAY_IN_MS * 2) { // In next 2 days | |
tableHtml += ' upcoming'; | |
} else if (airDate.getTime() - currentDate.getTime() <= 0 && currentDate.getTime() - airDate.getTime() < DAY_IN_MS / 8) { // In past 3 hours | |
tableHtml += ' current'; | |
} else if (currentDate.getTime() - airDate.getTime() >= DAY_IN_MS / 8 && currentDate.getTime() - airDate.getTime() < DAY_IN_MS * 2) { // In past 2 days | |
tableHtml += ' past'; | |
} | |
if (episode.episode.number == 1) { // Episode is a premiere | |
tableHtml += ' premiere'; | |
} | |
tableHtml += '">' | |
+ '<td class="episode-date gray">' + airDate.getDate() + '.' + (airDate.getMonth() + 1) + '.' + airDate.getFullYear().toString().slice(-2) | |
+ '<td class="episode-day">' + WEEK_DAYS[airDate.getDay()] + '</td>' | |
+ '<td class="episode-time">' + airDate.getHours() + '<span class="gray">:</span>' + ('0' + airDate.getMinutes()).slice(-2) + '</td>' | |
+ '<td class="episode-show"><a href="https://www.imdb.com/title/' + episode.show.ids.imdb + '">' + showTitle + '</a></td>' | |
+ '<td class="episode-number">' + episode.episode.season + '<span class="gray">x</span>' + ('0' + episode.episode.number).slice(-2) + '</td>' | |
+ '<td class="episode-title gray">' + episode.episode.title + '</td>' | |
+ '</tr>'; | |
} | |
for (let element of document.getElementsByClassName('episodes')) element.innerHTML = tableHtml; | |
}); | |
} | |
function getAccessToken() { | |
if (localStorage.getItem('clientId') === 'REPLACE_THIS_WITH_CLIENT_ID' | |
|| localStorage.getItem('clientSecret') === 'REPLACE_THIS_WITH_CLIENT_SECRET' | |
|| localStorage.getItem('redirectUri') === 'REPLACE_THIS_WITH_REDIRECT_URI' | |
|| localStorage.getItem('refreshToken') === 'REPLACE_THIS_WITH_REFRESH_TOKEN') { | |
showInstructions(); | |
} else { | |
fetch('https://api.trakt.tv/oauth/token', { | |
method: 'post', | |
mode: 'no-cors', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
'refresh_token': localStorage.getItem('clientId'), | |
'client_id': localStorage.getItem('clientSecret'), | |
'client_secret': localStorage.getItem('refreshToken'), | |
'redirect_uri': localStorage.getItem('redirectUri'), | |
'grant_type': 'refresh_token' | |
}) | |
}) | |
.then(response => response.json()).then(accessToken => { | |
console.log(accessToken); | |
getEpisodes(firstDayLastWeek, CAL_PERIOD); | |
}); | |
} | |
} | |
function showInstructions() { | |
let instructions = '<ul><li>Sign up at trakt.tv and add your shows</li>' | |
+ '<li>Create an API app</li>' | |
+ '<li><a href="https://trakt.tv/oauth/applications">trakt.tv/oauth/applications</a></li>' | |
+ '<li>For CORS set "null", if you open this webpage locally or the domain, if the page is on a webserver</li>' | |
+ '<li>Use the authorize button on the API app overview, to get the auth code from the url</li>' | |
+ '<li>Go to the Get Token API docs console</li>' | |
+ '<li><a href="https://trakt.docs.apiary.io/#reference/authentication-oauth/get-token/exchange-code-for-access_token?console=1">' | |
+ 'trakt.docs.apiary.io/#reference/authentication-oauth/get-token/exchange-code-for-access_token?console=1</a></li>' | |
+ '<li>In the "Body" tab replace the values for the "code", "client_id", "client_secret" and "redirect_uri" with your own and press "Call Resource"</li>' | |
+ '<li>From the "Response Body" copy the "access_token" and "refresh_token"</li>' | |
+ '<li>Open the developer options of your browser on this page and replace the values of the locale storage entries for' | |
+ ' "clientId", "clientSecret", "redirectUri", "access_token" and "refreshToken" with your own and reload the page</li>' | |
+ '<li>In Chrome/Firefox you can do this by pressing "F12" and going to "Application/Storage -> Local Storage"</li>' | |
+ '<li>You might need to enable those tabs in the developer options settings</li></ul>'; | |
for (let element of document.getElementsByClassName('episodes')) element.innerHTML = instructions; | |
} | |
if (windowUrl.searchParams.get('clientId')) { | |
localStorage.setItem('clientId', windowUrl.searchParams.get('clientId')); | |
} | |
if (windowUrl.searchParams.get('clientSecret')) { | |
localStorage.setItem('clientSecret', windowUrl.searchParams.get('clientSecret')); | |
} | |
if (windowUrl.searchParams.get('redirectUri')) { | |
localStorage.setItem('redirectUri', windowUrl.searchParams.get('redirectUri')); | |
} | |
if (windowUrl.searchParams.get('accessToken')) { | |
localStorage.setItem('accessToken', windowUrl.searchParams.get('accessToken')); | |
} | |
if (windowUrl.searchParams.get('refreshToken')) { | |
localStorage.setItem('refreshToken', windowUrl.searchParams.get('refreshToken')); | |
} | |
if (!localStorage.getItem('clientId')) { | |
localStorage.setItem('clientId', 'REPLACE_THIS_WITH_CLIENT_ID'); | |
} | |
if (!localStorage.getItem('clientSecret')) { | |
localStorage.setItem('clientSecret', 'REPLACE_THIS_WITH_CLIENT_SECRET'); | |
} | |
if (!localStorage.getItem('redirectUri')) { | |
localStorage.setItem('redirectUri', 'REPLACE_THIS_WITH_REDIRECT_URI'); | |
} | |
if (!localStorage.getItem('accessToken')) { | |
localStorage.setItem('accessToken', 'REPLACE_THIS_WITH_ACCESS_TOKEN'); | |
} | |
if (!localStorage.getItem('refreshToken')) { | |
localStorage.setItem('refreshToken', 'REPLACE_THIS_WITH_REFRESH_TOKEN'); | |
} | |
if (localStorage.getItem('clientId') === 'REPLACE_THIS_WITH_CLIENT_ID' | |
|| localStorage.getItem('accessToken') === 'REPLACE_THIS_WITH_ACCESS_TOKEN') { | |
showInstructions(); | |
} else { | |
getEpisodes(firstDayLastWeek, CAL_PERIOD); | |
} | |
for (let element of document.getElementsByClassName('date-clock')) { | |
element.innerText = currentDate.toDateString() + ', ' + currentDate.toLocaleTimeString(); | |
element.addEventListener('click', () => getEpisodes(new Date(firstDayLastWeek.getTime() + DAY_IN_MS * CAL_PERIOD), EXTENDED_CAL_PERIOD), {once: true}); // Extend calendar | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment