Skip to content

Commit

Permalink
Users from Tautulli fix
Browse files Browse the repository at this point in the history
Sync details to user from every Tautulli server during user aggregation
Prioritize active status to true if any Tautulli server says so
Sync users before showing statistics and validating active state
Show Tautulli servers on user page
  • Loading branch information
aunefyren committed Nov 16, 2023
1 parent 2ec6d68 commit 8072003
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 99 deletions.
84 changes: 84 additions & 0 deletions modules/tautulli.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,87 @@ func TautulliGetUsersFromEveryServer() (tautulliUsers []models.TautulliUserGroup

return
}

func TautulliSyncUsersToWrapperr() (err error) {
err = nil

// Get all users from Tautulli
users, err := TautulliGetUsersFromEveryServer()
if err != nil {
log.Println("Failed to get users. Error: " + err.Error())
return errors.New("Failed to get users.")
}

// Create Wrapperr user objects array to fill
newUserArray := []models.WrapperrUser{}

// Create new Wrapperr user objects from Tautulli reply
for _, userGroup := range users {
var userMatch bool = false
var userIndex int = 0
for index, wrapperrUser := range newUserArray {
if userGroup.TautulliUser.UserID == wrapperrUser.UserID {
userMatch = true
userIndex = index
break
}
}

// User object is not in array, create and push to array
// If user object is in array, append new server and weigh active status
if !userMatch {
// Create bool from small int
var Active = false
if userGroup.TautulliUser.IsActive == 1 {
Active = true
} else if userGroup.TautulliUser.IsActive == 0 {
Active = false
} else {
log.Println("Failed to convert integer to bool. Error: " + err.Error())
return errors.New("Failed to convert integer to bool.")
}

// Create new object
wrapperrUser := models.WrapperrUser{
FriendlyName: userGroup.TautulliUser.FriendlyName,
User: userGroup.TautulliUser.Username,
UserID: userGroup.TautulliUser.UserID,
Email: userGroup.TautulliUser.Email,
TautulliServers: []string{userGroup.TautulliServer},
Active: Active,
Wrappings: []models.WrapperrHistoryEntry{},
}

// Push to array
newUserArray = append(newUserArray, wrapperrUser)
} else {
// If any server is "active", the user is active
if newUserArray[userIndex].Active == false && userGroup.TautulliUser.IsActive == 1 {
newUserArray[userIndex].Active = true
}

// Add new server
newUserArray[userIndex].TautulliServers = append(newUserArray[userIndex].TautulliServers, userGroup.TautulliServer)
}
}

// Update every new Wrapperr user with new object
for _, user := range newUserArray {
_, err = UsersGetUser(user.UserID)
if err != nil {
err = UsersSaveUserEntry(user)
if err != nil {
log.Println("Failed to save new user. Error: " + err.Error())
return errors.New("Failed to save new user.")
}
} else {
err = UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers)
if err != nil {
log.Println("Failed to update user. Error: " + err.Error())
return errors.New("Failed to update user.")
}
}
}

return
}
72 changes: 4 additions & 68 deletions routes/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func ApiWrapperCacheStatistics(context *gin.Context) {
return
}

context.JSON(http.StatusBadRequest, gin.H{"message": "Completed caching request.", "error": false, "data": *cachingComplete})
context.JSON(http.StatusOK, gin.H{"message": "Completed caching request.", "error": false, "data": *cachingComplete})
return
}

Expand Down Expand Up @@ -433,78 +433,14 @@ func ApiSyncTautulliUsers(context *gin.Context) {
return
}

users, err := modules.TautulliGetUsersFromEveryServer()
err = modules.TautulliSyncUsersToWrapperr()
if err != nil {
log.Println("Failed to get users. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get users."})
log.Println("Failed to sync users from Tautulli. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync users from Tautulli."})
context.Abort()
return
}

newUserArray := []models.WrapperrUser{}
for _, userGroup := range users {
var userMatch bool = false
var userIndex int = 0
for index, wrapperrUser := range newUserArray {
if userGroup.TautulliUser.UserID == wrapperrUser.UserID {
userMatch = true
userIndex = index
}
}

if !userMatch {
var Active = false
if userGroup.TautulliUser.IsActive == 1 {
Active = true
} else if userGroup.TautulliUser.IsActive == 0 {
Active = false
} else {
log.Println("Failed to convert integer to bool. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to convert integer to bool."})
context.Abort()
return
}

wrapperrUser := models.WrapperrUser{
FriendlyName: userGroup.TautulliUser.FriendlyName,
User: userGroup.TautulliUser.Username,
UserID: userGroup.TautulliUser.UserID,
Email: userGroup.TautulliUser.Email,
TautulliServers: []string{userGroup.TautulliServer},
Active: Active,
Wrappings: []models.WrapperrHistoryEntry{},
}

newUserArray = append(newUserArray, wrapperrUser)
} else {
if !newUserArray[userIndex].Active && userGroup.TautulliUser.IsActive == 1 {
newUserArray[userIndex].Active = true
}
newUserArray[userIndex].TautulliServers = append(newUserArray[userIndex].TautulliServers, userGroup.TautulliServer)
}
}

