diff options
Diffstat (limited to 'assets/js/main.js')
-rwxr-xr-x | assets/js/main.js | 595 |
1 files changed, 199 insertions, 396 deletions
diff --git a/assets/js/main.js b/assets/js/main.js index 42ff881..76b9544 100755 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,414 +1,217 @@ /** - * Clientside Script of the Netflix Stats Generator + * Client-side Script of the Netflix Stats Generator * @author Marvin Borner - * @copyright Marvin Borner 2018 + * @copyright Marvin Borner 2019 */ -$(() => { - const DebuggingMode = true; - const CookieInput = $(".CookieInput"); - let NetflixJson; - - moment.locale("de"); - moment().utcOffset(0); // offset for unix timestamp - - if (!DebuggingMode) { - CookieInput.keyup(e => { - if (e.keyCode === 13) { - $.ajax({ - url: "assets/php/getNetflixJson.php", - data: { - Cookie: CookieInput.val() - }, - type: "POST" - }).done(response => { - CookieInput.val(""); - CookieInput.hide(); - $(".Main").fadeIn(); - AnalyzeData(response); - }); - } - }); - } else { - CookieInput.hide(); - $.ajax({ - url: "assets/js/ExampleData.js", - type: "POST" - }).done(response => { - $(".Main").fadeIn(); - AnalyzeData(response); - }); - } - - /** - * Analyzes the Netflix data JSON response - * @param {JSON} JsonResponse - */ - function AnalyzeData(JsonResponse) { - /** - * @example response of a series: - * bookmark: 0 - * country: "DE" - * date: 1529338765489 - * dateStr: "18.06.18" - * deviceType: 1481 - * duration: 3302 - * episodeTitle: "Folge 13" - * estRating: "50" - * index: 0 - * movieID: 80205354 - * seasonDescriptor: "Teil 1" - * series: 80192098 - * seriesTitle: "Haus des Geldes" - * title: "Teil 1: \"Folge 13\"" - * topNodeId: "80192098" - * videoTitle: "Folge 13" - * - * @example response of a movie: - * bookmark: 7771 - * country: "DE" - * date: 1476477258019 - * dateStr: "14.10.16" - * deviceType: 1193 - * duration: 8160 - * estRating: "30" - * index: 916 - * movieID: 20557937 - * title: "Matrix" - * topNodeId: "20557937" - * videoTitle: "Matrix" - */ - NetflixJson = JSON.parse(JsonResponse); - console.log(NetflixJson); - let EveryWatched = []; - let TitleWatchTime = {}; //how long you watched a series/movies - let HeatmapDatesAll = []; - let HeatmapDates = []; - let IndividualTitles = []; - let IndividualSeries = []; - let IndividualMovies = []; - let AverageWatchTimes = []; // when you watched a series/movie - - NetflixJson.forEach((item, pageKey) => { - item.forEach((eachItem, ItemNumber) => { - const currentObject = NetflixJson[pageKey][ItemNumber]; - let currentTitle; // will be overriden by 'if series' - - if ("seriesTitle" in eachItem) { - // is series - currentTitle = currentObject.seriesTitle; - EveryWatched.push(currentTitle); - if ( - IndividualSeries.indexOf(currentTitle) === -1 && - currentTitle !== undefined - ) { - // only if not already crawled -> individualism - IndividualSeries.push(currentTitle); - } - } else { - // is movie - currentTitle = currentObject.videoTitle; - EveryWatched.push(currentTitle); - if ( - IndividualMovies.indexOf(currentTitle) === -1 && - currentTitle !== undefined - ) { - // only if not already crawled -> individualism - IndividualMovies.push(currentTitle); - } - } - - // individualism check for every title - if ( - IndividualMovies.indexOf(currentTitle) === -1 && - currentTitle !== undefined - ) { - if (!(IndividualTitles.includes(currentTitle))) IndividualTitles.push(currentTitle); - - // get watch-time in hours (how long you watched a series/movies) - const watchTimeInHours = currentObject.duration / 60 / 60; - let watchTime; - if (currentTitle in TitleWatchTime) { - // already in object -> add to previous - const previousTitleWatchTime = TitleWatchTime[currentTitle]; - watchTime = watchTimeInHours + previousTitleWatchTime; - } else { - watchTime = watchTimeInHours; - } - TitleWatchTime[currentTitle] = watchTime; - } - - // get watch time as date (when you watched a series/movie) - const DayTimeInHours = Number(moment.unix(currentObject.date).format('HH')); - AverageWatchTimes.push(DayTimeInHours); - - // get dates and push to heatmap date array for later duplicate deletion - HeatmapDatesAll.push(currentObject.dateStr); - // HeatmapDates.push({ - // date: moment(currentObject.dateStr, 'DD.MM.YY').toDate(), - // count: 1 - // }); - }); - }); - - // calculate count of dates for heatmap chart - const HeatmapDatesOccurrenceCounter = new Map( - [...new Set(HeatmapDatesAll)].map(x => [ - x, - HeatmapDatesAll.filter(y => y === x).length // get length (=> occurrence) of filtered array - ]) - ); - - uniqueHeatmapDates = HeatmapDatesAll.filter(function (item, pos) { - return HeatmapDatesAll.indexOf(item) == pos; - }); - - uniqueHeatmapDates.forEach((index, val) => { - HeatmapDates.push({ - date: moment(uniqueHeatmapDates[val], 'DD.MM.YY').toDate(), - count: HeatmapDatesOccurrenceCounter.get(uniqueHeatmapDates[val]) - }); - }); - - - const TotalSeriesWatched = IndividualSeries.length; - - // Calculate watch time occurrence (times in which the user watched sth.) - let AverageWatchTimeOccurrence = []; - const WatchTimeOccurrenceCounter = new Map( - [...new Set(AverageWatchTimes)].map(x => [ - x, - AverageWatchTimes.filter(y => y === x).length // get length (=> occurrence) of filtered array - ]) - ); - for (let i = 0; i < 24; i++) { - AverageWatchTimeOccurrence.push(WatchTimeOccurrenceCounter.get(i)); - } - - // Calculate the most watched series/movies - const UnsortedTitleOccurrenceCounter = EveryWatched.reduce( - (prev, curr) => ((prev[curr] = ++prev[curr] || 1), prev), {} - ); - const SortedTitleOccurrenceCounter = sortObject( - UnsortedTitleOccurrenceCounter - ); - const TopSeries = Object.keys(SortedTitleOccurrenceCounter)[ - Object.keys(SortedTitleOccurrenceCounter).length - 1 - ]; - - // log - console.table(IndividualTitles); - console.table(IndividualSeries); - console.table(IndividualMovies); - console.table(AverageWatchTimeOccurrence); - console.table(SortedTitleOccurrenceCounter); - console.table(TitleWatchTime); - - // render - RenderTopSeries(TopSeries); - RenderDayTimeChart(AverageWatchTimeOccurrence); - RenderMostWatchedChart(SortedTitleOccurrenceCounter, TitleWatchTime); - RenderHeatmap(HeatmapDates); - } - - /** - * Renders the day time chart - * @param {Array} AverageWatchTimeOccurrenceArray - */ - function RenderDayTimeChart(AverageWatchTimeOccurrenceArray) { - var randomColorGenerator = () => { - return "#" + (Math.random().toString(16) + "0000000").slice(2, 8); +const cookie = document.querySelector("#cookie"); +const cookieWrap = document.querySelector("#cookie_wrap"); +const loading = document.querySelector("#loading"); +const stats = document.querySelector("#stats"); +const heatMap = document.querySelector("#heatMap"); + +cookie.addEventListener("keyup", e => { + if (e.key === "Enter") { + const request = new XMLHttpRequest(); + request.onreadystatechange = () => { + if (request.readyState === 4 && request.status === 200) { + analyze(request.responseText); + loading.style.display = "none"; + stats.style.display = "block"; + } else if (request.readyState === 4 && request.status !== 200) + alert("Cookie is not valid!") }; - - // Render day time chart - const WatchTimeChartElement = document - .getElementById("WatchTimeChart") - .getContext("2d"); - new Chart(WatchTimeChartElement, { - type: "line", - data: { - labels: [ - "12am", - "1am", - "2am", - "3am", - "4am", - "5am", - "6am", - "7am", - "8am", - "9am", - "10am", - "11am", - "12pm", - "1pm", - "2pm", - "3pm", - "4pm", - "5pm", - "6pm", - "7pm", - "8pm", - "9pm", - "10pm", - "11pm" - ], - datasets: [{ - label: "Watches at daytime", - borderColor: "rgb(255, 99, 132)", - cubicInterpolationMode: "monotone", - pointRadius: 0, - pointHitRadius: 15, - data: AverageWatchTimeOccurrenceArray - }] - }, - options: { - scales: { - yAxes: [{ - ticks: { - display: false - }, - gridLines: { - zeroLineColor: "transparent", - zeroLineWidth: 2, - drawTicks: false, - drawBorder: false, - color: "transparent" - } - }], - xAxes: [{ - gridLines: { - zeroLineColor: "rgba(255, 255, 255, 0.25)", - display: true, - drawBorder: false, - color: "rgba(255, 255, 255, 0.25)" - } - }] - }, - tension: 1 - } - }); + request.open("POST", "assets/php/getData.php", true); + request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + request.send("cookie=" + cookie.value); + loading.style.display = "block"; + cookieWrap.style.display = "none"; } - - /** - * Renders the "most watched series" doughnut chart - * @param {Object} TitleOccurrenceCounterObject - * @param {Object} TitleWatchTimeObject - */ - function RenderMostWatchedChart(TitleOccurrenceCounterObject, TitleWatchTimeObject) { - // Render and calculate most watched chart - const GenerateRandomColorArray = () => { - let RandomColorArray = []; - const Generate = () => { - var letters = "0123456789ABCDEF".split(""); - var color = "#"; - for (var i = 0; i < 6; i++) { - color += letters[Math.floor(Math.random() * 16)]; - } - return color; +}); + +function analyze(data) { + data = JSON.parse(data).flat(1); + let totalWatchedSeconds = 0; + const hourObject = Array(24).fill(0); + const watchCountObject = {}; + + data.forEach(element => { + let title; + const seriesTitle = element.seriesTitle; + const movieTitle = element.title; + const watchDate = element.date; + const duration = element.duration; + + // Generate watch time array (eg. 12am) + hourObject[(new Date(watchDate)).getHours()]++; + + if (seriesTitle !== undefined) title = seriesTitle; + else title = movieTitle; + + if (watchCountObject[title] !== undefined) { + watchCountObject[title].date.push(new Date(watchDate)); + watchCountObject[title].watchTimeInSeconds += duration; + watchCountObject[title].watchTime = secondsToHours(watchCountObject[title].watchTimeInSeconds); + watchCountObject[title].count++; + totalWatchedSeconds += duration + } else { + watchCountObject[title] = { + date: [new Date(watchDate)], + watchTime: secondsToHours(duration), + watchTimeInSeconds: duration, + count: 1 }; - for (var key in TitleOccurrenceCounterObject) { - RandomColorArray.push(Generate()); - } - return RandomColorArray; - }; - - const MostWatchedChartElement = document - .getElementById("MostWatchedChart") - .getContext("2d"); - var MostWatchedChartData = { - labels: [], + totalWatchedSeconds += duration; + } + }); + + renderTotalSpent(totalWatchedSeconds); + renderHourChart(hourObject); + renderTopChart(watchCountObject); + renderHeatMap(watchCountObject); + console.log(watchCountObject); +} + +function renderTotalSpent(total) { + document.querySelector("#totalSpent").innerHTML = ` + Days: ${Math.floor(total / 60 / 60 / 24)}, + Hours: ${Math.floor(total / 60 / 60)}, + Minutes: ${Math.round(total / 60)}, + Seconds: ${total}` +} + +function renderHourChart(hourObject) { + const element = document + .getElementById("hourChart") + .getContext("2d"); + + new Chart(element, { + type: "line", + data: { + labels: [ + "12am", + "1am", + "2am", + "3am", + "4am", + "5am", + "6am", + "7am", + "8am", + "9am", + "10am", + "11am", + "12pm", + "1pm", + "2pm", + "3pm", + "4pm", + "5pm", + "6pm", + "7pm", + "8pm", + "9pm", + "10pm", + "11pm" + ], datasets: [{ - label: "Most watched", - backgroundColor: GenerateRandomColorArray(), - data: [] + label: "Average watch times", + borderColor: "rgb(255, 99, 132)", + cubicInterpolationMode: "monotone", + pointRadius: 0, + pointHitRadius: 15, + data: hourObject }] - }; - Chart.pluginService.register({ - beforeInit: chart => { - var data = chart.config.data; - for (var key in TitleOccurrenceCounterObject) { - if (TitleOccurrenceCounterObject.hasOwnProperty(key)) { - if (TitleOccurrenceCounterObject[key] > 1) { - data.labels.push(`${key} (Time: ${Math.round(TitleWatchTimeObject[key] * 100) / 100} hours)`); // add label with rounded watch time - data.datasets[0].data.push(TitleOccurrenceCounterObject[key]); - } + }, + options: { + scales: { + yAxes: [{ + ticks: { + display: false } - } + }] + }, + legend: { + display: false } - }); - new Chart(MostWatchedChartElement, { - type: "doughnut", - data: MostWatchedChartData, - options: { - animation: { - animateScale: true, - animateRotate: true - }, - legend: { - display: false - } + } + }); +} + +function renderTopChart(object) { + const sorted = Object.keys(object).sort((a, b) => { + return object[b].watchTimeInSeconds - object[a].watchTimeInSeconds + }); + const data = sorted.map(element => object[element].watchTimeInSeconds); + const labels = sorted.map(element => { + return element + " (" + Math.floor(object[element].watchTimeInSeconds / 60 / 60) + " hours)" + }); + const colorArray = Array.from({length: data.length}, () => + "#" + ((1 << 24) * Math.random() | 0).toString(16)); + + const element = document + .getElementById("topChart") + .getContext("2d"); + + new Chart(element, { + type: 'doughnut', + data: { + datasets: [{ + data: data, + backgroundColor: colorArray + }], + labels: labels, + }, + options: { + animation: { + animateScale: true, + animateRotate: true + }, + legend: { + display: false } - }); + } + }); +} + +function renderHeatMap(object) { + const allDates = Object.keys(object).map(element => object[element].date).flat(10) + .map(element => element.setHours(0, 0, 0, 0)); + const watchedPerWeek = [[], [], [], [], [], [], []]; + + for (let i = 0; i < 366; i++) { + const date = new Date(); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + watchedPerWeek[date.getDay()].push(allDates.map(element => element === date.getTime()).filter(Boolean).length); } - /** - * Renders the top series in the DOM - * @param {String} Title - */ - function RenderTopSeries(Title) { - $.ajax({ - url: "assets/php/getInformation.php", - data: { - Title: Title - }, - type: "POST" - }).done(result => { - const TopInformation = JSON.parse(result); - $(".MostWatchedOverview > .MostWatchedPoster").attr( - "src", - `https://image.tmdb.org/t/p/w300${TopInformation.poster_path}` - ); - $(".MostWatchedOverview > .Description").text( - TopInformation.overview - ); - console.log(TopInformation); - }); - } + const maxWatchedPerDay = Math.max.apply(Math, watchedPerWeek.flat(2)); - /** - * Renders a heatmap of all watched days - * @param {Array} chartData - */ - function RenderHeatmap(chartData) { - const Heatmap = calendarHeatmap() - .data(chartData) - .selector('#Heatmap') - .colorRange(['#ffd3d3', '#fc1111']) - .tooltipEnabled(true) - .onClick(function (data) { - console.log('onClick callback. Data:', data); - }); + watchedPerWeek.map((element, i) => { + watchedPerWeek[i].push(["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][i]); + return watchedPerWeek[i].reverse(); + }); - Heatmap(); - } + watchedPerWeek.forEach(element => { + const tableRow = document.createElement("tr"); + element.forEach(count => { + const tableData = document.createElement("td"); + tableData.style.backgroundColor = "rgba(255,13,0," + count / maxWatchedPerDay + ")"; + if (typeof count !== "number") tableData.appendChild(document.createTextNode(count)); + tableRow.appendChild(tableData); - /** - * Sorts an js object by value {int} - * @param {Object} list - */ - function sortObject(list) { - var sortable = []; - for (var key in list) { - sortable.push([key, list[key]]); - } - sortable.sort((a, b) => { - return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0; + tableData.addEventListener("mouseover", () => { + document.querySelector("#information").innerText = `You've watched ${count} titles on that day!`; + }); }); - var orderedList = {}; - for (var i = 0; i < sortable.length; i++) { - orderedList[sortable[i][0]] = sortable[i][1]; - } - return orderedList; - } -});
\ No newline at end of file + + heatMap.appendChild(tableRow) + }) +} + +function secondsToHours(seconds) { + const date = new Date(null); + date.setSeconds(seconds); + return date.toISOString().substr(11, 8) +} |