mirror of
https://github.com/aunefyren/wrapperr
synced 2024-11-13 23:17:06 +00:00
1325 lines
48 KiB
Go
1325 lines
48 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 configuration 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 received, 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 received, 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 process 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 processed 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,
|
|
OriginallyAvailableAt: tautulli_data[j].OriginallyAvailableAt,
|
|
}
|
|
|
|
// Append to day data
|
|
wrapperr_day.Data = append(wrapperr_day.Data, tautulli_entry)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// If the date is the current day, mark as incomplete 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_data[i].Data[j].OriginallyAvailableAt == "") || wrapperr_user_movie[d].OriginallyAvailableAt == wrapperr_data[i].Data[j].OriginallyAvailableAt) && 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_data[i].Data[j].OriginallyAvailableAt == "") || wrapperr_user_episode[d].OriginallyAvailableAt == wrapperr_data[i].Data[j].OriginallyAvailableAt) && wrapperr_user_episode[d].Title == wrapperr_data[i].Data[j].Title && wrapperr_user_episode[d].ParentTitle == wrapperr_data[i].Data[j].ParentTitle && wrapperr_user_episode[d].GrandparentTitle == wrapperr_data[i].Data[j].GrandparentTitle {
|
|
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_data[i].Data[j].OriginallyAvailableAt == "") || wrapperr_user_track[d].OriginallyAvailableAt == wrapperr_data[i].Data[j].OriginallyAvailableAt) && 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_data[i].Data[j].OriginallyAvailableAt == "") || wrapperr_user_album[d].OriginallyAvailableAt == wrapperr_data[i].Data[j].OriginallyAvailableAt) && 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_data[i].Data[j].OriginallyAvailableAt == "") || wrapperr_year_movie[d].OriginallyAvailableAt == wrapperr_data[i].Data[j].OriginallyAvailableAt) && 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].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 the longest duration spent on a certain episode
|
|
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
|
|
|
|
}
|