2022-11-29 11:43:01 +00:00
package routes
2022-07-04 15:47:59 +00:00
import (
2022-11-29 11:43:01 +00:00
"aunefyren/wrapperr/files"
"aunefyren/wrapperr/models"
"aunefyren/wrapperr/modules"
"aunefyren/wrapperr/utilities"
2022-07-04 15:47:59 +00:00
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
2023-02-03 14:22:06 +00:00
"github.com/goombaio/namegenerator"
2022-07-04 15:47:59 +00:00
"github.com/patrickmn/sortutil"
)
func ApiWrapperGetStatistics ( w http . ResponseWriter , r * http . Request ) {
2022-11-29 11:43:01 +00:00
ip_string := utilities . GetOriginIPString ( w , r )
2022-07-04 15:47:59 +00:00
log . Println ( "New /get/statistics request." + ip_string )
2022-11-29 11:43:01 +00:00
bool_state , err := files . GetConfigState ( )
2022-07-04 15:47:59 +00:00
if err != nil {
log . Println ( err )
2022-12-31 16:58:36 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to retrieve configuration state." ) , 500 )
2022-07-04 15:47:59 +00:00
return
} else if ! bool_state {
log . Println ( "Wrapperr get statistics failed. Configuration state function retrieved false response." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Can't retrieve statistics because Wrapperr is not configured." ) , 400 )
2022-07-04 15:47:59 +00:00
return
}
2022-11-29 11:43:01 +00:00
config , err := files . GetConfig ( )
2022-07-04 15:47:59 +00:00
if err != nil {
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to load Wrapperr configuration." ) , 500 )
2022-07-04 15:47:59 +00:00
return
}
log . Println ( "1. Configuration check passed." + ip_string )
2022-10-20 19:38:55 +00:00
// Check every Tautulli server
for i := 0 ; i < len ( config . TautulliConfig ) ; i ++ {
2022-11-27 17:12:11 +00:00
log . Println ( "Checking Tautulli server '" + config . TautulliConfig [ i ] . TautulliName + "'." + ip_string )
2022-11-29 11:43:01 +00:00
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 )
2022-10-20 19:38:55 +00:00
if err != nil {
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to reach Tautulli server '" + config . TautulliConfig [ i ] . TautulliName + "'." ) , 500 )
2022-10-20 19:38:55 +00:00
return
} else if ! tautulli_state {
log . Println ( "Failed to ping Tautulli server '" + config . TautulliConfig [ i ] . TautulliName + "' before retrieving statistics." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to reach Tautulli server '" + config . TautulliConfig [ i ] . TautulliName + "'." ) , 400 )
2022-10-20 19:38:55 +00:00
return
}
2022-07-04 15:47:59 +00:00
}
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
2023-10-21 15:17:05 +00:00
payload , err := modules . AuthorizeToken ( w , r , false )
2022-07-04 15:47:59 +00:00
// 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 )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to authorize request." ) , 401 )
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
plex_object , err := modules . PlexAuthValidateToken ( payload . AuthToken , config . ClientKey , config . WrapperrVersion )
2022-07-04 15:47:59 +00:00
if err != nil {
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Could not validate Plex Auth login." ) , 500 )
2022-07-04 15:47:59 +00:00
return
}
2023-10-22 12:25:41 +00:00
// Set user details from Plex login
2022-07-04 15:47:59 +00:00
user_name = plex_object . Username
user_id = plex_object . ID
2023-10-22 12:25:41 +00:00
// Check for friendly name using Tautulli
for i := 0 ; i < len ( config . TautulliConfig ) ; i ++ {
_ , 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 , user_name )
if err == nil {
user_name = new_username
}
break
}
2022-07-04 15:47:59 +00:00
}
log . Println ( "3. Auth check passed." + ip_string )
// Read payload from Post input
reqBody , _ := ioutil . ReadAll ( r . Body )
2022-11-29 11:43:01 +00:00
var wrapperr_request models . SearchWrapperrRequest
2022-07-04 15:47:59 +00:00
json . Unmarshal ( reqBody , & wrapperr_request )
2022-12-31 16:58:36 +00:00
// If auth is not passed, caching mode is false, and no PlexIdentity was received, mark it as a bad request
2022-07-04 15:47:59 +00:00
if wrapperr_request . PlexIdentity == "" && ! auth_passed && ! wrapperr_request . CachingMode {
log . Println ( "Cannot retrieve statistics because search parameter is invalid." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Invalid search parameter." ) , 400 )
2022-07-04 15:47:59 +00:00
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 {
2022-10-20 19:38:55 +00:00
UserNameFound := false
for i := 0 ; i < len ( config . TautulliConfig ) ; i ++ {
2022-11-29 11:43:01 +00:00
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 )
2022-10-20 19:38:55 +00:00
if err == nil {
UserNameFound = true
user_name = new_username
user_id = new_id
}
}
if ! UserNameFound {
2022-07-04 15:47:59 +00:00
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Could not find a matching user." ) , 500 )
2022-07-04 15:47:59 +00:00
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." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "You can not retrieve stats as admin." ) , 400 )
2022-07-04 15:47:59 +00:00
return
}
// If caching mode is true and user is not admin, return bad request error
if wrapperr_request . CachingMode && ! admin {
2022-12-09 14:29:59 +00:00
log . Println ( "Caching mode received, but user was not verified as admin." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Only the admin can perform caching." ) , 401 )
2022-07-04 15:47:59 +00:00
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." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Caching mode enabled, but the cache feature is disabled in the settings." ) , 500 )
2022-07-04 15:47:59 +00:00
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." )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "User validation error." ) , 500 )
2022-07-04 15:47:59 +00:00
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.
2022-11-29 11:43:01 +00:00
wrapperr_data := [ ] models . WrapperrDay { }
2022-07-04 15:47:59 +00:00
if config . UseCache {
2022-11-29 11:43:01 +00:00
wrapperr_data , err = files . GetCache ( )
2022-07-04 15:47:59 +00:00
if err != nil {
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to load cache file." ) , 500 )
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
err = files . SaveCache ( & wrapperr_data )
2022-07-04 15:47:59 +00:00
if err != nil {
log . Println ( err )
2022-11-29 11:43:01 +00:00
utilities . RespondDefaultError ( w , r , errors . New ( "Failed to save new cache file for " + user_name + " (" + strconv . Itoa ( user_id ) + ")." ) , 500 )
2022-07-04 15:47:59 +00:00
return
}
}
log . Println ( "7. Cache saving stage completed for " + user_name + " (" + strconv . Itoa ( user_id ) + ")." + ip_string )
2022-12-31 16:58:36 +00:00
// If caching mode is in use, stop the process here and return the result to the user
2022-07-04 15:47:59 +00:00
if wrapperr_request . CachingMode {
2022-11-29 11:43:01 +00:00
boolean_reply := models . BooleanReply {
2022-07-04 15:47:59 +00:00
Message : "Completed caching request." ,
Error : false ,
Data : wrapperr_data_complete ,
}
2022-11-29 11:43:01 +00:00
ip_string := utilities . GetOriginIPString ( w , r )
2022-07-04 15:47:59 +00:00
log . Println ( "Caching request completed for " + user_name + " (" + strconv . Itoa ( user_id ) + ")." + ip_string )
2022-11-29 11:43:01 +00:00
utilities . RespondWithJSON ( w , http . StatusOK , boolean_reply )
2022-07-04 15:47:59 +00:00
return
}
// Create reply object
2022-11-29 11:43:01 +00:00
var wrapperr_reply models . WrapperrStatisticsReply
2022-07-04 15:47:59 +00:00
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 )
2022-11-29 11:43:01 +00:00
ip_string = utilities . GetOriginIPString ( w , r )
2022-07-04 15:47:59 +00:00
log . Println ( "8. Wrapperr request completed for " + user_name + " (" + strconv . Itoa ( user_id ) + ")." + ip_string )
2022-11-29 11:43:01 +00:00
utilities . RespondWithJSON ( w , http . StatusOK , wrapperr_reply )
2022-07-04 15:47:59 +00:00
return
}
2022-11-29 11:43:01 +00:00
func WrapperrDownloadDays ( ID int , wrapperr_data [ ] models . WrapperrDay , loop_interval int , config * models . WrapperrConfig ) ( [ ] models . WrapperrDay , bool , error ) {
2022-07-04 15:47:59 +00:00
// Define variables
var complete_date_loop bool = true
var use_loop_interval bool = true
2022-10-20 19:38:55 +00:00
var found_date bool = false
var found_date_index int = 0
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// 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
}
2022-10-20 19:38:55 +00:00
// Go through each Tautulli server
for q := 0 ; q < len ( config . TautulliConfig ) ; q ++ {
2022-07-04 15:47:59 +00:00
2022-11-27 17:12:11 +00:00
// Log Tautulli server
log . Println ( "Checking Tautulli server '" + config . TautulliConfig [ q ] . TautulliName + "'." )
2022-10-20 19:38:55 +00:00
// Define object with end date from wrapped period
end_loop_date := time . Unix ( int64 ( config . WrappedEnd ) , 0 )
2022-07-04 15:47:59 +00:00
2022-10-20 19:38:55 +00:00
// 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 ] = ""
2022-07-04 15:47:59 +00:00
}
2022-10-20 19:38:55 +00:00
// 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 ) {
2022-07-04 15:47:59 +00:00
2022-10-20 19:38:55 +00:00
// 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 ( )
2022-07-04 15:47:59 +00:00
2022-10-20 19:38:55 +00:00
// Stop if time has reached current time or end loop date
if loop_time . After ( now ) || loop_time . After ( end_loop_date ) {
2022-07-04 15:47:59 +00:00
break
}
2022-10-20 19:38:55 +00:00
// Clean array to populate with results
2022-11-29 11:43:01 +00:00
wrapperr_day := models . WrapperrDay {
2022-11-27 17:12:11 +00:00
Date : current_loop_date ,
Data : nil ,
DataComplete : true ,
TautulliServers : [ ] string { } ,
2022-10-20 19:38:55 +00:00
}
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// Declare variables to populate
2022-10-20 19:38:55 +00:00
found_date = false
found_date_index = 0
2022-11-27 17:12:11 +00:00
tautulli_server_processed := false
2022-11-29 18:32:30 +00:00
// Go through all dates in current dataset
2022-10-20 19:38:55 +00:00
for j := 0 ; j < len ( wrapperr_data ) ; j ++ {
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// Parse current date string
2022-10-20 19:38:55 +00:00
time_temp , err := time . Parse ( "2006-01-02" , wrapperr_data [ j ] . Date )
if err != nil {
log . Println ( err )
}
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// If current date in the dataset is current processing date
2022-10-20 19:38:55 +00:00
if time_temp . Format ( "2006-01-02" ) == loop_time . Format ( "2006-01-02" ) {
2022-11-29 18:32:30 +00:00
// Keep index, save bool as true,
2022-10-20 19:38:55 +00:00
found_date_index = j
found_date = true
2022-11-27 17:12:11 +00:00
2022-12-31 16:58:36 +00:00
// Look at processed servers for current server
2022-11-27 17:12:11 +00:00
for y := 0 ; y < len ( wrapperr_data [ j ] . TautulliServers ) ; y ++ {
if wrapperr_data [ j ] . TautulliServers [ y ] == config . TautulliConfig [ q ] . TautulliName {
tautulli_server_processed = true
}
}
2022-11-29 18:32:30 +00:00
// Keep current dataset
2022-11-28 20:18:29 +00:00
wrapperr_day = wrapperr_data [ j ]
2022-11-27 17:12:11 +00:00
2022-11-29 18:32:30 +00:00
// Stop looking
2022-10-20 19:38:55 +00:00
break
}
2022-07-04 15:47:59 +00:00
}
2022-11-27 17:12:11 +00:00
if found_date && wrapperr_data [ found_date_index ] . DataComplete && tautulli_server_processed {
2022-11-29 18:32:30 +00:00
// Found the date, the data is complete and the server is processed for the day. Skip.
2022-10-20 19:38:55 +00:00
continue
2022-11-29 18:32:30 +00:00
2022-10-20 19:38:55 +00:00
} else if found_date && ! wrapperr_data [ found_date_index ] . DataComplete {
2022-11-27 17:12:11 +00:00
2022-11-29 18:32:30 +00:00
// The date is found, the data is not complete
2022-10-20 19:38:55 +00:00
log . Println ( "Date " + current_loop_date + " from server '" + config . TautulliConfig [ q ] . TautulliName + "' marked as incomplete in cache. Refreshing." )
2022-11-27 17:12:11 +00:00
2022-11-29 18:32:30 +00:00
// Remove processed server to ensure re-processing by all servers
2022-11-27 17:12:11 +00:00
wrapperr_day . TautulliServers = [ ] string { }
} else if ! found_date || ! tautulli_server_processed {
2022-11-29 18:32:30 +00:00
// No data found, download with empty dataset
2022-10-20 19:38:55 +00:00
log . Println ( "Downloading day: " + current_loop_date + " from server '" + config . TautulliConfig [ q ] . TautulliName + "'." )
2022-11-29 18:32:30 +00:00
2022-07-04 15:47:59 +00:00
} else {
2022-11-29 18:32:30 +00:00
// Something is wrong. Quit.
2022-10-20 19:38:55 +00:00
log . Println ( "Unknown date error from server '" + config . TautulliConfig [ q ] . TautulliName + "'. Skipping." )
continue
2022-11-29 18:32:30 +00:00
2022-07-04 15:47:59 +00:00
}
2022-11-29 18:32:30 +00:00
// Loop through selected libraries for Tautullli API calls
2022-10-20 19:38:55 +00:00
for library_loop := 0 ; library_loop < len ( libraries ) ; library_loop ++ {
2022-07-04 15:47:59 +00:00
2022-10-20 19:38:55 +00:00
var library_str string = ""
var grouping string = ""
2022-07-04 15:47:59 +00:00
2022-10-20 19:38:55 +00:00
// 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 ] )
2022-07-04 15:47:59 +00:00
}
2022-10-20 19:38:55 +00:00
2022-11-29 18:32:30 +00:00
// Create string for selecting grouping feature in API call
2022-10-20 19:38:55 +00:00
if config . TautulliConfig [ q ] . TautulliGrouping {
grouping = "1"
} else {
grouping = "0"
}
2022-11-29 18:32:30 +00:00
// Get data from Tautulli for server, day, and library
2022-11-29 11:43:01 +00:00
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 )
2022-10-20 19:38:55 +00:00
if err != nil {
log . Println ( err )
}
2022-11-29 18:32:30 +00:00
// Loop through retrieved data from Tautulli
2022-10-20 19:38:55 +00:00
for j := 0 ; j < len ( tautulli_data ) ; j ++ {
if tautulli_data [ j ] . MediaType == "movie" || tautulli_data [ j ] . MediaType == "episode" || tautulli_data [ j ] . MediaType == "track" {
2022-12-10 12:42:37 +00:00
2022-11-29 11:43:01 +00:00
tautulli_entry := models . TautulliEntry {
2022-12-10 12:42:37 +00:00
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 ,
2022-10-20 19:38:55 +00:00
}
2022-11-29 18:32:30 +00:00
// Append to day data
2022-10-20 19:38:55 +00:00
wrapperr_day . Data = append ( wrapperr_day . Data , tautulli_entry )
}
}
2022-07-04 15:47:59 +00:00
}
2022-12-31 16:58:36 +00:00
// If the date is the current day, mark as incomplete so it can be refreshed the next time
2022-10-20 19:38:55 +00:00
if loop_time . Format ( "2006-01-02" ) == now . Format ( "2006-01-02" ) {
wrapperr_day . DataComplete = false
}
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// Add current Tautulli server to processed servers for this day
2022-11-27 17:12:11 +00:00
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 )
}
2022-11-29 18:32:30 +00:00
// If found in dataset, update the values. if not found, append to dataset array.
2022-10-20 19:38:55 +00:00
if found_date {
wrapperr_data [ found_date_index ] = wrapperr_day
} else {
wrapperr_data = append ( wrapperr_data , wrapperr_day )
}
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// Change the loop interval to change date
2022-10-20 19:38:55 +00:00
if loop_interval > 0 && use_loop_interval {
loop_interval -= 1
}
2022-07-04 15:47:59 +00:00
2022-11-29 18:32:30 +00:00
// If loop interval is enabled, and reached 0, break the for loop and mark the data download as incomplete
2022-10-20 19:38:55 +00:00
if loop_interval == 0 && use_loop_interval {
complete_date_loop = false
break
}
2022-07-04 15:47:59 +00:00
}
2022-11-29 18:32:30 +00:00
// 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
}
2022-07-04 15:47:59 +00:00
}
return wrapperr_data , complete_date_loop , nil
}
2022-11-29 11:43:01 +00:00
func WrapperrLoopData ( user_id int , config * models . WrapperrConfig , wrapperr_data [ ] models . WrapperrDay , wrapperr_reply models . WrapperrStatisticsReply ) ( models . WrapperrStatisticsReply , error ) {
2022-07-04 15:47:59 +00:00
end_loop_date := time . Unix ( int64 ( config . WrappedEnd ) , 0 )
start_loop_date := time . Unix ( int64 ( config . WrappedStart ) , 0 )
2022-10-15 20:07:15 +00:00
top_list_limit := config . WrapperrCustomize . StatsTopListLength
2022-07-04 15:47:59 +00:00
2022-11-29 11:43:01 +00:00
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
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
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 {
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
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 {
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
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 {
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
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 {
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
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 {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
var user_entry = models . WrapperrYearUserEntry {
2022-07-04 15:47:59 +00:00
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 ++ {
2022-12-10 12:42:37 +00:00
if wrapperr_year_show [ d ] . GrandparentTitle == wrapperr_data [ i ] . Data [ j ] . GrandparentTitle {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
var user_entry = models . WrapperrYearUserEntry {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
var user_entry = models . WrapperrYearUserEntry {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . User . UserMovies . Data . MoviesDuration = [ ] models . TautulliEntry { }
wrapperr_reply . User . UserMovies . Data . MoviesPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
break
}
wrapperr_reply . User . UserShows . Data . ShowsPlays = append ( wrapperr_reply . User . UserShows . Data . ShowsPlays , entry )
count += 1
}
2022-12-10 12:42:37 +00:00
// Find the longest duration spent on a certain episode
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . User . UserShows . Data . ShowsDuration = [ ] models . TautulliEntry { }
wrapperr_reply . User . UserShows . Data . ShowsPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . User . UserMusic . Data . TracksDuration = [ ] models . TautulliEntry { }
wrapperr_reply . User . UserMusic . Data . TracksPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
break
}
wrapperr_reply . YearStats . YearMovies . Data . MoviesPlays = append ( wrapperr_reply . YearStats . YearMovies . Data . MoviesPlays , entry )
2022-11-29 18:32:30 +00:00
count += 1
2022-07-04 15:47:59 +00:00
}
// 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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . YearStats . YearMovies . Data . MoviesDuration = [ ] models . TautulliEntry { }
wrapperr_reply . YearStats . YearMovies . Data . MoviesPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
break
}
wrapperr_reply . YearStats . YearShows . Data . ShowsPlays = append ( wrapperr_reply . YearStats . YearShows . Data . ShowsPlays , entry )
2022-11-29 18:32:30 +00:00
count += 1
2022-07-04 15:47:59 +00:00
}
// 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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . YearStats . YearShows . Data . ShowsDuration = [ ] models . TautulliEntry { }
wrapperr_reply . YearStats . YearShows . Data . ShowsPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
break
}
wrapperr_reply . YearStats . YearMusic . Data . ArtistsDuration = append ( wrapperr_reply . YearStats . YearMusic . Data . ArtistsDuration , entry )
count += 1
}
2022-11-09 10:42:44 +00:00
// Sort year artist array by plays
2022-07-04 15:47:59 +00:00
sortutil . DescByField ( wrapperr_year_artist , "Plays" )
count = 0
for _ , entry := range wrapperr_year_artist {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
wrapperr_reply . YearStats . YearMusic . Data . ArtistsDuration = [ ] models . TautulliEntry { }
wrapperr_reply . YearStats . YearMusic . Data . ArtistsPlays = [ ] models . TautulliEntry { }
2022-07-04 15:47:59 +00:00
wrapperr_reply . YearStats . YearMusic . Message = "No tracks processed."
}
2023-02-03 14:22:06 +00:00
// Create userID to obfuscated name struct
type obfuscatedUser struct {
userID int
newName string
}
// Create array to store userIDs and their obfucasted name
obfuscateCatalog := [ ] obfuscatedUser { }
// Create name generator engine
seed := time . Now ( ) . UTC ( ) . UnixNano ( )
nameGenerator := namegenerator . NewNameGenerator ( seed )
2022-07-04 15:47:59 +00:00
// 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
2022-11-29 11:43:01 +00:00
var wrapperr_year_user_summed [ ] models . WrapperrYearUserEntry
2023-02-03 14:22:06 +00:00
2022-07-04 15:47:59 +00:00
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
2023-02-03 14:22:06 +00:00
if config . WrapperrCustomize . ObfuscateOtherUsers {
// Declare variables
var newName string
currentUserID := wrapperr_year_user [ d ] . UserID
// Give new name
if currentUserID == 0 {
newName = "Managed user"
} else {
2023-02-03 17:22:08 +00:00
// Generate random name
newNameGen := nameGenerator . Generate ( )
// Try to improve random name
newNameGenArray := strings . Split ( newNameGen , "-" )
newNamePartOne := newNameGenArray [ 0 ]
newNamePartTwo := newNameGenArray [ 1 ]
newName = strings . Title ( newNamePartOne ) + " " + strings . Title ( newNamePartTwo )
2023-02-03 14:22:06 +00:00
}
// Create obfucasted struct type
obfuscatedUser := obfuscatedUser {
userID : wrapperr_year_user [ d ] . UserID ,
newName : newName ,
}
// Verify user is not already in catalog
userIDFound := false
for o := 0 ; o < len ( obfuscateCatalog ) ; o ++ {
if obfuscateCatalog [ o ] . userID == currentUserID {
userIDFound = true
break
}
}
// If not found, push to catalog
if ! userIDFound {
obfuscateCatalog = append ( obfuscateCatalog , obfuscatedUser )
}
2023-02-03 17:22:08 +00:00
// Obfuscate user in dataset if it is not themself
if currentUserID != user_id {
wrapperr_year_user [ d ] . FriendlyName = newName
wrapperr_year_user [ d ] . User = newName
wrapperr_year_user [ d ] . UserID = 0
}
2023-02-03 14:22:06 +00:00
}
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
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 {
2022-10-15 20:07:15 +00:00
if count >= top_list_limit && top_list_limit != 0 {
2022-07-04 15:47:59 +00:00
break
}
wrapperr_reply . YearStats . YearUsers . Data . UsersPlays = append ( wrapperr_reply . YearStats . YearUsers . Data . UsersPlays , entry )
2022-11-29 18:32:30 +00:00
count += 1
2022-07-04 15:47:59 +00:00
}
wrapperr_reply . YearStats . YearMovies . Message = "All users processed."
2022-10-15 21:47:53 +00:00
// 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
}
}
2022-07-04 15:47:59 +00:00
} else {
2022-11-29 11:43:01 +00:00
wrapperr_reply . YearStats . YearUsers . Data . UsersDuration = [ ] models . WrapperrYearUserEntry { }
wrapperr_reply . YearStats . YearUsers . Data . UsersPlays = [ ] models . WrapperrYearUserEntry { }
2022-07-04 15:47:59 +00:00
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 {
2023-02-03 14:22:06 +00:00
err , buddy_name , buddy_id , buddy_found , buddy_duration := GetUserShowBuddy ( config , wrapperr_reply . User . UserShows . Data . ShowsDuration [ 0 ] , user_id , wrapperr_data )
2022-07-04 15:47:59 +00:00
2022-11-29 11:43:01 +00:00
var show_buddy models . WrapperrShowBuddy
2022-07-04 15:47:59 +00:00
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 {
2023-02-03 14:22:06 +00:00
// Obfuscate username of buddy if enabled
if config . WrapperrCustomize . ObfuscateOtherUsers {
// Verify user is not already in catalog of users to maintain name consistancy
userIDFound := false
userNameFound := ""
for o := 0 ; o < len ( obfuscateCatalog ) ; o ++ {
if obfuscateCatalog [ o ] . userID == buddy_id {
userIDFound = true
userNameFound = obfuscateCatalog [ o ] . newName
break
}
}
// If not found, give random name
// If found, give buddy that username
if ! userIDFound {
buddy_name = nameGenerator . Generate ( )
} else {
buddy_name = userNameFound
}
// Remove buddy id
buddy_id = 0
}
2022-07-04 15:47:59 +00:00
show_buddy . Message = "Show buddy retrieved."
show_buddy . Error = false
show_buddy . BuddyName = buddy_name
2023-02-03 14:22:06 +00:00
show_buddy . BuddyID = buddy_id
2022-07-04 15:47:59 +00:00
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
}
2023-02-03 14:22:06 +00:00
func GetUserShowBuddy ( config * models . WrapperrConfig , top_show models . TautulliEntry , user_id int , wrapperr_data [ ] models . WrapperrDay ) ( error , string , int , bool , int ) {
2022-07-04 15:47:59 +00:00
2022-11-29 11:43:01 +00:00
var top_show_users [ ] models . WrapperrYearUserEntry
2022-07-04 15:47:59 +00:00
var top_show_buddy_name = "Something went wrong."
2023-02-03 14:22:06 +00:00
var top_show_buddy_id = 0
2022-07-04 15:47:59 +00:00
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 {
2022-11-29 11:43:01 +00:00
var user_entry = models . WrapperrYearUserEntry {
2022-07-04 15:47:59 +00:00
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 {
2023-02-03 14:22:06 +00:00
return nil , "None" , 0 , false , 0
2022-07-04 15:47:59 +00:00
}
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
2023-02-03 14:22:06 +00:00
top_show_buddy_id = user . UserID
2022-07-04 15:47:59 +00:00
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
2023-02-03 14:22:06 +00:00
top_show_buddy_id = user . UserID
2022-07-04 15:47:59 +00:00
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
2023-02-03 14:22:06 +00:00
top_show_buddy_id = user . UserID
2022-07-04 15:47:59 +00:00
top_show_buddy_duration = user . Duration
top_show_buddy_found = true
error_bool = nil
break
}
}
2023-02-03 14:22:06 +00:00
return error_bool , top_show_buddy_name , top_show_buddy_id , top_show_buddy_found , top_show_buddy_duration
2022-07-04 15:47:59 +00:00
}