mirror of
https://github.com/aunefyren/wrapperr
synced 2024-11-13 23:17:06 +00:00
7b2e07bf1c
Added some comments Fixed loop logic based on multiple servers Minimum cache days must be greater than Tautulli server count
1322 lines
47 KiB
Go
1322 lines
47 KiB
Go
package routes
|
|
|
|
import (
|
|
"aunefyren/wrapperr/files"
|
|
"aunefyren/wrapperr/models"
|
|
"aunefyren/wrapperr/modules"
|
|
"aunefyren/wrapperr/utilities"
|
|
"encoding/json"
|
|
"errors"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/patrickmn/sortutil"
|
|
)
|
|
|
|
func ApiWrapperGetStatistics(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ip_string := utilities.GetOriginIPString(w, r)
|
|
log.Println("New /get/statistics request." + ip_string)
|
|
|
|
bool_state, err := files.GetConfigState()
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to retrieve confguration state."), 500)
|
|
return
|
|
} else if !bool_state {
|
|
log.Println("Wrapperr get statistics failed. Configuration state function retrieved false response.")
|
|
utilities.RespondDefaultError(w, r, errors.New("Can't retrieve statistics because Wrapperr is not configured."), 400)
|
|
return
|
|
}
|
|
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to load Wrapperr configuration."), 500)
|
|
return
|
|
}
|
|
|
|
log.Println("1. Configuration check passed." + ip_string)
|
|
|
|
// Check every Tautulli server
|
|
for i := 0; i < len(config.TautulliConfig); i++ {
|
|
log.Println("Checking Tautulli server '" + config.TautulliConfig[i].TautulliName + "'." + ip_string)
|
|
tautulli_state, err := modules.TautulliTestConnection(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey)
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 500)
|
|
return
|
|
} else if !tautulli_state {
|
|
log.Println("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "' before retrieving statistics.")
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to reach Tautulli server '"+config.TautulliConfig[i].TautulliName+"'."), 400)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Println("2. Tautulli check passed." + ip_string)
|
|
|
|
var auth_passed bool = false
|
|
var user_name string = ""
|
|
var user_id int = 0
|
|
var cache_limit int = 0
|
|
var admin bool = false
|
|
|
|
// Try to authorize bearer token from header
|
|
payload, err := modules.AuthorizeToken(w, r)
|
|
|
|
// If it failed and PlexAuth is enabled, respond with and error
|
|
// If it didn't fail, and PlexAuth is enabled, declare auth as passed
|
|
if err != nil && config.PlexAuth {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to authorize request."), 401)
|
|
return
|
|
} else if config.PlexAuth {
|
|
auth_passed = true
|
|
}
|
|
|
|
// If PlexAuth is enabled and the user is admin in payload, declare admin bool as true
|
|
if err == nil && payload.Admin {
|
|
admin = true
|
|
auth_passed = true
|
|
}
|
|
|
|
// If the user is not an admin, and PlexAuth is enabled, validate and retrieve details from Plex Token in payload
|
|
if !admin && config.PlexAuth {
|
|
plex_object, err := modules.PlexAuthValidateToken(payload.AuthToken, config.ClientKey, config.WrapperrVersion)
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Could not validate Plex Auth login."), 500)
|
|
return
|
|
}
|
|
|
|
user_name = plex_object.Username
|
|
user_id = plex_object.ID
|
|
}
|
|
|
|
log.Println("3. Auth check passed." + ip_string)
|
|
|
|
// Read payload from Post input
|
|
reqBody, _ := ioutil.ReadAll(r.Body)
|
|
var wrapperr_request models.SearchWrapperrRequest
|
|
json.Unmarshal(reqBody, &wrapperr_request)
|
|
|
|
// If auth is not passed, caching mode is false, and no PlexIdentity was recieved, mark it as a bad request
|
|
if wrapperr_request.PlexIdentity == "" && !auth_passed && !wrapperr_request.CachingMode {
|
|
log.Println("Cannot retrieve statistics because search parameter is invalid.")
|
|
utilities.RespondDefaultError(w, r, errors.New("Invalid search parameter."), 400)
|
|
return
|
|
}
|
|
|
|
// If no auth has been passed, caching mode is false, and user is not admin, search for the Plex details using Tautulli and PlexIdentity
|
|
if !auth_passed && !wrapperr_request.CachingMode && !admin {
|
|
|
|
UserNameFound := false
|
|
|
|
for i := 0; i < len(config.TautulliConfig); i++ {
|
|
new_id, new_username, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, wrapperr_request.PlexIdentity)
|
|
|
|
if err == nil {
|
|
UserNameFound = true
|
|
user_name = new_username
|
|
user_id = new_id
|
|
}
|
|
}
|
|
|
|
if !UserNameFound {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Could not find a matching user."), 500)
|
|
return
|
|
}
|
|
}
|
|
|
|
// If caching mode is false and user is admin, return bad request error
|
|
if !wrapperr_request.CachingMode && admin {
|
|
log.Println("Caching mode deactivated, but admin login session retrieved.")
|
|
utilities.RespondDefaultError(w, r, errors.New("You can not retrieve stats as admin."), 400)
|
|
return
|
|
}
|
|
|
|
// If caching mode is true and user is not admin, return bad request error
|
|
if wrapperr_request.CachingMode && !admin {
|
|
log.Println("Caching mode recieved, but user was not verified as admin.")
|
|
utilities.RespondDefaultError(w, r, errors.New("Only the admin can perform caching."), 401)
|
|
return
|
|
}
|
|
|
|
// If admin and caching mode, set username and user_id to correct values
|
|
if wrapperr_request.CachingMode && admin {
|
|
user_name = "Caching-Mode"
|
|
user_id = 0
|
|
cache_limit = wrapperr_request.CachingLimit
|
|
|
|
if !config.UseCache {
|
|
log.Println("Admin attempted to use cache mode, but the cache feature is disabled in the config.")
|
|
utilities.RespondDefaultError(w, r, errors.New("Caching mode enabled, but the cache feature is disabled in the settings."), 500)
|
|
return
|
|
}
|
|
}
|
|
|
|
// If no username and no user_id has been declared at this point, something is wrong. Return error.
|
|
if user_name == "" && user_id == 0 {
|
|
log.Println("At this point the user should have been verified, but username and ID is empty.")
|
|
utilities.RespondDefaultError(w, r, errors.New("User validation error."), 500)
|
|
return
|
|
}
|
|
|
|
log.Println("4. User details confirmed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
// Create empty array object for each day in Wrapped period. If cache is enabled, call GetCache() and replace the empty object.
|
|
wrapperr_data := []models.WrapperrDay{}
|
|
if config.UseCache {
|
|
wrapperr_data, err = files.GetCache()
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to load cache file."), 500)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Println("5. Cache stage completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
// Download/refresh data-set from Tautulli
|
|
wrapperr_data, wrapperr_data_complete, err := WrapperrDownloadDays(user_id, wrapperr_data, cache_limit, config)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
log.Println("6. Tautulli refresh/download stage completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
// If cache is enabled, send the object to SaveCache() for later use.
|
|
if config.UseCache {
|
|
err = files.SaveCache(&wrapperr_data)
|
|
if err != nil {
|
|
log.Println(err)
|
|
utilities.RespondDefaultError(w, r, errors.New("Failed to save new cache file for "+user_name+" ("+strconv.Itoa(user_id)+")."), 500)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Println("7. Cache saving stage completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
// If caching mode is in use, stop the proccess here and return the result to the user
|
|
if wrapperr_request.CachingMode {
|
|
|
|
boolean_reply := models.BooleanReply{
|
|
Message: "Completed caching request.",
|
|
Error: false,
|
|
Data: wrapperr_data_complete,
|
|
}
|
|
|
|
ip_string := utilities.GetOriginIPString(w, r)
|
|
log.Println("Caching request completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
utilities.RespondWithJSON(w, http.StatusOK, boolean_reply)
|
|
return
|
|
|
|
}
|
|
|
|
// Create reply object
|
|
var wrapperr_reply models.WrapperrStatisticsReply
|
|
wrapperr_reply.User.ID = user_id
|
|
wrapperr_reply.User.Name = user_name
|
|
wrapperr_reply.Date = time.Now().Format("2006-01-02")
|
|
wrapperr_reply.Message = "Statistics retrieved."
|
|
|
|
// Loop through Wrapperr data and format reply
|
|
wrapperr_reply, err = WrapperrLoopData(user_id, config, wrapperr_data, wrapperr_reply)
|
|
|
|
ip_string = utilities.GetOriginIPString(w, r)
|
|
log.Println("8. Wrapperr request completed for " + user_name + " (" + strconv.Itoa(user_id) + ")." + ip_string)
|
|
|
|
utilities.RespondWithJSON(w, http.StatusOK, wrapperr_reply)
|
|
return
|
|
|
|
}
|
|
|
|
func WrapperrDownloadDays(ID int, wrapperr_data []models.WrapperrDay, loop_interval int, config *models.WrapperrConfig) ([]models.WrapperrDay, bool, error) {
|
|
|
|
// Define variables
|
|
var complete_date_loop bool = true
|
|
var use_loop_interval bool = true
|
|
var found_date bool = false
|
|
var found_date_index int = 0
|
|
|
|
// If loop_interval is less than one, do not utilize the loop interval and set use_loop_interval to false
|
|
if loop_interval < 1 {
|
|
use_loop_interval = false
|
|
}
|
|
|
|
// Go through each Tautulli server
|
|
for q := 0; q < len(config.TautulliConfig); q++ {
|
|
|
|
// Log Tautulli server
|
|
log.Println("Checking Tautulli server '" + config.TautulliConfig[q].TautulliName + "'.")
|
|
|
|
// Define object with end date from wrapped period
|
|
end_loop_date := time.Unix(int64(config.WrappedEnd), 0)
|
|
|
|
// Split the string containing libraries. If the result is zero, set the first object in the array to an empty string
|
|
libraries := strings.Split(config.TautulliConfig[q].TautulliLibraries, ",")
|
|
if len(libraries) < 1 {
|
|
libraries[0] = ""
|
|
}
|
|
|
|
// Create a date time object containing the beginning of the wrapped period. If that date time object is before the end_loop_date variable, keep adding one day, each iteration
|
|
for loop_time := time.Unix(int64(config.WrappedStart), 0); !loop_time.After(end_loop_date); loop_time = loop_time.AddDate(0, 0, 1) {
|
|
|
|
// Define string variable with current iteration date and variable with date time containing the current local time
|
|
current_loop_date := loop_time.Format("2006-01-02")
|
|
now := time.Now()
|
|
|
|
// Stop if time has reached current time or end loop date
|
|
if loop_time.After(now) || loop_time.After(end_loop_date) {
|
|
break
|
|
}
|
|
|
|
// Clean array to populate with results
|
|
wrapperr_day := models.WrapperrDay{
|
|
Date: current_loop_date,
|
|
Data: nil,
|
|
DataComplete: true,
|
|
TautulliServers: []string{},
|
|
}
|
|
|
|
// Declare variables to populate
|
|
found_date = false
|
|
found_date_index = 0
|
|
tautulli_server_processed := false
|
|
|
|
// Go through all dates in current dataset
|
|
for j := 0; j < len(wrapperr_data); j++ {
|
|
|
|
// Parse current date string
|
|
time_temp, err := time.Parse("2006-01-02", wrapperr_data[j].Date)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
// If current date in the dataset is current processing date
|
|
if time_temp.Format("2006-01-02") == loop_time.Format("2006-01-02") {
|
|
// Keep index, save bool as true,
|
|
found_date_index = j
|
|
found_date = true
|
|
|
|
// Look at proccessed servers for current server
|
|
for y := 0; y < len(wrapperr_data[j].TautulliServers); y++ {
|
|
if wrapperr_data[j].TautulliServers[y] == config.TautulliConfig[q].TautulliName {
|
|
tautulli_server_processed = true
|
|
}
|
|
}
|
|
|
|
// Keep current dataset
|
|
wrapperr_day = wrapperr_data[j]
|
|
|
|
// Stop looking
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
if found_date && wrapperr_data[found_date_index].DataComplete && tautulli_server_processed {
|
|
|
|
// Found the date, the data is complete and the server is processed for the day. Skip.
|
|
continue
|
|
|
|
} else if found_date && !wrapperr_data[found_date_index].DataComplete {
|
|
|
|
// The date is found, the data is not complete
|
|
log.Println("Date " + current_loop_date + " from server '" + config.TautulliConfig[q].TautulliName + "' marked as incomplete in cache. Refreshing.")
|
|
|
|
// Remove processed server to ensure re-processing by all servers
|
|
wrapperr_day.TautulliServers = []string{}
|
|
|
|
} else if !found_date || !tautulli_server_processed {
|
|
|
|
// No data found, download with empty dataset
|
|
log.Println("Downloading day: " + current_loop_date + " from server '" + config.TautulliConfig[q].TautulliName + "'.")
|
|
|
|
} else {
|
|
|
|
// Something is wrong. Quit.
|
|
log.Println("Unknown date error from server '" + config.TautulliConfig[q].TautulliName + "'. Skipping.")
|
|
continue
|
|
|
|
}
|
|
|
|
// Loop through selected libraries for Tautullli API calls
|
|
for library_loop := 0; library_loop < len(libraries); library_loop++ {
|
|
|
|
var library_str string = ""
|
|
var grouping string = ""
|
|
|
|
// If no libraries are selected do not specify one in API call to Tautulli
|
|
if libraries[library_loop] == "" {
|
|
library_str = ""
|
|
} else {
|
|
library_str = "§ion_id=" + strings.TrimSpace(libraries[library_loop])
|
|
}
|
|
|
|
// Create string for selecting grouping feature in API call
|
|
if config.TautulliConfig[q].TautulliGrouping {
|
|
grouping = "1"
|
|
} else {
|
|
grouping = "0"
|
|
}
|
|
|
|
// Get data from Tautulli for server, day, and library
|
|
tautulli_data, err := modules.TautulliDownloadStatistics(config.TautulliConfig[q].TautulliPort, config.TautulliConfig[q].TautulliIP, config.TautulliConfig[q].TautulliHttps, config.TautulliConfig[q].TautulliRoot, config.TautulliConfig[q].TautulliApiKey, config.TautulliConfig[q].TautulliLength, library_str, grouping, current_loop_date)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
// Loop through retrieved data from Tautulli
|
|
for j := 0; j < len(tautulli_data); j++ {
|
|
if tautulli_data[j].MediaType == "movie" || tautulli_data[j].MediaType == "episode" || tautulli_data[j].MediaType == "track" {
|
|
tautulli_entry := models.TautulliEntry{
|
|
Date: tautulli_data[j].Date,
|
|
RowID: tautulli_data[j].RowID,
|
|
Duration: tautulli_data[j].Duration,
|
|
FriendlyName: tautulli_data[j].FriendlyName,
|
|
FullTitle: tautulli_data[j].FullTitle,
|
|
GrandparentRatingKey: tautulli_data[j].GrandparentRatingKey,
|
|
GrandparentTitle: tautulli_data[j].GrandparentTitle,
|
|
OriginalTitle: tautulli_data[j].OriginalTitle,
|
|
MediaType: tautulli_data[j].MediaType,
|
|
ParentRatingKey: tautulli_data[j].ParentRatingKey,
|
|
ParentTitle: tautulli_data[j].ParentTitle,
|
|
PausedCounter: tautulli_data[j].PausedCounter,
|
|
PercentComplete: tautulli_data[j].PercentComplete,
|
|
RatingKey: tautulli_data[j].RatingKey,
|
|
Title: tautulli_data[j].Title,
|
|
User: tautulli_data[j].User,
|
|
UserID: tautulli_data[j].UserID,
|
|
Year: tautulli_data[j].Year,
|
|
}
|
|
|
|
// Append to day data
|
|
wrapperr_day.Data = append(wrapperr_day.Data, tautulli_entry)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// If the date is the current day, mark as imcomplete so it can be refreshed the next time
|
|
if loop_time.Format("2006-01-02") == now.Format("2006-01-02") {
|
|
wrapperr_day.DataComplete = false
|
|
}
|
|
|
|
// Add current Tautulli server to processed servers for this day
|
|
if wrapperr_day.TautulliServers == nil || len(wrapperr_day.TautulliServers) == 0 {
|
|
var servers []string
|
|
servers = append(servers, config.TautulliConfig[q].TautulliName)
|
|
wrapperr_day.TautulliServers = servers
|
|
} else {
|
|
wrapperr_day.TautulliServers = append(wrapperr_day.TautulliServers, config.TautulliConfig[q].TautulliName)
|
|
}
|
|
|
|
// If found in dataset, update the values. if not found, append to dataset array.
|
|
if found_date {
|
|
wrapperr_data[found_date_index] = wrapperr_day
|
|
} else {
|
|
wrapperr_data = append(wrapperr_data, wrapperr_day)
|
|
}
|
|
|
|
// Change the loop interval to change date
|
|
if loop_interval > 0 && use_loop_interval {
|
|
loop_interval -= 1
|
|
}
|
|
|
|
// If loop interval is enabled, and reached 0, break the for loop and mark the data download as incomplete
|
|
if loop_interval == 0 && use_loop_interval {
|
|
complete_date_loop = false
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
// If loop interval is enabled, and reached 0, break the for loop and mark the data download as incomplete
|
|
if loop_interval == 0 && use_loop_interval {
|
|
complete_date_loop = false
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
return wrapperr_data, complete_date_loop, nil
|
|
}
|
|
|
|
func WrapperrLoopData(user_id int, config *models.WrapperrConfig, wrapperr_data []models.WrapperrDay, wrapperr_reply models.WrapperrStatisticsReply) (models.WrapperrStatisticsReply, error) {
|
|
|
|
end_loop_date := time.Unix(int64(config.WrappedEnd), 0)
|
|
start_loop_date := time.Unix(int64(config.WrappedStart), 0)
|
|
top_list_limit := config.WrapperrCustomize.StatsTopListLength
|
|
|
|
var wrapperr_user_movie []models.TautulliEntry
|
|
var wrapperr_user_episode []models.TautulliEntry
|
|
var wrapperr_user_show []models.TautulliEntry
|
|
var wrapperr_user_track []models.TautulliEntry
|
|
var wrapperr_user_album []models.TautulliEntry
|
|
var wrapperr_user_artist []models.TautulliEntry
|
|
var wrapperr_year_user []models.WrapperrYearUserEntry
|
|
var wrapperr_year_movie []models.TautulliEntry
|
|
var wrapperr_year_show []models.TautulliEntry
|
|
var wrapperr_year_artist []models.TautulliEntry
|
|
|
|
for i := 0; i < len(wrapperr_data); i++ {
|
|
|
|
for j := 0; j < len(wrapperr_data[i].Data); j++ {
|
|
|
|
// Create object using datetime from history entry
|
|
current_loop_date := time.Unix(int64(wrapperr_data[i].Data[j].Date), 0)
|
|
|
|
// Stop if time has reached current time or end loop date
|
|
if current_loop_date.After(end_loop_date) || current_loop_date.Before(start_loop_date) {
|
|
break
|
|
}
|
|
|
|
// If the entry is a movie watched by the current user
|
|
if config.WrapperrCustomize.GetUserMovieStats && wrapperr_data[i].Data[j].MediaType == "movie" && wrapperr_data[i].Data[j].UserID == user_id {
|
|
|
|
// Skip entry if year is invalid (0 or some value where no movies could have been made) or duration is less than 5 min
|
|
if wrapperr_data[i].Data[j].Year < 1750 || wrapperr_data[i].Data[j].Duration < 300 {
|
|
continue
|
|
}
|
|
|
|
movie_found := false
|
|
|
|
// Look for movie within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_movie); d++ {
|
|
if wrapperr_user_movie[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_user_movie[d].Title == wrapperr_data[i].Data[j].Title {
|
|
wrapperr_user_movie[d].Plays += 1
|
|
wrapperr_user_movie[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_movie[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
movie_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !movie_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_movie = append(wrapperr_user_movie, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
}
|
|
|
|
// If the entry is an episode watched by the current user
|
|
if config.WrapperrCustomize.GetUserShowStats && wrapperr_data[i].Data[j].MediaType == "episode" && wrapperr_data[i].Data[j].UserID == user_id {
|
|
|
|
episode_found := false
|
|
show_found := false
|
|
|
|
// Look for episode within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_episode); d++ {
|
|
if wrapperr_user_episode[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_user_episode[d].Title == wrapperr_data[i].Data[j].Title {
|
|
wrapperr_user_episode[d].Plays += 1
|
|
wrapperr_user_episode[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_episode[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
episode_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !episode_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_episode = append(wrapperr_user_episode, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// Look for show within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_show); d++ {
|
|
if wrapperr_user_show[d].GrandparentTitle == wrapperr_data[i].Data[j].GrandparentTitle {
|
|
wrapperr_user_show[d].Plays += 1
|
|
wrapperr_user_show[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_show[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
show_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !show_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_show = append(wrapperr_user_show, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
}
|
|
|
|
// If the entry is a track listened to by the current user
|
|
if config.WrapperrCustomize.GetUserMusicStats && wrapperr_data[i].Data[j].MediaType == "track" && wrapperr_data[i].Data[j].UserID == user_id {
|
|
|
|
track_found := false
|
|
album_found := false
|
|
artist_found := false
|
|
|
|
// Look for track within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_track); d++ {
|
|
if wrapperr_user_track[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_user_track[d].Title == wrapperr_data[i].Data[j].Title {
|
|
wrapperr_user_track[d].Plays += 1
|
|
wrapperr_user_track[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_track[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
track_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !track_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_track = append(wrapperr_user_track, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// If track has unknown album or artist, skip
|
|
if wrapperr_data[i].Data[j].ParentTitle == "[Unknown Album]" || wrapperr_data[i].Data[j].GrandparentTitle == "[Unknown Artist]" {
|
|
continue
|
|
}
|
|
|
|
// Look for album within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_album); d++ {
|
|
if wrapperr_user_album[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_user_album[d].ParentTitle == wrapperr_data[i].Data[j].ParentTitle {
|
|
wrapperr_user_album[d].Plays += 1
|
|
wrapperr_user_album[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_album[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
album_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !album_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_album = append(wrapperr_user_album, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// Look for artist within pre-defined array
|
|
for d := 0; d < len(wrapperr_user_artist); d++ {
|
|
if wrapperr_user_artist[d].GrandparentTitle == wrapperr_data[i].Data[j].GrandparentTitle {
|
|
wrapperr_user_artist[d].Plays += 1
|
|
wrapperr_user_artist[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_user_artist[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
artist_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !artist_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_user_artist = append(wrapperr_user_artist, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
}
|
|
|
|
// If the entry is a movie watched by any user and the stat setting is configured
|
|
if config.WrapperrCustomize.GetYearStatsMovies && wrapperr_data[i].Data[j].MediaType == "movie" {
|
|
|
|
// Skip entry if year is invalid (0 or some value where no movies could have been made) or duration is less than 5 min
|
|
if wrapperr_data[i].Data[j].Year < 1750 || wrapperr_data[i].Data[j].Duration < 300 {
|
|
continue
|
|
}
|
|
|
|
movie_found := false
|
|
user_found := false
|
|
|
|
// Look for movie within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_movie); d++ {
|
|
if wrapperr_year_movie[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_year_movie[d].Title == wrapperr_data[i].Data[j].Title {
|
|
wrapperr_year_movie[d].Plays += 1
|
|
wrapperr_year_movie[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_movie[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
movie_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If movie was not found, add it to array
|
|
if !movie_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_year_movie = append(wrapperr_year_movie, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// Look for user within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_user); d++ {
|
|
if wrapperr_year_user[d].UserID == wrapperr_data[i].Data[j].UserID {
|
|
wrapperr_year_user[d].Plays += 1
|
|
wrapperr_year_user[d].DurationMovies += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_user[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
user_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If user was not found, add it to array
|
|
if !user_found {
|
|
var user_entry = models.WrapperrYearUserEntry{
|
|
Plays: 1,
|
|
DurationMovies: wrapperr_data[i].Data[j].Duration,
|
|
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
|
|
User: wrapperr_data[i].Data[j].FriendlyName,
|
|
UserID: wrapperr_data[i].Data[j].UserID,
|
|
}
|
|
wrapperr_year_user = append(wrapperr_year_user, user_entry)
|
|
}
|
|
|
|
}
|
|
|
|
// If the entry is a show watched by any user and the stat setting is configured
|
|
if config.WrapperrCustomize.GetYearStatsShows && wrapperr_data[i].Data[j].MediaType == "episode" {
|
|
|
|
show_found := false
|
|
user_found := false
|
|
|
|
// Look for show within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_show); d++ {
|
|
if wrapperr_year_show[d].Year == wrapperr_data[i].Data[j].Year && wrapperr_year_show[d].GrandparentTitle == wrapperr_data[i].Data[j].GrandparentTitle {
|
|
wrapperr_year_show[d].Plays += 1
|
|
wrapperr_year_show[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_show[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
show_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If show was not found, add it to array
|
|
if !show_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_year_show = append(wrapperr_year_show, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// Look for user within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_user); d++ {
|
|
if wrapperr_year_user[d].UserID == wrapperr_data[i].Data[j].UserID {
|
|
wrapperr_year_user[d].Plays += 1
|
|
wrapperr_year_user[d].DurationShows += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_user[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
user_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If user was not found, add it to array
|
|
if !user_found {
|
|
var user_entry = models.WrapperrYearUserEntry{
|
|
Plays: 1,
|
|
DurationShows: wrapperr_data[i].Data[j].Duration,
|
|
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
|
|
User: wrapperr_data[i].Data[j].FriendlyName,
|
|
UserID: wrapperr_data[i].Data[j].UserID,
|
|
}
|
|
wrapperr_year_user = append(wrapperr_year_user, user_entry)
|
|
}
|
|
|
|
}
|
|
|
|
// If the entry is a track listened to by any user and the stat setting is configured
|
|
if config.WrapperrCustomize.GetYearStatsMusic && wrapperr_data[i].Data[j].MediaType == "track" {
|
|
|
|
artist_found := false
|
|
user_found := false
|
|
|
|
// Look for artist within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_artist); d++ {
|
|
if wrapperr_year_artist[d].GrandparentTitle == wrapperr_data[i].Data[j].GrandparentTitle {
|
|
wrapperr_year_artist[d].Plays += 1
|
|
wrapperr_year_artist[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_artist[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
artist_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If artist was not found, add it to array
|
|
if !artist_found {
|
|
wrapperr_data[i].Data[j].Plays = 1
|
|
wrapperr_year_artist = append(wrapperr_year_artist, wrapperr_data[i].Data[j])
|
|
}
|
|
|
|
// Look for user within pre-defined array
|
|
for d := 0; d < len(wrapperr_year_user); d++ {
|
|
if wrapperr_year_user[d].UserID == wrapperr_data[i].Data[j].UserID {
|
|
wrapperr_year_user[d].Plays += 1
|
|
wrapperr_year_user[d].DurationArtists += wrapperr_data[i].Data[j].Duration
|
|
wrapperr_year_user[d].PausedCounter += wrapperr_data[i].Data[j].PausedCounter
|
|
|
|
user_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If user was not found, add it to array
|
|
if !user_found {
|
|
var user_entry = models.WrapperrYearUserEntry{
|
|
Plays: 1,
|
|
DurationArtists: wrapperr_data[i].Data[j].Duration,
|
|
PausedCounter: wrapperr_data[i].Data[j].PausedCounter,
|
|
User: wrapperr_data[i].Data[j].FriendlyName,
|
|
UserID: wrapperr_data[i].Data[j].UserID,
|
|
}
|
|
wrapperr_year_user = append(wrapperr_year_user, user_entry)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Format reply for personal movie details
|
|
if config.WrapperrCustomize.GetUserMovieStats && len(wrapperr_user_movie) > 0 {
|
|
|
|
// Sort user movie array by duration
|
|
sortutil.DescByField(wrapperr_user_movie, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_user_movie {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMovies.Data.MoviesDuration = append(wrapperr_reply.User.UserMovies.Data.MoviesDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user movie array by plays
|
|
sortutil.DescByField(wrapperr_user_movie, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_movie {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMovies.Data.MoviesPlays = append(wrapperr_reply.User.UserMovies.Data.MoviesPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find longest pause
|
|
sortutil.DescByField(wrapperr_user_movie, "PausedCounter")
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieMostPaused.Duration = wrapperr_user_movie[0].Duration
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieMostPaused.PausedCounter = wrapperr_user_movie[0].PausedCounter
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieMostPaused.Plays = wrapperr_user_movie[0].Plays
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieMostPaused.Title = wrapperr_user_movie[0].Title
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieMostPaused.Year = wrapperr_user_movie[0].Year
|
|
|
|
// Find average movie completion, duration sum and play sum
|
|
movie_completion_sum := 0
|
|
movie_duration_sum := 0
|
|
for _, entry := range wrapperr_user_movie {
|
|
movie_completion_sum += entry.PercentComplete
|
|
movie_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieFinishingPercent = float64(movie_completion_sum) / float64(len(wrapperr_user_movie))
|
|
wrapperr_reply.User.UserMovies.Data.MovieDuration = movie_duration_sum
|
|
wrapperr_reply.User.UserMovies.Data.MoviePlays = len(wrapperr_user_movie)
|
|
|
|
// Find oldest movie
|
|
sortutil.AscByField(wrapperr_user_movie, "Year")
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieOldest.Duration = wrapperr_user_movie[0].Duration
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieOldest.PausedCounter = wrapperr_user_movie[0].PausedCounter
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieOldest.Plays = wrapperr_user_movie[0].Plays
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieOldest.Title = wrapperr_user_movie[0].Title
|
|
wrapperr_reply.User.UserMovies.Data.UserMovieOldest.Year = wrapperr_user_movie[0].Year
|
|
|
|
wrapperr_reply.User.UserMovies.Message = "All movies processed."
|
|
|
|
} else {
|
|
wrapperr_reply.User.UserMovies.Data.MoviesDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.User.UserMovies.Data.MoviesPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.User.UserMovies.Message = "No movies processed."
|
|
}
|
|
|
|
// Format reply for personal show details
|
|
if config.WrapperrCustomize.GetUserShowStats && len(wrapperr_user_show) > 0 {
|
|
|
|
// Sort user show array by duration
|
|
sortutil.DescByField(wrapperr_user_show, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_user_show {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserShows.Data.ShowsDuration = append(wrapperr_reply.User.UserShows.Data.ShowsDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user show array by plays
|
|
sortutil.DescByField(wrapperr_user_show, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_show {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserShows.Data.ShowsPlays = append(wrapperr_reply.User.UserShows.Data.ShowsPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find longest pause
|
|
sortutil.DescByField(wrapperr_user_episode, "Duration")
|
|
wrapperr_reply.User.UserShows.Data.EpisodeDurationLongest.Duration = wrapperr_user_episode[0].Duration
|
|
wrapperr_reply.User.UserShows.Data.EpisodeDurationLongest.GrandparentTitle = wrapperr_user_episode[0].GrandparentTitle
|
|
wrapperr_reply.User.UserShows.Data.EpisodeDurationLongest.ParentTitle = wrapperr_user_episode[0].ParentTitle
|
|
wrapperr_reply.User.UserShows.Data.EpisodeDurationLongest.Plays = wrapperr_user_episode[0].Plays
|
|
wrapperr_reply.User.UserShows.Data.EpisodeDurationLongest.Title = wrapperr_user_episode[0].Title
|
|
|
|
// Find duration sum and play sum
|
|
episode_duration_sum := 0
|
|
for _, entry := range wrapperr_user_episode {
|
|
episode_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.User.UserShows.Data.ShowDuration = episode_duration_sum
|
|
wrapperr_reply.User.UserShows.Data.ShowPlays = len(wrapperr_user_show)
|
|
|
|
// Find show buddy...
|
|
|
|
wrapperr_reply.User.UserShows.Message = "All shows processed."
|
|
|
|
} else {
|
|
wrapperr_reply.User.UserShows.Data.ShowsDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.User.UserShows.Data.ShowsPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.User.UserShows.Message = "No shows processed."
|
|
}
|
|
|
|
// Format reply for personal music details
|
|
if config.WrapperrCustomize.GetUserMusicStats && len(wrapperr_user_track) > 0 {
|
|
|
|
// Sort user track array by duration
|
|
sortutil.DescByField(wrapperr_user_track, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_user_track {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.TracksDuration = append(wrapperr_reply.User.UserMusic.Data.TracksDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user track array by plays
|
|
sortutil.DescByField(wrapperr_user_track, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_track {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.TracksPlays = append(wrapperr_reply.User.UserMusic.Data.TracksPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user album array by duration
|
|
sortutil.DescByField(wrapperr_user_album, "Duration")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_album {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.AlbumsDuration = append(wrapperr_reply.User.UserMusic.Data.AlbumsDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user album array by plays
|
|
sortutil.DescByField(wrapperr_user_album, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_album {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.AlbumsPlays = append(wrapperr_reply.User.UserMusic.Data.AlbumsPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user artist array by duration
|
|
sortutil.DescByField(wrapperr_user_artist, "Duration")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_artist {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.ArtistsDuration = append(wrapperr_reply.User.UserMusic.Data.ArtistsDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort user artist array by plays
|
|
sortutil.DescByField(wrapperr_user_artist, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_user_artist {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.ArtistsPlays = append(wrapperr_reply.User.UserMusic.Data.ArtistsPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find duration sum and play sum
|
|
track_duration_sum := 0
|
|
for _, entry := range wrapperr_user_track {
|
|
track_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.TrackDuration = track_duration_sum
|
|
wrapperr_reply.User.UserMusic.Data.TrackPlays = len(wrapperr_user_track)
|
|
|
|
// Find oldest album
|
|
sortutil.AscByField(wrapperr_user_album, "Year")
|
|
var oldest_album_index = 0
|
|
for d := 0; d < len(wrapperr_user_album); d++ {
|
|
if wrapperr_user_album[d].Year > 1000 {
|
|
oldest_album_index = d
|
|
break
|
|
}
|
|
}
|
|
wrapperr_reply.User.UserMusic.Data.UserAlbumOldest.Duration = wrapperr_user_album[oldest_album_index].Duration
|
|
wrapperr_reply.User.UserMusic.Data.UserAlbumOldest.GrandparentTitle = wrapperr_user_album[oldest_album_index].GrandparentTitle
|
|
wrapperr_reply.User.UserMusic.Data.UserAlbumOldest.ParentTitle = wrapperr_user_album[oldest_album_index].ParentTitle
|
|
wrapperr_reply.User.UserMusic.Data.UserAlbumOldest.Plays = wrapperr_user_album[oldest_album_index].Plays
|
|
wrapperr_reply.User.UserMusic.Data.UserAlbumOldest.Year = wrapperr_user_album[oldest_album_index].Year
|
|
|
|
wrapperr_reply.User.UserMusic.Message = "All tracks processed."
|
|
|
|
} else {
|
|
wrapperr_reply.User.UserMusic.Data.TracksDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.User.UserMusic.Data.TracksPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.User.UserMusic.Message = "No tracks processed."
|
|
}
|
|
|
|
// Format reply for universal movie details
|
|
if config.WrapperrCustomize.GetYearStatsMovies && len(wrapperr_year_movie) > 0 {
|
|
|
|
// Sort year movie array by duration
|
|
sortutil.DescByField(wrapperr_year_movie, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_year_movie {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearMovies.Data.MoviesDuration = append(wrapperr_reply.YearStats.YearMovies.Data.MoviesDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort year movie array by plays
|
|
sortutil.DescByField(wrapperr_year_movie, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_year_movie {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearMovies.Data.MoviesPlays = append(wrapperr_reply.YearStats.YearMovies.Data.MoviesPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find duration sum and play sum
|
|
movie_duration_sum := 0
|
|
movie_play_sum := 0
|
|
for _, entry := range wrapperr_year_movie {
|
|
movie_play_sum += entry.Plays
|
|
movie_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.YearStats.YearMovies.Data.MovieDuration = movie_duration_sum
|
|
wrapperr_reply.YearStats.YearMovies.Data.MoviePlays = movie_play_sum
|
|
|
|
wrapperr_reply.YearStats.YearMovies.Message = "All movies processed."
|
|
|
|
} else {
|
|
wrapperr_reply.YearStats.YearMovies.Data.MoviesDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.YearStats.YearMovies.Data.MoviesPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.YearStats.YearMovies.Message = "No movies processed."
|
|
}
|
|
|
|
// Format reply for universal show details
|
|
if config.WrapperrCustomize.GetYearStatsShows && len(wrapperr_year_show) > 0 {
|
|
|
|
// Sort year show array by duration
|
|
sortutil.DescByField(wrapperr_year_show, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_year_show {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowsDuration = append(wrapperr_reply.YearStats.YearShows.Data.ShowsDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort year show array by plays
|
|
sortutil.DescByField(wrapperr_year_show, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_year_show {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowsPlays = append(wrapperr_reply.YearStats.YearShows.Data.ShowsPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find duration sum and play sum
|
|
show_duration_sum := 0
|
|
show_play_sum := 0
|
|
for _, entry := range wrapperr_year_show {
|
|
show_play_sum += entry.Plays
|
|
show_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowDuration = show_duration_sum
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowPlays = show_play_sum
|
|
|
|
wrapperr_reply.YearStats.YearShows.Message = "All shows processed."
|
|
|
|
} else {
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowsDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.YearStats.YearShows.Data.ShowsPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.YearStats.YearShows.Message = "No shows processed."
|
|
}
|
|
|
|
// Format reply for universal artist details
|
|
if config.WrapperrCustomize.GetYearStatsMusic && len(wrapperr_year_artist) > 0 {
|
|
|
|
// Sort year artist array by duration
|
|
sortutil.DescByField(wrapperr_year_artist, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_year_artist {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearMusic.Data.ArtistsDuration = append(wrapperr_reply.YearStats.YearMusic.Data.ArtistsDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort year artist array by plays
|
|
sortutil.DescByField(wrapperr_year_artist, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_year_artist {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearMusic.Data.ArtistsPlays = append(wrapperr_reply.YearStats.YearMusic.Data.ArtistsPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Find duration sum and play sum
|
|
music_duration_sum := 0
|
|
music_play_sum := 0
|
|
for _, entry := range wrapperr_year_artist {
|
|
music_play_sum += entry.Plays
|
|
music_duration_sum += entry.Duration
|
|
}
|
|
wrapperr_reply.YearStats.YearMusic.Data.MusicDuration = music_duration_sum
|
|
wrapperr_reply.YearStats.YearMusic.Data.MusicPlays = music_play_sum
|
|
|
|
wrapperr_reply.YearStats.YearMusic.Message = "All tracks processed."
|
|
|
|
} else {
|
|
wrapperr_reply.YearStats.YearMusic.Data.ArtistsDuration = []models.TautulliEntry{}
|
|
wrapperr_reply.YearStats.YearMusic.Data.ArtistsPlays = []models.TautulliEntry{}
|
|
|
|
wrapperr_reply.YearStats.YearMusic.Message = "No tracks processed."
|
|
}
|
|
|
|
// Format reply for universal user details
|
|
if (config.WrapperrCustomize.GetYearStatsMusic || config.WrapperrCustomize.GetYearStatsMovies || config.WrapperrCustomize.GetYearStatsShows) && config.WrapperrCustomize.GetYearStatsLeaderboard && len(wrapperr_year_user) > 0 {
|
|
|
|
// Create new array with duration sum, then sort year users array by duration
|
|
var wrapperr_year_user_summed []models.WrapperrYearUserEntry
|
|
for d := 0; d < len(wrapperr_year_user); d++ {
|
|
wrapperr_year_user[d].Duration = wrapperr_year_user[d].DurationMovies + wrapperr_year_user[d].DurationShows + wrapperr_year_user[d].DurationArtists
|
|
wrapperr_year_user_summed = append(wrapperr_year_user_summed, wrapperr_year_user[d])
|
|
}
|
|
sortutil.DescByField(wrapperr_year_user, "Duration")
|
|
count := 0
|
|
for _, entry := range wrapperr_year_user {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration = append(wrapperr_reply.YearStats.YearUsers.Data.UsersDuration, entry)
|
|
count += 1
|
|
}
|
|
|
|
// Sort year show array by plays
|
|
sortutil.DescByField(wrapperr_year_user, "Plays")
|
|
count = 0
|
|
for _, entry := range wrapperr_year_user {
|
|
if count >= top_list_limit && top_list_limit != 0 {
|
|
break
|
|
}
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays = append(wrapperr_reply.YearStats.YearUsers.Data.UsersPlays, entry)
|
|
count += 1
|
|
}
|
|
|
|
wrapperr_reply.YearStats.YearMovies.Message = "All users processed."
|
|
|
|
// Scrub the data after ordering array
|
|
if !config.WrapperrCustomize.GetYearStatsLeaderboardNumbers {
|
|
for index, _ := range wrapperr_reply.YearStats.YearUsers.Data.UsersPlays {
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].Duration = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].DurationArtists = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].DurationMovies = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].DurationShows = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].Plays = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays[index].PausedCounter = 0
|
|
}
|
|
for index, _ := range wrapperr_reply.YearStats.YearUsers.Data.UsersDuration {
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].Duration = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].DurationArtists = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].DurationMovies = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].DurationShows = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].Plays = 0
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration[index].PausedCounter = 0
|
|
}
|
|
}
|
|
|
|
} else {
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersDuration = []models.WrapperrYearUserEntry{}
|
|
wrapperr_reply.YearStats.YearUsers.Data.UsersPlays = []models.WrapperrYearUserEntry{}
|
|
|
|
wrapperr_reply.YearStats.YearMovies.Message = "No users processed."
|
|
}
|
|
|
|
// Get show buddy
|
|
if config.WrapperrCustomize.GetUserShowBuddy {
|
|
|
|
if len(wrapperr_reply.User.UserShows.Data.ShowsDuration) < 1 {
|
|
wrapperr_reply.User.UserShows.Data.ShowBuddy.Message = "Show buddy is enabled but no top show was not found."
|
|
wrapperr_reply.User.UserShows.Data.ShowBuddy.Error = true
|
|
} else {
|
|
|
|
err, buddy_name, buddy_found, buddy_duration := GetUserShowBuddy(config, wrapperr_reply.User.UserShows.Data.ShowsDuration[0], user_id, wrapperr_data)
|
|
|
|
var show_buddy models.WrapperrShowBuddy
|
|
|
|
if err != nil {
|
|
log.Println("Show buddy threw error: ")
|
|
log.Println(err)
|
|
show_buddy.Message = "Failed to retrieve show buddy."
|
|
show_buddy.Error = true
|
|
} else {
|
|
show_buddy.Message = "Show buddy retrieved."
|
|
show_buddy.Error = false
|
|
show_buddy.BuddyName = buddy_name
|
|
show_buddy.BuddyDuration = buddy_duration
|
|
show_buddy.BuddyFound = buddy_found
|
|
|
|
log.Println("Show buddy retrieved.")
|
|
}
|
|
|
|
wrapperr_reply.User.UserShows.Data.ShowBuddy = show_buddy
|
|
|
|
}
|
|
|
|
} else {
|
|
wrapperr_reply.User.UserShows.Data.ShowBuddy.Message = "Show buddy is disabled in the settings."
|
|
wrapperr_reply.User.UserShows.Data.ShowBuddy.Error = true
|
|
}
|
|
|
|
return wrapperr_reply, nil
|
|
|
|
}
|
|
|
|
func GetUserShowBuddy(config *models.WrapperrConfig, top_show models.TautulliEntry, user_id int, wrapperr_data []models.WrapperrDay) (error, string, bool, int) {
|
|
|
|
var top_show_users []models.WrapperrYearUserEntry
|
|
var top_show_buddy_name = "Something went wrong."
|
|
var top_show_buddy_duration = 0
|
|
var top_show_buddy_found = false
|
|
var error_bool = errors.New("Something went wrong.")
|
|
|
|
end_loop_date := time.Unix(int64(config.WrappedEnd), 0)
|
|
start_loop_date := time.Unix(int64(config.WrappedStart), 0)
|
|
|
|
for i := 0; i < len(wrapperr_data); i++ {
|
|
|
|
for j := 0; j < len(wrapperr_data[i].Data); j++ {
|
|
|
|
// Create object using datetime from history entry
|
|
current_loop_date := time.Unix(int64(wrapperr_data[i].Data[j].Date), 0)
|
|
|
|
// Stop if time has reached current time or end loop date
|
|
if current_loop_date.After(end_loop_date) || current_loop_date.Before(start_loop_date) {
|
|
continue
|
|
}
|
|
|
|
if wrapperr_data[i].Data[j].GrandparentTitle == top_show.GrandparentTitle {
|
|
|
|
user_found := false
|
|
|
|
// Look for user within pre-defined array
|
|
for d := 0; d < len(top_show_users); d++ {
|
|
if top_show_users[d].UserID == wrapperr_data[i].Data[j].UserID {
|
|
top_show_users[d].Plays += 1
|
|
top_show_users[d].Duration += wrapperr_data[i].Data[j].Duration
|
|
|
|
user_found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// If user was not found, add it to array
|
|
if !user_found {
|
|
var user_entry = models.WrapperrYearUserEntry{
|
|
Plays: 1,
|
|
Duration: wrapperr_data[i].Data[j].Duration,
|
|
FriendlyName: wrapperr_data[i].Data[j].FriendlyName,
|
|
UserID: wrapperr_data[i].Data[j].UserID,
|
|
}
|
|
top_show_users = append(top_show_users, user_entry)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(top_show_users) < 2 {
|
|
return nil, "None", false, 0
|
|
}
|
|
|
|
sortutil.DescByField(top_show_users, "Duration")
|
|
|
|
// Find requesters index
|
|
user_index := 0
|
|
for index, user := range top_show_users {
|
|
if user_id == user.UserID {
|
|
user_index = index
|
|
break
|
|
}
|
|
}
|
|
|
|
// Find relative users
|
|
for index, user := range top_show_users {
|
|
|
|
if user.UserID != user_id && len(top_show_users) == 2 {
|
|
top_show_buddy_name = user.FriendlyName
|
|
top_show_buddy_duration = user.Duration
|
|
top_show_buddy_found = true
|
|
error_bool = nil
|
|
break
|
|
}
|
|
|
|
if user.UserID != user_id && index == user_index-1 {
|
|
top_show_buddy_name = user.FriendlyName
|
|
top_show_buddy_duration = user.Duration
|
|
top_show_buddy_found = true
|
|
error_bool = nil
|
|
break
|
|
}
|
|
|
|
if user.UserID != user_id && index == user_index+1 {
|
|
top_show_buddy_name = user.FriendlyName
|
|
top_show_buddy_duration = user.Duration
|
|
top_show_buddy_found = true
|
|
error_bool = nil
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
return error_bool, top_show_buddy_name, top_show_buddy_found, top_show_buddy_duration
|
|
|
|
}
|