Skip to content

Instantly share code, notes, and snippets.

@microraptor
Last active November 11, 2019 04:33
Show Gist options
  • Save microraptor/d3eb8fc43dd1087b0eb5b1b40eb346af to your computer and use it in GitHub Desktop.
Save microraptor/d3eb8fc43dd1087b0eb5b1b40eb346af to your computer and use it in GitHub Desktop.
<!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