for _, user := range newUserArray {
_, err := modules.UsersGetUser(user.UserID)
if err != nil {
err = modules.UsersSaveUserEntry(user)
if err != nil {
log.Println("Failed to save new user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new user."})
context.Abort()
return
}
} else {
err = modules.UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers)
if err != nil {
log.Println("Failed to update user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user."})
context.Abort()
return
}
}
}

newusers, err := files.GetUsers()
if err != nil {
log.Println("Failed to new users. Error: " + err.Error())
Expand Down
69 changes: 41 additions & 28 deletions routes/both.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"log"
"net/http"
"strconv"
"strings"
"time"

"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -158,20 +159,21 @@ func ApiWrapperGetStatistics(context *gin.Context) {
}

// Check connection to every Tautulli server
for i := 0; i < len(config.TautulliConfig); i++ {
log.Println("Checking Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
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("Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'."})
context.Abort()
return
} else if !tautulli_state {
log.Println("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "' before retrieving statistics.")
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'."})
context.Abort()
return
}
err = modules.TautulliTestEveryServer()
if err != nil {
log.Println("Failed to test Tautulli server. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to test Tautulli server."})
context.Abort()
return
}

// Sync users from Tautulli to Wrapperr
err = modules.TautulliSyncUsersToWrapperr()
if err != nil {
log.Println("Failed to sync users from Tautulli. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync users from Tautulli."})
context.Abort()
return
}

var userName string = ""
Expand Down Expand Up @@ -213,17 +215,17 @@ func ApiWrapperGetStatistics(context *gin.Context) {
userEmail = plex_object.Email

// Check for friendly name using Tautulli
for i := 0; i < len(config.TautulliConfig); i++ {
_, new_username, new_friendlyname, _, new_active, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, userName)

if err == nil {
userName = new_username
userFriendlyName = new_friendlyname
userActive = new_active
}
break
wrapperrUser, err := modules.UsersGetUser(userId)
if err != nil {
log.Println("Failed to find Wrapperr user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find Wrapperr user."})
context.Abort()
return
}

userName = wrapperrUser.User
userFriendlyName = wrapperrUser.FriendlyName
userActive = wrapperrUser.Active
}

// Read payload from Post input
Expand All @@ -248,15 +250,12 @@ func ApiWrapperGetStatistics(context *gin.Context) {
UserNameFound := false

for i := 0; i < len(config.TautulliConfig); i++ {
new_id, new_username, user_friendlyname, new_email, new_active, 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)
new_id, _, _, _, _, err := modules.TautulliGetUserId(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey, strings.TrimSpace(wrapperr_request.PlexIdentity))

if err == nil {
UserNameFound = true
userName = new_username
userId = new_id
userEmail = new_email
userFriendlyName = user_friendlyname
userActive = new_active
break
}
}

Expand All @@ -266,6 +265,20 @@ func ApiWrapperGetStatistics(context *gin.Context) {
context.Abort()
return
}

// Check for friendly name using Tautulli
wrapperrUser, err := modules.UsersGetUser(userId)
if err != nil {
log.Println("Failed to find Wrapperr user. Error: " + err.Error())
context.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to find Wrapperr user."})
context.Abort()
return
}

userName = wrapperrUser.User
userEmail = wrapperrUser.Email
userFriendlyName = wrapperrUser.FriendlyName
userActive = wrapperrUser.Active
}

// If no username and no user_id has been declared at this point, something is wrong. Return error.
Expand Down
18 changes: 15 additions & 3 deletions web/assets/css/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -355,19 +355,31 @@ a {
background-color: var(--green);
border-radius: 0.25em;
width: min-content;
padding: 0 0.5em;
padding: 0.15em 0.5em;
margin: auto;
}

.user-active-false {
background-color: var(--red);
border-radius: 0.25em;
width: min-content;
padding: 0 0.5em;
padding: 0.15em 0.5em;
margin: auto;
}

.user-username, .user-friendlyname, .user-email {
.tautulli-server-badge {
background-color: var(--yellow);
border-radius: 0.25em;
width: min-content;
padding: 0.15em 0.5em;
margin: auto;
color: var(--black);
white-space: nowrap;
margin: 0.5em auto;
overflow: hidden;
}

.user-username, .user-friendlyname, .user-email, .user-tautulli-servers {
margin: 0.25em 0.5em;
text-align: center;
width: 15em;
Expand Down
7 changes: 7 additions & 0 deletions web/js/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function placeUsers(usersArray) {
<div class="user-header">Friendly name</div>
<div class="user-header">Email</div>
<div class="user-header-short">Active</div>
<div class="user-header">Tautulli servers</div>
</div>
`;

Expand All @@ -93,6 +94,11 @@ function placeUsers(usersArray) {
active_state_class = "user-active-false"
}

var tautulliServersString = ""
user.user_tautulli_servers.forEach(server => {
tautulliServersString += `<div class="tautulli-server-badge" title="${server}">${server}</div>`
});

var html = `
<div class="user-object">
<div class="user-details">
Expand All @@ -105,6 +111,7 @@ function placeUsers(usersArray) {
${user.user_active}
</div>
</div>
<div class="user-tautulli-servers">${tautulliServersString}</div>
</div>
${historyDiv}
Expand Down

0 comments on commit 8072003

Please sign in to comment.