First version
The first version of my script, modified to remove sensetive information.
29
README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Plex Wrapped 2020
|
||||
A website for collecting Plex user stats from 2020 using Tautulli.
|
||||
|
||||
## Warning!
|
||||
I made this for fun, in a very limited timeframe. This was not intended to be a public tool. It might not work with your system, the code is not perfect. My main concern is if your Tautulli is configured differently. I made this using my system, and tested it with my system. <b>Use this code as inspiration</b>.
|
||||
|
||||
## Instructions
|
||||
There are multiple settings you must configure!
|
||||
|
||||
<b>config.json</b><br>
|
||||
This file needs multiple values. Most importantly your Tautulli IP, port and API key. I made this using HTTP, if you use HTTPS you have to modify the API it.
|
||||
|
||||
<b>api/get_stats_2020.php</b><br>
|
||||
This file needs to know the ID of your movie and show library. It only supports two libraries. The ID can be found in your url when you open the library on the Tautulli website.
|
||||
|
||||
If you are lucky, it could be functional now.
|
||||
<br><br><br>
|
||||
If you want to change something:
|
||||
* The website is built in "get_stats_2020.js"
|
||||
* All values and processing is pulled in "/api/get_stats_2020.js"
|
||||
* The CSS is a mess and located in "/assets/css/main.css"
|
||||
|
||||
|
||||
## Need help?
|
||||
If you contact me I might have time to help you. Or maybe not. If not, many people on several forums (including /r/plex) might be able to assist you.
|
||||
|
||||
This idea was quite popular, so I might create a better, public solution in the future.
|
||||
|
||||
<b>Goodybye.</b>
|
228
api/get_stats_2020.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
$config = json_decode(file_get_contents("../config.json"));
|
||||
|
||||
//CONFIGURE THESE!
|
||||
$library_id_movies = 1;
|
||||
$library_id_shows = 2;
|
||||
//
|
||||
|
||||
$p_email = $data->p_email;
|
||||
$p_email = htmlspecialchars($p_email);
|
||||
$id = "fail";
|
||||
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_users";
|
||||
$response = json_decode(file_get_contents($url));
|
||||
|
||||
for ($i = 0; $i < count($response->response->data); $i++) {
|
||||
if ($response->response->data[$i]->email == $p_email) {
|
||||
$id = $response->response->data[$i]->user_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($id == "fail") {
|
||||
echo json_encode(array("message" => "No user found with that email", "error" => "true"));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Name
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_user_ips&user_id=" . $id;
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$name = $response->response->data->data[0]->friendly_name;
|
||||
|
||||
//LOG NAME
|
||||
$date = date('m/d/Y h:i:s a', time());
|
||||
$logg = "\n" . $date . " - " . $name . " (" . $p_email . ")";
|
||||
file_put_contents("log.txt", $logg, FILE_APPEND);
|
||||
|
||||
//MOVIES
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_history&user_id=" . $id . "§ion_id=" . $library_id_movies . "&order_column=full_title&order_dir=asc&length=10000";
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$array = $response->response->data->data;
|
||||
$movies = array();
|
||||
$movies_percent_complete = array();
|
||||
|
||||
for ($i = 0; $i < count($array); $i++) {
|
||||
$title = $array[$i]->full_title;
|
||||
$duration = $array[$i]->duration;
|
||||
$percent_complete = $array[$i]->percent_complete;
|
||||
$paused_counter = $array[$i]->paused_counter;
|
||||
|
||||
if(!($array[$i]->date > 1577836800 && $array[$i]->date < 1609459200)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if($duration > 300) {
|
||||
array_push($movies_percent_complete, $array[$i]->percent_complete);
|
||||
}
|
||||
|
||||
$found = False;
|
||||
|
||||
for ($j = 0; $j < count($movies); $j++) {
|
||||
if($movies[$j]["title"] == $title) {
|
||||
$movies[$j]["plays"] = intval($movies[$j]["plays"]) + 1;
|
||||
$movies[$j]["duration"] = intval($movies[$j]["duration"]) + intval($duration);
|
||||
$found = True;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$found) {
|
||||
array_push($movies, array("title" => $title, "plays" => 1, "duration" => $duration, "paused_counter" => $paused_counter));
|
||||
}
|
||||
}
|
||||
|
||||
//MOST PAUSED
|
||||
$paused_counter = array_column($movies, 'paused_counter');
|
||||
array_multisort($paused_counter, SORT_DESC, $movies);
|
||||
if(count($movies) > 0) {
|
||||
$movie_most_paused = array("title" => $movies[0]["title"], "paused_counter" => $movies[0]["paused_counter"]);
|
||||
} else {
|
||||
$movie_most_paused = array("title" => "No movies", "paused_counter" => 0);
|
||||
}
|
||||
|
||||
//SORT MOVIES BY DURATION
|
||||
$duration = array_column($movies, 'duration');
|
||||
array_multisort($duration, SORT_DESC, $movies);
|
||||
|
||||
//AVERAGE MOVIE PERCENT COMPLETE
|
||||
$sum = 0;
|
||||
for($i = 0; $i < count($movies_percent_complete); $i++) {
|
||||
$sum = $sum + $movies_percent_complete[$i];
|
||||
}
|
||||
if(count($movies_percent_complete) > 0) {
|
||||
$movie_percent_average = $sum / count($movies_percent_complete);
|
||||
} else {
|
||||
$movie_percent_average = 0;
|
||||
}
|
||||
|
||||
//SHOWS
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_history&user_id=" . $id . "§ion_id=" . $library_id_shows . "&order_column=full_title&order_dir=asc&length=10000";
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$array = $response->response->data->data;
|
||||
$shows = array();
|
||||
|
||||
for ($i = 0; $i < count($array); $i++) {
|
||||
$title = $array[$i]->grandparent_title;
|
||||
$duration = $array[$i]->duration;
|
||||
|
||||
if(!($array[$i]->date > 1577836800 && $array[$i]->date < 1609459200)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$found = False;
|
||||
|
||||
for ($j = 0; $j < count($shows); $j++) {
|
||||
if($shows[$j]["title"] == $title) {
|
||||
$shows[$j]["duration"] = intval($shows[$j]["duration"]) + intval($duration);
|
||||
$found = True;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$found) {
|
||||
array_push($shows, array("title" => $title, "duration" => $duration));
|
||||
}
|
||||
}
|
||||
|
||||
//SORT SHOWS BY DURATION
|
||||
$duration = array_column($shows, 'duration');
|
||||
array_multisort($duration, SORT_DESC, $shows);
|
||||
|
||||
//SHOW BUDDIES
|
||||
if(count($shows) > 0){
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_history§ion_id=" . $library_id_shows . "&order_column=full_title&order_dir=asc&length=10000&search=" . urlencode($shows[0]["title"]);
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$array = $response->response->data->data;
|
||||
$top_show_users = array();
|
||||
|
||||
for ($i = 0; $i < count($array); $i++) {
|
||||
$user = $array[$i]->friendly_name;
|
||||
$duration = $array[$i]->duration;
|
||||
|
||||
if(!((intval($array[$i]->date) > 1577836800 && intval($array[$i]->date) < 1609459200) && ($array[$i]->grandparent_title == $shows[0]["title"]))) {
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
$found = False;
|
||||
|
||||
for ($j = 0; $j < count($top_show_users); $j++) {
|
||||
if($top_show_users[$j]["user"] == $user) {
|
||||
$top_show_users[$j]["duration"] = intval($top_show_users[$j]["duration"]) + intval($duration);
|
||||
$found = True;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$found) {
|
||||
array_push($top_show_users, array("user" => $user, "duration" => $duration));
|
||||
}
|
||||
}
|
||||
|
||||
//SORT SHOW BUDDIES BY DURATION
|
||||
$duration = array_column($top_show_users, 'duration');
|
||||
array_multisort($duration, SORT_DESC, $top_show_users);
|
||||
|
||||
$index = 0;
|
||||
if(count($top_show_users) > 1) {
|
||||
for($i = 0; $i < count($top_show_users); $i++) {
|
||||
if($top_show_users[$i]["user"] == $name) {
|
||||
$index = $i;
|
||||
}
|
||||
}
|
||||
|
||||
if((($index == 0) || ($index % 2 == 0)) AND ($index < count($top_show_users)-1)) {
|
||||
$buddy = array("user" => $top_show_users[$index+1]["user"], "duration" => $top_show_users[$index+1]["duration"], "error" => False, "watched_relative_to_you" => "less");
|
||||
} else {
|
||||
$buddy = array("user" => $top_show_users[$index-1]["user"], "duration" => $top_show_users[$index-1]["duration"], "error" => False, "watched_relative_to_you" => "more");
|
||||
}
|
||||
|
||||
} else {
|
||||
$buddy = array("user" => "none", "duration" => 0, "error" => True, "watched_relative_to_you" => "none");
|
||||
}
|
||||
} else {
|
||||
$buddy = array("user" => "none", "duration" => 0, "error" => True, "watched_relative_to_you" => "none");
|
||||
}
|
||||
|
||||
//GET USERS LAST 365
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_plays_by_top_10_users&time_range=365&y_axis=duration";
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$top_users = $response->response->data->categories;
|
||||
|
||||
//GET TOP MOVIES AND SHOWS
|
||||
$url = "http://" . $config->ip . ":" . $config->plexpy_port . "/plexpy/api/v2?apikey=" . $config->plexpy_apikey . "&cmd=get_home_stats&time_range=365&stats_type=duration&grouping=1&stats_count=10";
|
||||
$response = json_decode(file_get_contents($url));
|
||||
$top_10_movies = array();
|
||||
$top_10_shows = array();
|
||||
|
||||
for($i = 0; $i < 10; $i++) {
|
||||
array_push($top_10_movies, array("title" => $response->response->data[0]->rows[$i]->title, "duration" => $response->response->data[0]->rows[$i]->total_duration, "plays" => $response->response->data[0]->rows[$i]->total_plays));
|
||||
array_push($top_10_shows, array("title" => $response->response->data[2]->rows[$i]->title, "duration" => $response->response->data[2]->rows[$i]->total_duration, "plays" => $response->response->data[2]->rows[$i]->total_plays));
|
||||
}
|
||||
|
||||
//SORT SHOW AND MOVIES BY DURATION
|
||||
$duration = array_column($top_10_movies, 'duration');
|
||||
array_multisort($duration, SORT_DESC, $top_10_movies);
|
||||
|
||||
$duration = array_column($top_10_shows, 'duration');
|
||||
array_multisort($duration, SORT_DESC, $top_10_shows);
|
||||
|
||||
|
||||
//PRINT VALUES
|
||||
echo json_encode(array( "name" => $name,
|
||||
"error" => False,
|
||||
"message" => "Data processed.",
|
||||
"movie_most_paused" => $movie_most_paused,
|
||||
"movie_percent_average" => $movie_percent_average,
|
||||
"top_movies" => $movies,
|
||||
"top_shows" => $shows,
|
||||
"show_buddy" => $buddy,
|
||||
"top_users" => $top_users,
|
||||
"top_10_movies" => $top_10_movies,
|
||||
"top_10_shows" => $top_10_shows
|
||||
));
|
0
api/log.txt
Normal file
346
assets/css/main.css
Normal file
|
@ -0,0 +1,346 @@
|
|||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
font-family: 'Roboto', serif;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 3em;
|
||||
color: #f9a825;
|
||||
text-shadow: 1px 1px 2px #555;
|
||||
line-height: 110%;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-overflow: clip;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h2{
|
||||
font-size: 2em;
|
||||
color: black;
|
||||
line-height: 110%;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h3{
|
||||
color: white;
|
||||
text-shadow: 1px 1px 2px #555;
|
||||
line-height: 110%;
|
||||
text-align: center;
|
||||
font-family: 'Roboto', serif;
|
||||
font-size: 1.5em;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
h4{
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
text-shadow: 0;
|
||||
}
|
||||
|
||||
p{
|
||||
font-size: 1em;
|
||||
color: black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.index {
|
||||
background-image: url("../assets/banner.webp") !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto;
|
||||
background-position: top;
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 100%;
|
||||
height: 28em;
|
||||
background-image: url("../img/backgrounds/interstellar.jpg") !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
#movies {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#shows {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#artists {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.tittel {
|
||||
padding-top: 10em;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.knapp {
|
||||
background-color: #f9a825;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2);
|
||||
align-self: center;
|
||||
width: 10em;
|
||||
margin: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.knapptekst {
|
||||
padding: 0.5em;
|
||||
font-size: 1.25em;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-family: 'Roboto', serif;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.boks {
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.boks2 {
|
||||
width: 30em;
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
max-width: 90%;
|
||||
margin-bottom: 2.5em;
|
||||
margin-top: 2.5em;
|
||||
top: 0;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.load_top_10 {
|
||||
width: 30em;
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
max-width: 90%;
|
||||
margin-bottom: 2.5em;
|
||||
margin-top: 2.5em;
|
||||
}
|
||||
|
||||
.tekst {
|
||||
padding: 1em;
|
||||
height: 14em;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.status_tekst {
|
||||
padding: 1em;
|
||||
height: 12em;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.stats_tekst {
|
||||
padding: 1em;
|
||||
height: 10em;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#stats {
|
||||
width: 22em;
|
||||
}
|
||||
|
||||
.boks-knapp {
|
||||
height: 5em;
|
||||
width: 100% !important;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.server:hover {
|
||||
background-color: white;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.19);
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 25px;
|
||||
padding: 0.5em;
|
||||
max-width: 25em;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#search_get {
|
||||
padding: 0.25em;
|
||||
font-family: 'Roboto', serif;
|
||||
background-color: #f9a825;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2);
|
||||
align-self: center;
|
||||
color: white;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.form {
|
||||
font-family: 'Roboto', serif;
|
||||
font-size: 1em;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.form_button {
|
||||
font-family: 'Roboto', serif;
|
||||
text-align: center;
|
||||
background-color: #f9a825;
|
||||
color: white;
|
||||
border: 0;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2);
|
||||
padding: 0.5em;
|
||||
font-size: 1em;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.form_input {
|
||||
font-family: 'Roboto', serif;
|
||||
font-size: 1em;
|
||||
width: 90%;
|
||||
margin: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background-color: white !important;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.19);
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 25px;
|
||||
padding: 0.5em;
|
||||
text-align: left;
|
||||
width: 10em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.number {
|
||||
text-align: right;
|
||||
margin: 0.1em;
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.info {
|
||||
text-align: left;
|
||||
margin: 0.1em;
|
||||
display: inline-block;
|
||||
width: 6em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.name {
|
||||
text-align: right;
|
||||
margin: 0.1em;
|
||||
display: inline-block;
|
||||
width: 8em;
|
||||
vertical: center;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.movie_name {
|
||||
width: 90%;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
margin: 0.1em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.info_name {
|
||||
width: 14em;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
margin: 0.1em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.server {
|
||||
width: 10em !important;
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.false {
|
||||
background-color: red;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.19);
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 0.25em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.false:hover {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.true {
|
||||
background-color: lightgreen;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.19);
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 0.25em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.true:hover {
|
||||
background-color: lightgreen !important;
|
||||
}
|
BIN
assets/img/favicons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/img/favicons/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
assets/img/favicons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/img/favicons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/img/favicons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/img/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
assets/img/favicons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 12 KiB |
222
get_stats_2020.js
Normal file
|
@ -0,0 +1,222 @@
|
|||
|
||||
var results;
|
||||
|
||||
var p_email = document.getElementById("p_email").value;
|
||||
|
||||
var p_email = p_email.replace(/[&\/\\#,+()$~%:*?<>{}]/g, '');
|
||||
|
||||
stats_form = {
|
||||
"p_email" : p_email
|
||||
};
|
||||
|
||||
stats_data = JSON.stringify(stats_form);
|
||||
|
||||
$.ajax({
|
||||
url: "api/get_stats_2020.php",
|
||||
type : "POST",
|
||||
contentType : 'application/json',
|
||||
async: false,
|
||||
data : stats_data,
|
||||
success : function(result) {
|
||||
|
||||
results = JSON.parse(result);
|
||||
if(results.error == "true") {
|
||||
$('#results_error').html(results.message);
|
||||
search_button("SEARCH");
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
},
|
||||
|
||||
// show error message to user
|
||||
error: function(){
|
||||
$('#results_error').html("Error, nådde ikke API... " + p_email);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if(results.error == "true") {
|
||||
$('#results_error').html(results.message);
|
||||
exit;
|
||||
}
|
||||
|
||||
var search_box = document.getElementById("search_input");
|
||||
search_box.style.display = "none";
|
||||
|
||||
var text = "";
|
||||
|
||||
//INTRODUCTION
|
||||
text += "<div class='boks' style='width: 100%; padding-bottom: 25em; padding-top: 25em; height:auto; background-color:#add8e6;'>";
|
||||
text += "<h2 style='font-size:3em;'>Hey there, " + results.name + "!</h2><h2>";
|
||||
text += "<br><br>2020 kinda sucked, but hopefully you watched some cool stuff...</p>"
|
||||
text += "</div>";
|
||||
|
||||
//MOVIES
|
||||
text += "<div class='boks' style='height: auto !important; width: 100%; padding-bottom: 25em; padding-top: 25em; height:10em; background-color:#f7efd2;'>";
|
||||
text += "<h2>Let's look at some movies.";
|
||||
text += "<br><br><br>You watched " + results.top_movies.length + " movies. That's a lot of movies!</h2><br>(or not, I am pre-programmed to say that)"
|
||||
text += "<br><br>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3'>";
|
||||
text += "<div class='stats'>";
|
||||
text += "<h4>Your top 10 movies in 2020</h4>";
|
||||
for(i = 0; (i < results.top_movies.length && i < 10); i++) {
|
||||
text += "<div class='item'>";
|
||||
text += "<div class='number'>";
|
||||
text += i+1 + ". ";
|
||||
text += "</div>";
|
||||
text += "<div class='movie_name'>";
|
||||
text += results.top_movies[i].title;
|
||||
var movie_hour = time_hours(results.top_movies[i].duration)
|
||||
text += " - " + movie_hour[0] + " hours, " + movie_hour[1] + " minutes";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3' style='padding:2.5em;>";
|
||||
text += "<div class='stats'>";
|
||||
var str = JSON.stringify(results.movie_percent_average);
|
||||
var percent = str.split('.');
|
||||
text += "<h4>Your average movie finishing percentage in 2020 was " + percent[0] + "%</h4>";
|
||||
text += "<br><br>You're not watching the credits like a nerd, are you?";
|
||||
text += "</div>";
|
||||
|
||||
text += "<div class='status' id='list3' style='margin-top:2.5em; padding:2.5em;>";
|
||||
text += "<div class='stats'>";
|
||||
text += "Your longest movie pause was watching <br><br><h4>" + results.movie_most_paused.title + ".</h4>";
|
||||
var str = JSON.stringify(results.movie_most_paused.paused_counter / 60);
|
||||
var minutes = str.split('.');
|
||||
text += "<br>It was paused for " + minutes[0] + " minutes...";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
//SHOWS
|
||||
text += "<div class='boks' style='height: auto !important; width: 100%; padding-bottom: 25em; padding-top: 25em; height:10em; background-color:#eed7a1;'>";
|
||||
text += "<h2>Now, let's have a look at some shows!";
|
||||
text += "<br><br><br>You watched " + results.top_shows.length + " different shows.</h2><br>(No, watching The Office twice in a year doesn't count as two shows)"
|
||||
text += "<br><br>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3'>";
|
||||
text += "<div class='stats'>";
|
||||
text += "<h4>Your top 10 shows in 2020</h4>";
|
||||
for(i = 0; (i < results.top_shows.length && i < 10); i++) {
|
||||
text += "<div class='item'>";
|
||||
text += "<div class='number'>";
|
||||
text += i+1 + ". ";
|
||||
text += "</div>";
|
||||
text += "<div class='movie_name'>";
|
||||
text += results.top_shows[i].title;
|
||||
var show_hour = time_hours(results.top_shows[i].duration)
|
||||
text += " - " + show_hour[0] + " hours, " + show_hour[1] + " minutes";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
if(results.top_shows.length > 0) {
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3' style='padding:2.5em;>";
|
||||
text += "<div class='stats'>";
|
||||
var str = JSON.stringify(results.movie_percent_average);
|
||||
var percent = str.split('.');
|
||||
text += "<h4>Your top show was " + results.top_shows[0].title + "</h4><br>";
|
||||
var buddy_error = JSON.stringify(results.show_buddy.error);
|
||||
if(buddy_error == "true") {
|
||||
text += "<br>That means you're a hipster, because you're the only viewer of that show in 2020 😎";
|
||||
} else {
|
||||
text += "And you're not alone! Your " + results.top_shows[0].title + "-buddy is ";
|
||||
text += "<b>" + results.show_buddy.user + "!</b><br><br>";
|
||||
var combined = parseInt(results.show_buddy.duration) + parseInt(results.top_shows[0].duration);
|
||||
var combined_2 = time_hours(combined);
|
||||
text += "Your combined efforts resulted in <b>" + combined_2[0] + " hours and " + combined_2[1] + " minutes</b> of " + results.top_shows[0].title + "!</b><br><br>😎";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
|
||||
|
||||
//TOP USERS
|
||||
text += "<div class='boks' style='height: auto !important; width: 100%; padding-bottom: 25em; padding-top: 25em; height:10em; background-color: #f7efd2;'>";
|
||||
text += "<h2>Finally, let's look at the top users, movies and shows in 2020!";
|
||||
text += "<br><br><br><br>It's okay to feel shame if you are on the list.</h2>(or missing from it...)"
|
||||
text += "<br><br>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3'>";
|
||||
text += "<div class='stats'>";
|
||||
text += "<h4>Top users from the past year</h4>";
|
||||
for(i = 0; i < 10; i++) {
|
||||
text += "<div class='item'>";
|
||||
text += "<div class='number'>";
|
||||
text += i+1 + ". ";
|
||||
text += "</div>";
|
||||
text += "<div class='name'>";
|
||||
text += results.top_users[i];
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3'>";
|
||||
text += "<div class='stats'>";
|
||||
text += "<h4>Top movies from the past year</h4>";
|
||||
for(i = 0; i < 10; i++) {
|
||||
text += "<div class='item'>";
|
||||
text += "<div class='number'>";
|
||||
text += i+1 + ". ";
|
||||
text += "</div>";
|
||||
text += "<div class='movie_name'><b>";
|
||||
text += results.top_10_movies[i].title;
|
||||
var movie_hour = time_hours(results.top_10_movies[i].duration)
|
||||
text += "</b><br>" + movie_hour[0] + " hours and " + movie_hour[1] + " minutes<br>" + results.top_10_movies[i].plays + " plays";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
text += "<div class='boks2'>";
|
||||
text += "<div class='status' id='list3'>";
|
||||
text += "<div class='stats'>";
|
||||
text += "<h4>Top shows from the past year</h4>";
|
||||
for(i = 0; i < 10; i++) {
|
||||
text += "<div class='item'>";
|
||||
text += "<div class='number'>";
|
||||
text += i+1 + ". ";
|
||||
text += "</div>";
|
||||
text += "<div class='movie_name'><b>";
|
||||
text += results.top_10_shows[i].title;
|
||||
var show_hour = time_hours(results.top_10_shows[i].duration)
|
||||
text += "</b><br>" + show_hour[0] + " hours and " + show_hour[1] + " minutes<br>" + results.top_10_shows[i].plays + " plays";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
}
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
text += "</div>";
|
||||
|
||||
text += "</div>";
|
||||
|
||||
|
||||
//SHOWS
|
||||
text += "<div class='boks' style='height: auto !important; width: 100%; padding-bottom: 25em; padding-top: 25em; height:10em; background-color:#cd8b62;'>";
|
||||
text += "<h2>Hope you are staying safe!<br><br><br><br>Goodybye.</h2>";
|
||||
text += "</div>";
|
||||
|
||||
$('#search_results').html(text);
|
||||
|
||||
search_button("SEARCH");
|
87
index.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
|
||||
<title>Plex Wrapped 2020</title>
|
||||
|
||||
<!-- Bootstrap 4 CSS and custom CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/main.css" />
|
||||
<link rel="shortcut icon" href="assets/img/favicons/favicon.ico" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="boks">
|
||||
|
||||
<div class="boks2" id="search_input">
|
||||
<div class="stats_tekst" style="height: auto;">
|
||||
<h2>Your year on Plex</h2>
|
||||
<p>
|
||||
Did you get that thing from Spotify and wondered what your Plex statistics for 2020 were?
|
||||
<br><br>Well, have a look...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="status" id="stats" style="width:auto;">
|
||||
|
||||
<div class="search">
|
||||
|
||||
<form id='stats_form' class='form' onsubmit='return false' action="" method="post">
|
||||
|
||||
<div class='form-group'>
|
||||
|
||||
<label for="p_email" title="The email for the Plex account you want to check"><h4>Plex Email</h4></label>
|
||||
<input type="email" class='form_input' name="p_email" id="p_email" required/>
|
||||
|
||||
|
||||
<button type='submit' style='border: none; background: none; cursor: pointer; padding: 0;'>
|
||||
<div id='search_get'>SEARCH</div>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
|
||||
<div id="results_error" style="padding:0.5em; bottom:0; color: #eb4034;"></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" id="search_results">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$.getScript("index.js", function(){
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
47
index.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
function search_button(string) {
|
||||
$('#search_get').html(string);
|
||||
}
|
||||
|
||||
function time_days(seconds_input) {
|
||||
var seconds = Number(seconds_input);
|
||||
var days = seconds * 1.15741E-5;
|
||||
|
||||
var hours = String(days).split(".");
|
||||
var hours_str = "0." + hours[1];
|
||||
var hours_int = Number(hours_str) * 24.0;
|
||||
|
||||
var minutes = String(hours_int).split(".");
|
||||
var minutes_str = "0." + minutes[1];
|
||||
var minutes_int = Number(minutes_str) * 60.0;
|
||||
|
||||
var days_form = String(days).split(".");
|
||||
var hours_form = String(hours_int).split(".");
|
||||
var minutes_form = String(minutes_int).split(".");
|
||||
|
||||
var final = [Number(days_form[0]), Number(hours_form[0]), Number(minutes_form[0])];
|
||||
return final;
|
||||
}
|
||||
|
||||
function time_hours(seconds_input) {
|
||||
var seconds = Number(seconds_input);
|
||||
var hours_int = Number(seconds) * 0.0002777778;
|
||||
|
||||
var minutes = String(hours_int).split(".");
|
||||
var minutes_str = "0." + minutes[1];
|
||||
var minutes_int = Number(minutes_str) * 60.0;
|
||||
|
||||
var hours_form = String(hours_int).split(".");
|
||||
var minutes_form = String(minutes_int).split(".");
|
||||
|
||||
var final = [Number(hours_form[0]), Number(minutes_form[0])];
|
||||
return final;
|
||||
}
|
||||
|
||||
$(document).on('submit', '#stats_form', function(){
|
||||
|
||||
search_button("SEARCHING...");
|
||||
|
||||
$.getScript("get_stats_2020.js", function(){
|
||||
});
|
||||
|
||||
});
|