├── .config └── pum.ini ├── .dockerignore ├── .github └── workflows │ ├── docker-image.yml │ ├── latest.yml │ └── main.yml ├── CHANGES ├── Dockerfile ├── README.md ├── VERSION ├── config.py ├── images ├── pum_logo.ico └── pum_logo.png ├── pum.py └── requirements.txt /.config/pum.ini: -------------------------------------------------------------------------------- 1 | [DATABASE] 2 | host = localhost 3 | user = pumuser 4 | passwd = PUM-USER 5 | db = pum 6 | 7 | [CONF] 8 | warn_user_near_expiration = 1 9 | warn_user_near_expiration_delay = 30 10 | warn_user_account_expiration = 1 11 | remove_user_access = 0 12 | delete_user = 0 13 | delete_user_delay = 30 14 | plex_db_sync = 24 15 | hide_guest = 1 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | README.md 2 | .dockerignore 3 | Dockerfile 4 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "Master" ] 6 | pull_request: 7 | branches: [ "Master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 19 | -------------------------------------------------------------------------------- /.github/workflows/latest.yml: -------------------------------------------------------------------------------- 1 | name: Docker Latest Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Build the Docker image 16 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 17 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Publish-2-docker 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "Master" branch 8 | push: 9 | branches: [ "Master" ] 10 | pull_request: 11 | branches: [ "Master" ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v3 27 | 28 | # Log to docker 29 | - name: Login to Docker Hub 30 | uses: docker/login-action@v1 31 | with: 32 | username: ${{ secrets.DOCKER_USERNAME }} 33 | password: ${{ secrets.DOCKER_ACCESS_TOKEN }} 34 | 35 | # set docker build 36 | - name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v1 38 | 39 | # Push to docker 40 | - name: Build and push 41 | uses: docker/build-push-action@v2 42 | with: 43 | context: . 44 | file: ./Dockerfile 45 | push: true 46 | tags: ${{ secrets.DOCKER_USERNAME }}/plex_user_manager:latest 47 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | v0.48 2 | - corrected mail to add new user 3 | - changed config.ini access 4 | - correct offline status to update at every sync 5 | - deleted servers are remove from db 6 | v0.47 7 | - corrected remove libraries when multiple changes 8 | v0.46 9 | - add user visualy completed 10 | - corrected expired users not removed from plexlibraries DB 11 | v0.45 12 | - libraries will be removed from expired users 13 | v0.44 14 | - all pum process killed on exit 15 | - when adding a server, only this one syncs 16 | - all filters now display correctly 17 | - clean old entries while syncing 18 | - add user panel 19 | V0.43 20 | - added plexservers and plexlibraries libraries 21 | - added server tab 22 | - GUI code optimisation 23 | - offline servers now detected 24 | - servers can be deleted and added 25 | - sections are now seen in user menu 26 | - hide guest and hide users with no libraries done 27 | - some minor bugs 28 | - user can now be removed from plex 29 | - PUM now can syn on standalone (default every 24H) 30 | V0.42 31 | - command to change plex url & token 32 | - graphical changes on general an user option tab 33 | - general and user options tab completely functionnal 34 | - hide guest user function added 35 | - removed servers don't appear in treeview anymore 36 | V0.41 37 | - added delete server function 38 | - added more settings 39 | - added plex url & token setting 40 | V0.40 41 | - added CHANGES 42 | - changed dates for expired account to work in docker 43 | - removed icon incompatible for now 44 | - added number of servers and libraries 45 | - changed screen size -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | python3-tk \ 5 | mariadb-server \ 6 | python3-pip \ 7 | wget 8 | 9 | RUN python3 -m venv /venv 10 | RUN ls -la /venv 11 | ENV PATH="/venv/bin:$PATH" 12 | RUN echo $PATH 13 | RUN chmod +x /venv/bin/activate 14 | RUN /venv/bin/python3 -m pip install --upgrade pip 15 | # RUN wget https://raw.githubusercontent.com/blacktwin/JBOPS/master/utility/plex_api_share.py \ 16 | 17 | COPY requirements.txt . 18 | RUN pip install -r requirements.txt 19 | 20 | COPY . . 21 | 22 | EXPOSE 8010 23 | 24 | ENV MYSQL_ROOT_PASSWORD=PUM-USER 25 | 26 | RUN service mariadb status | grep -q 'Active: active (running)' || service mariadb start 27 | RUN service mariadb status 28 | 29 | # Create a new database and user 30 | RUN service mysql start \ 31 | && mysql -u root -p$MYSQL_ROOT_PASSWORD -e "CREATE SCHEMA IF NOT EXISTS pum DEFAULT CHARACTER SET utf8;" \ 32 | && mysql -u root -p$MYSQL_ROOT_PASSWORD -e "CREATE USER IF NOT EXISTS 'pumuser'@'%' IDENTIFIED BY 'PUM-USER';" \ 33 | && mysql -u root -p$MYSQL_ROOT_PASSWORD -e "GRANT ALL PRIVILEGES ON pum.* TO 'pumuser'@'%';" \ 34 | && mysql -u root -p$MYSQL_ROOT_PASSWORD -e "FLUSH PRIVILEGES;" 35 | 36 | CMD ["/venv/bin/python", "pum.py"] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Plex User Manager 2 | 3 | Plex User Manager is a tool to help manage your users from your plex servers. As we now can't clearly identify our users, this will help you make the difference. 4 | Creation, edition, or deletion of users and access is no longer a problem. 5 | Also adding access expiration to stop user access when date passed. 6 | I'd be glad to have some help with python and docker(GitHub). 7 | 8 | left to do: 9 | - in conf tab buttons are done functions not yet working 10 | * add mail or tautulli conf to send message before account expiration 11 | * warn users near expiation (with delay / mail or tautulli) 12 | * warn user of expiration (mail or tautulli) 13 | - build docker container to display pum_web.py 14 | - add help text 15 | - add CHANGES to info tab 16 | 17 | bugs: 18 | - minor gui diplay glitch 19 | 20 | Usage: 21 | - run python3 pum.py 22 | 23 | ![screen](https://user-images.githubusercontent.com/9554635/172479259-af074417-b187-4483-8e98-91dde70861ba.png) 24 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.48 -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | from configparser import ConfigParser 2 | 3 | 4 | # global config_path 5 | config_path = ".config/" 6 | api_path = "api/" 7 | 8 | # Read config.ini file 9 | config_object = ConfigParser() 10 | config_object.read(config_path + "pum.ini") 11 | 12 | userinfo = config_object["CONF"] 13 | warn_user_near_expiration = userinfo["warn_user_near_expiration"] 14 | warn_user_near_expiration_delay = userinfo["warn_user_near_expiration_delay"] 15 | warn_user_account_expiration = userinfo["warn_user_account_expiration"] 16 | remove_user_access = userinfo["remove_user_access"] 17 | delete_user = userinfo["delete_user"] 18 | delete_user_delay_str = userinfo["delete_user_delay"] 19 | plex_db_sync_str = userinfo["plex_db_sync"] 20 | hide_guest_str = userinfo["hide_guest"] 21 | hide_no_lib_users_str = userinfo["hide_no_lib_users"] 22 | # cron_conf_str = userinfo["cron_conf"] 23 | nbr_backup_to_keep_str = userinfo["nbr_backup_to_keep"] 24 | sync_plex_delai = userinfo["sync_plex_delai"] 25 | config_object = ConfigParser() 26 | config_object.read(config_path + "pum.ini") 27 | userinfo = config_object["DATABASE"] 28 | db_host = userinfo["host"] 29 | db_user = userinfo["user"] 30 | db_passwd = userinfo["passwd"] 31 | db_db = userinfo["db"] 32 | 33 | -------------------------------------------------------------------------------- /images/pum_logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nexius2/Plex_User_Manager/8a28e09cf5dc4339552d6e4873b832ee9efe6702/images/pum_logo.ico -------------------------------------------------------------------------------- /images/pum_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nexius2/Plex_User_Manager/8a28e09cf5dc4339552d6e4873b832ee9efe6702/images/pum_logo.png -------------------------------------------------------------------------------- /pum.py: -------------------------------------------------------------------------------- 1 | # ********************************************************************************************************************** 2 | # *************************************************** imports ********************************************************** 3 | # ********************************************************************************************************************** 4 | import tkinter 5 | from tkinter import * 6 | from tkinter import messagebox 7 | from tkinter import ttk 8 | from configparser import ConfigParser 9 | from tkinter.messagebox import askyesno 10 | import mysql.connector, os, sys, time 11 | from plexapi.server import PlexServer 12 | import requests 13 | import plexapi 14 | import glob 15 | import json 16 | import threading 17 | from config import * 18 | 19 | # ********************************************************************************************************************** 20 | # ********************************************* GUI settings *********************************************************** 21 | # ********************************************************************************************************************** 22 | 23 | root = Tk() 24 | root.title('Plex User Manager') 25 | root.geometry("1100x800") 26 | root.configure(bg="#282828") 27 | style = ttk.Style(root) 28 | style.theme_create("pum_theme", parent="alt", settings={ 29 | "TNotebook": {"configure": {"tabmargins": [2, 5, 2, 0], 30 | "tabposition": 'ne', 31 | "background": "#1F1F1F", 32 | "borderwidth": "0", 33 | # "font": "Helvetica 10 bold", 34 | }}, 35 | "TNotebook.Tab": { 36 | "configure": {"padding": [30, 1], 37 | "background": "#1F1F1F", 38 | "foreground": "white", 39 | "borderwidth": "0", 40 | "fieldbackground": "#1F1F1F"}, 41 | "map": {"background": [("selected", "#383838")], 42 | "foreground": [('selected', "#e5a00d")], 43 | "borderwidth": "0"}}}) 44 | 45 | style.theme_use("pum_theme") 46 | # set tabs 47 | notebook = ttk.Notebook(root, style='TNotebook') 48 | # user panel 49 | user_tab: Frame = Frame(notebook) # frame for user page 50 | notebook.add(user_tab, text="users") 51 | user_tab.configure(background="#1F1F1F") 52 | # server panel 53 | server_tab: Frame = Frame(notebook) # frame for server page 54 | notebook.add(server_tab, text="servers") 55 | server_tab.configure(background="#1F1F1F") 56 | # setting panel 57 | conf_tab: Frame = Frame(notebook) # frame for conf page 58 | notebook.add(conf_tab, text="Settings") 59 | conf_tab.configure(background="#1F1F1F") 60 | # help & info panel 61 | help_and_info_tab: Frame = Frame(notebook) # frame for help & info page 62 | notebook.add(help_and_info_tab, text="Help & info") 63 | help_and_info_tab.configure(background="#1F1F1F") 64 | notebook.pack(expand=True, fill="both") # expand to space not used 65 | 66 | # Configure treeview header color 67 | style.configure('Treeview.Heading', 68 | background="#1F1F1F", 69 | font="Helvetica 10 bold", 70 | foreground="grey") 71 | 72 | # Configure the Treeview Colors 73 | style.configure("Treeview", 74 | background="#282828", 75 | foreground="white", 76 | rowheight=25, 77 | relief="flat", 78 | borderwidth=0, 79 | fieldbackground="#282828") 80 | 81 | # Change Selected Color 82 | style.map('Treeview', 83 | background=[('selected', "#383838")], 84 | #font=[('selected', "bold")], 85 | foreground=[('selected', "#e5a00d")]) 86 | 87 | # ********************************************************************************************************************** 88 | # ************************************************* VARIABLES ********************************************************** 89 | # ********************************************************************************************************************** 90 | # var for new servers 91 | NEW_PLEX_SERVER = "" 92 | 93 | """ 94 | # global config_path 95 | config_path = ".config/" 96 | api_path = "api/" 97 | 98 | # Read config.ini file 99 | config_object = ConfigParser() 100 | config_object.read(config_path + "pum.ini") 101 | # Get the conf info 102 | userinfo = config_object["CONF"] 103 | warn_user_near_expiration = userinfo["warn_user_near_expiration"] 104 | warn_user_near_expiration_delay = userinfo["warn_user_near_expiration_delay"] 105 | warn_user_account_expiration = userinfo["warn_user_account_expiration"] 106 | remove_user_access = userinfo["remove_user_access"] 107 | delete_user = userinfo["delete_user"] 108 | delete_user_delay_str = userinfo["delete_user_delay"] 109 | plex_db_sync_str = userinfo["plex_db_sync"] 110 | hide_guest_str = userinfo["hide_guest"] 111 | hide_no_lib_users_str = userinfo["hide_no_lib_users"] 112 | # cron_conf_str = userinfo["cron_conf"] 113 | nbr_backup_to_keep_str = userinfo["nbr_backup_to_keep"] 114 | sync_plex_delai = userinfo["sync_plex_delai"] 115 | 116 | # DB connection 117 | # Read config.ini file 118 | config_object = ConfigParser() 119 | config_object.read(config_path + "pum.ini") 120 | # Get the conf info 121 | userinfo = config_object["DATABASE"] 122 | db_host = userinfo["host"] 123 | db_user = userinfo["user"] 124 | db_passwd = userinfo["passwd"] 125 | db_db = userinfo["db"] 126 | """ 127 | 128 | # connect to MySQL 129 | mydb = mysql.connector.connect( 130 | host=db_host, 131 | user=db_user, 132 | passwd=db_passwd, 133 | database=db_db, 134 | auth_plugin='mysql_native_password') 135 | # Create a cursor and initialize it 136 | cursor = mydb.cursor() 137 | 138 | 139 | # ********************************************************************************************************************** 140 | # ************************************************* DB create ********************************************************** 141 | # ********************************************************************************************************************** 142 | def db_create(): 143 | # Create database if not exists 144 | cursor.execute("CREATE DATABASE IF NOT EXISTS pum") 145 | 146 | # Create table plexusers 147 | cursor.execute("CREATE TABLE IF NOT EXISTS plexusers(first_name VARCHAR(255), \ 148 | last_name VARCHAR(255), \ 149 | username VARCHAR(255), \ 150 | email VARCHAR(255), \ 151 | serverName VARCHAR(255) NOT NULL, \ 152 | account_expire_date DATE, \ 153 | sections VARCHAR(255), \ 154 | allowSync VARCHAR(10), \ 155 | camera VARCHAR(10), \ 156 | channels VARCHAR(10), \ 157 | filterMovies VARCHAR(255) DEFAULT NULL, \ 158 | filterMusic VARCHAR(255) DEFAULT NULL, \ 159 | filterTelevision VARCHAR(255) DEFAULT NULL, \ 160 | title VARCHAR(255), \ 161 | is_on_plex INT(10), \ 162 | account_creation_date DATE, \ 163 | account_renewed_date DATE, \ 164 | userID INT NOT NULL, \ 165 | description VARCHAR(255), \ 166 | hidden INT(10) DEFAULT 0, \ 167 | PRIMARY KEY(userID, serverName) );") 168 | 169 | # Create table tempusers 170 | cursor.execute("CREATE TABLE IF NOT EXISTS tempusers(serverName VARCHAR(255) NOT NULL, \ 171 | userID INT NOT NULL, \ 172 | PRIMARY KEY(userID, serverName) );") 173 | 174 | # Create table plexservers 175 | cursor.execute("CREATE TABLE IF NOT EXISTS plexservers(serverName VARCHAR(255), \ 176 | token VARCHAR(255) NOT NULL, \ 177 | url VARCHAR(255) NOT NULL, \ 178 | server_offline INT(10) default NULL,\ 179 | PRIMARY KEY(url, token) );") 180 | 181 | # Create table plexlibraries 182 | cursor.execute("CREATE TABLE IF NOT EXISTS plexlibraries(serverName VARCHAR(255) NOT NULL, \ 183 | email VARCHAR(255) NOT NULL, \ 184 | library VARCHAR(255) NOT NULL, \ 185 | PRIMARY KEY(library, serverName, email) );") 186 | 187 | # Create table plexfilterMovies 188 | cursor.execute("CREATE TABLE IF NOT EXISTS plexfilterMovies(serverName VARCHAR(255) NOT NULL, \ 189 | email VARCHAR(255) NOT NULL, \ 190 | filterMovies VARCHAR(255) NOT NULL, \ 191 | PRIMARY KEY(filterMovies, serverName, email) );") 192 | 193 | # Create table plexfilterMusic 194 | cursor.execute("CREATE TABLE IF NOT EXISTS plexfilterMusic(serverName VARCHAR(255) NOT NULL, \ 195 | email VARCHAR(255) NOT NULL, \ 196 | filterMusic VARCHAR(255) NOT NULL, \ 197 | PRIMARY KEY(filterMusic, serverName, email) );") 198 | 199 | # Create table plexfilterTelevision 200 | cursor.execute("CREATE TABLE IF NOT EXISTS plexfilterTelevision(serverName VARCHAR(255) NOT NULL, \ 201 | email VARCHAR(255) NOT NULL, \ 202 | filterTelevision VARCHAR(255) NOT NULL, \ 203 | PRIMARY KEY(filterTelevision, serverName, email) );") 204 | 205 | # ********************************************************************************************************************** 206 | # *********************************************** import data ********************************************************** 207 | # ********************************************************************************************************************** 208 | def import_data(): 209 | global NEW_PLEX_SERVER, NEW_PLEX_URL, NEW_PLEX_TOKEN 210 | root.title('Plex User Manager... loading data...') 211 | """ 212 | config_path = ".config/" 213 | api_path = "api/" 214 | # DB connection 215 | # Read config.ini file 216 | config_object = ConfigParser() 217 | config_object.read(config_path + "pum.ini") 218 | # Get the conf info 219 | userinfo = config_object["DATABASE"] 220 | db_host = userinfo["host"] 221 | db_user = userinfo["user"] 222 | db_passwd = userinfo["passwd"] 223 | db_db = userinfo["db"] 224 | 225 | # Read config.ini file 226 | config_object = ConfigParser() 227 | config_object.read(config_path + "pum.ini") 228 | # Get the conf info 229 | userinfo = config_object["CONF"] 230 | remove_user_access = userinfo["remove_user_access"] 231 | """ 232 | # connect to MySQL 233 | mydb = mysql.connector.connect( 234 | host=db_host, 235 | user=db_user, 236 | passwd=db_passwd, 237 | database=db_db, 238 | auth_plugin='mysql_native_password') 239 | # Create a cursor and initialize it 240 | cursor = mydb.cursor() 241 | 242 | # clean old entries 243 | cursor.execute( 244 | "SELECT * FROM plexfilterMovies WHERE NOT EXISTS(SELECT NULL FROM plexusers WHERE plexusers.email = plexfilterMovies.email);") 245 | deleted_filterMovies = cursor.fetchall() 246 | for delet_filtMov in deleted_filterMovies: 247 | cursor.execute("DELETE FROM plexfilterMovies WHERE serverName = %s AND email = %s AND library = %s;", 248 | [delet_filtMov[0], delet_filtMov[1], delet_filtMov[2]]) 249 | print("Old entry : filterMovies " + delet_filtMov[2] + " has been removed on server " + delet_filtMov[0] + " for user " + 250 | delet_filtMov[1]) 251 | cursor.execute( 252 | "SELECT * FROM plexfilterMusic WHERE NOT EXISTS(SELECT NULL FROM plexusers WHERE plexusers.email = plexfilterMusic.email);") 253 | deleted_filterMusic = cursor.fetchall() 254 | for delet_filtMus in deleted_filterMusic: 255 | cursor.execute("DELETE FROM plexfilterMusic WHERE serverName = %s AND email = %s AND library = %s;", 256 | [delet_filtMus[0], delet_filtMus[1], delet_filtMus[2]]) 257 | print("Old entry : filterMusic " + delet_filtMus[2] + " has been removed on server " + delet_filtMus[0] + " for user " + 258 | delet_filtMus[1]) 259 | cursor.execute( 260 | "SELECT * FROM plexfilterTelevision WHERE NOT EXISTS(SELECT NULL FROM plexusers WHERE plexusers.email = plexfilterTelevision.email);") 261 | deleted_filterTelevision = cursor.fetchall() 262 | for delet_filtTV in deleted_filterTelevision: 263 | cursor.execute("DELETE FROM plexfilterTelevision WHERE serverName = %s AND email = %s AND library = %s;", 264 | [delet_filtTV[0], delet_filtTV[1], delet_filtTV[2]]) 265 | print("Old entry : filterTelevision " + delet_filtTV[2] + " has been removed on server " + delet_filtTV[0] + " for user " + delet_filtTV[1]) 266 | cursor.execute( 267 | "SELECT * FROM plexlibraries WHERE NOT EXISTS(SELECT NULL FROM plexusers WHERE plexusers.email = plexlibraries.email);") 268 | deleted_libraries = cursor.fetchall() 269 | for delet_lib in deleted_libraries: 270 | cursor.execute("DELETE FROM plexlibraries WHERE serverName = %s AND email = %s AND library = %s;", [delet_lib[0], delet_lib[1], delet_lib[2]]) 271 | print("Old entry : library " + delet_lib[2] + " has been removed on server " + delet_lib[0] + " for user " + delet_lib[1]) 272 | 273 | # remove user access if expired 274 | if remove_user_access == "1": 275 | cursor.execute("SELECT DISTINCT plexusers.email, plexusers.serverName FROM plexusers, plexlibraries WHERE plexusers.account_expire_date < CURDATE() AND plexusers.email = plexlibraries.email AND plexusers.serverName = plexlibraries.serverName;") 276 | expired_user = cursor.fetchall() 277 | print("following users will have access removed:") 278 | print(expired_user) 279 | for exp_usr in expired_user: 280 | #print("deleting: " + exp_usr) 281 | usr_job = expired_user[0] 282 | #print(expired_user[0]) 283 | """ 284 | config_path = ".config/" 285 | # DB connection 286 | # Read config.ini file 287 | config_object = ConfigParser() 288 | config_object.read(config_path + "pum.ini") 289 | # Get the conf info 290 | userinfo = config_object["DATABASE"] 291 | db_host = userinfo["host"] 292 | db_user = userinfo["user"] 293 | db_passwd = userinfo["passwd"] 294 | db_db = userinfo["db"] 295 | """ 296 | 297 | # connect to MySQL 298 | mydb = mysql.connector.connect( 299 | host=db_host, 300 | user=db_user, 301 | passwd=db_passwd, 302 | database=db_db, 303 | auth_plugin='mysql_native_password') 304 | # Create a cursor and initialize it 305 | cursor = mydb.cursor() 306 | # get server token and url 307 | cursor.execute("SELECT * FROM plexservers WHERE serverName = %s;", [usr_job[1]]) 308 | selected_plex_info = cursor.fetchall() 309 | # print(type(selected_plex_info)) 310 | new_selected_plex_info = selected_plex_info[0] 311 | PLEX_URL = str(new_selected_plex_info[2]) 312 | PLEX_TOKEN = str(new_selected_plex_info[1]) 313 | # write to plex_api config.ini 314 | plex_config_object = ConfigParser() 315 | plex_config_object.read(plexapi.CONFIG_PATH) 316 | plex_config_object['auth']['server_baseurl'] = PLEX_URL 317 | plex_config_object['auth']['server_token'] = PLEX_TOKEN 318 | with open(plexapi.CONFIG_PATH, 'w') as plex_configfile: 319 | plex_config_object.write(plex_configfile) 320 | # export json from plex 321 | os.system("python3 plex_api_share.py --unshare --user " + usr_job[0]) 322 | cursor.execute("UPDATE plexusers SET sections = 'NULL' WHERE email = %s AND serverName = %s;", [usr_job[0], usr_job[1]]) 323 | print("unshare all libraries for user " + usr_job[0] + " on server " + usr_job[1]) 324 | cursor.execute("DELETE FROM plexlibraries WHERE email = %s AND serverName = %s;", [usr_job[0], usr_job[1]]) 325 | 326 | 327 | if NEW_PLEX_SERVER: 328 | records = [NEW_PLEX_SERVER, NEW_PLEX_URL, NEW_PLEX_TOKEN] 329 | else: 330 | cursor.execute("SELECT * FROM plexservers;") 331 | records = cursor.fetchall() 332 | for record in records: 333 | #serverName = record[0] 334 | PLEX_TOKEN = record[1] 335 | PLEX_URL = record[2] 336 | sess = requests.Session() 337 | # Ignore verifying the SSL certificate 338 | sess.verify = False # '/path/to/certfile' 339 | # If verify is set to a path to a directory, 340 | # the directory must have been processed using the c_rehash utility supplied 341 | # with OpenSSL. 342 | if sess.verify is False: 343 | # Disable the warning that the request is insecure, we know that... 344 | import urllib3 345 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 346 | # test if server online 347 | try: 348 | plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) 349 | except: 350 | cursor.execute("UPDATE plexservers SET server_offline = 1 WHERE token = %s AND url = %s;", 351 | [PLEX_TOKEN, PLEX_URL]) 352 | #return 353 | else: 354 | # if the name has changed then update 355 | plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) 356 | if record[0] != plex.friendlyName: 357 | cursor.execute("UPDATE plexservers SET serverName = %s WHERE token = %s AND url = %s;", [plex.friendlyName, PLEX_TOKEN, PLEX_URL]) 358 | # server is online 359 | cursor.execute("UPDATE plexservers SET server_offline = 0 WHERE token = %s AND url = %s;", [PLEX_TOKEN, PLEX_URL]) 360 | serverName = plex.friendlyName 361 | #print(serverName) 362 | #print(PLEX_TOKEN) 363 | #print(PLEX_URL) 364 | #print(PLEX_TOKEN) 365 | plex_config_object = ConfigParser() 366 | plex_config_object.read(plexapi.CONFIG_PATH) 367 | plex_config_object['auth']['server_baseurl'] = PLEX_URL 368 | plex_config_object['auth']['server_token'] = PLEX_TOKEN 369 | with open(plexapi.CONFIG_PATH, 'w') as plex_configfile: 370 | plex_config_object.write(plex_configfile) 371 | # export json from plex 372 | os.system("python3 plex_api_share.py --backup") 373 | print(serverName + " json created") 374 | NEW_PLEX_SERVER = "" 375 | 376 | # empty table tempusers 377 | cursor.execute("TRUNCATE TABLE tempusers;") 378 | # import user data 379 | path_to_json = glob.glob(r'./*.json') 380 | # check json file 381 | if not path_to_json: 382 | print("No JSON files... ") 383 | #quit() 384 | # loop for every json file 385 | for jfile in path_to_json: 386 | # load json 387 | json_data = open(jfile).read() 388 | json_obj = json.loads(json_data) 389 | # do validation and checks before insert 390 | def validate_string(val): 391 | if val != None: 392 | if type(val) is int: 393 | # for x in val: 394 | # print(x) 395 | return str(val).encode('utf-8') 396 | else: 397 | return val 398 | 399 | # remove not wanted characters 400 | for mydict in json_obj: 401 | # check if filterMovies exist and put it in DB 402 | if mydict['filterMovies']: 403 | filtermovie_entrie = mydict['filterMovies'] 404 | cursor.execute("DELETE FROM plexfilterMovies WHERE email = %s AND serverName = %s;", 405 | [mydict['email'], mydict['serverName']]) 406 | for key in filtermovie_entrie['label']: 407 | cursor.execute("INSERT INTO plexfilterMovies SET filterMovies = %s, email = %s, serverName = %s;", 408 | [key, mydict['email'], mydict['serverName']]) 409 | 410 | # check if filterMusic exist and put it in DB 411 | if mydict['filterMusic']: 412 | filterMusic_entrie = mydict['filterMusic'] 413 | cursor.execute("DELETE FROM plexfilterMusic WHERE email = %s AND serverName = %s;", 414 | [mydict['email'], mydict['serverName']]) 415 | for key in filterMusic_entrie['label']: 416 | cursor.execute("INSERT INTO plexfilterMusic SET filterMusic = %s, email = %s, serverName = %s;", 417 | [key, mydict['email'], mydict['serverName']]) 418 | 419 | # check if filterTelevision exist and put it in DB 420 | if mydict['filterTelevision']: 421 | filterTelevision_entrie = mydict['filterTelevision'] 422 | cursor.execute("DELETE FROM plexfilterTelevision WHERE email = %s AND serverName = %s;", 423 | [mydict['email'], mydict['serverName']]) 424 | for key in filterTelevision_entrie['label']: 425 | cursor.execute( 426 | "INSERT INTO plexfilterTelevision SET filterTelevision = %s, email = %s, serverName = %s;", 427 | [key, mydict['email'], mydict['serverName']]) 428 | 429 | # check if sections exist and put it in DB 430 | if mydict['sections']: 431 | sections_entrie = mydict['sections'] 432 | #print(sections_entrie) 433 | cursor.execute("DELETE FROM plexlibraries WHERE email = %s AND serverName = %s;", 434 | [mydict['email'], mydict['serverName']]) 435 | for key in sections_entrie: 436 | cursor.execute( 437 | "INSERT INTO plexlibraries SET library = %s, email = %s, serverName = %s;", 438 | [key, mydict['email'], mydict['serverName']]) 439 | 440 | # create or update user 441 | #print(mydict['userID']) 442 | #print(mydict['serverName']) 443 | #print(mydict['allowSync']) 444 | #print(mydict['camera']) 445 | #print(mydict['channels']) 446 | #print(mydict['email']) 447 | #print(mydict['title']) 448 | #print(mydict['username']) 449 | 450 | cursor.execute("INSERT IGNORE INTO plexusers SET userID = %s, account_creation_date = CURDATE(), serverName = %s;", [mydict['userID'], mydict['serverName']]) 451 | cursor.execute("UPDATE plexusers SET allowSync = %s, camera = %s, channels = %s, email = %s, title = %s, username = %s, is_on_plex = 1 WHERE userID = %s AND serverName = %s;", [ 452 | mydict['allowSync'], mydict['camera'], mydict['channels'], mydict['email'], mydict['title'], mydict['username'], mydict['userID'], mydict['serverName']]) 453 | 454 | # add to temp db to compare 455 | cursor.execute("INSERT IGNORE INTO tempusers SET userID = %s, serverName = %s;", [mydict['userID'], mydict['serverName']]) 456 | print("json files imported to db") 457 | 458 | 459 | 460 | # delete old json file 461 | app_dir = os.listdir(os.getcwd()) 462 | for json_item in app_dir: 463 | if json_item.endswith(".json"): 464 | os.remove(os.path.join(os.getcwd(), json_item)) 465 | # compare tempusers and plexusers and remove old user 466 | cursor.execute("DELETE FROM plexusers WHERE is_on_plex = 0 AND userID NOT in (SELECT userID FROM tempusers) AND serverName IN (SELECT serverName FROM tempusers);") 467 | # Commit changes 468 | mydb.commit() 469 | # Close connexion 470 | mydb.close() 471 | '''# clear my_server_tree 472 | my_user_tree.delete(*my_user_tree.get_children()) 473 | # get server data back 474 | query_user_info()''' 475 | # clear my_server_tree 476 | my_user_tree.delete(*my_user_tree.get_children()) 477 | # get server data back 478 | root.title('Plex User Manager') 479 | query_user_info() 480 | multithreading_sync_data() 481 | print("All data imported") 482 | 483 | 484 | def multithreading_import_data(): 485 | thread_import_data = threading.Thread(target=import_data, name="import data") 486 | thread_import_data.start() 487 | 488 | def sync_data(): 489 | #time.sleep(86400) 490 | time.sleep(int(sync_plex_delai)*3600) 491 | multithreading_import_data() 492 | print("waited for " + sync_plex_delai + " Hours, data synced") 493 | # clear my_server_tree 494 | my_user_tree.delete(*my_user_tree.get_children()) 495 | # get server data back 496 | query_user_info() 497 | 498 | def multithreading_sync_data(): 499 | thread_sync_data = threading.Thread(target=sync_data, name="sync data") 500 | thread_sync_data.daemon = True 501 | thread_sync_data.start() 502 | 503 | db_create() 504 | 505 | # check if server is configured to sync 506 | cursor.execute("SELECT serverName FROM plexservers;") 507 | records = cursor.fetchall() 508 | if records: 509 | multithreading_import_data() 510 | else: 511 | tkinter.messagebox.showinfo(title="Server missing", message="please add server in server panel") 512 | 513 | 514 | 515 | # ********************************************************************************************************************** 516 | # ********************************************* user panel ************************************************************* 517 | # ********************************************************************************************************************** 518 | 519 | # missing if server_offline == 1 then do not update user 520 | 521 | def query_user_info(): 522 | # DB connection 523 | """ 524 | # Read config.ini file 525 | config_object = ConfigParser() 526 | config_object.read(config_path + "pum.ini") 527 | # Get the conf info 528 | userinfo = config_object["DATABASE"] 529 | db_host = userinfo["host"] 530 | db_user = userinfo["user"] 531 | db_passwd = userinfo["passwd"] 532 | db_db = userinfo["db"] 533 | """ 534 | # connect to MySQL 535 | mydb = mysql.connector.connect( 536 | host=db_host, 537 | user=db_user, 538 | passwd=db_passwd, 539 | database=db_db, 540 | auth_plugin='mysql_native_password') 541 | # Create a cursor and initialize it 542 | cursor = mydb.cursor() 543 | # Select info from db 544 | if hide_guest_str == "1" and hide_no_lib_users_str != "1": 545 | cursor.execute("SELECT * FROM plexusers WHERE title != 'Guest';") 546 | if hide_no_lib_users_str == "1" and hide_guest_str != "1": 547 | #cursor.execute("SELECT * FROM plexusers WHERE hidden != '1';") 548 | cursor.execute("SELECT DISTINCT plexusers.* FROM plexusers, plexlibraries WHERE plexusers.email = plexlibraries.email AND plexusers.serverName = plexlibraries.serverName;") 549 | if hide_no_lib_users_str == "1" and hide_guest_str == "1": 550 | cursor.execute("SELECT DISTINCT plexusers.* FROM plexusers, plexlibraries WHERE plexusers.email = plexlibraries.email AND plexusers.serverName = plexlibraries.serverName AND plexusers.title != 'Guest';") 551 | if hide_no_lib_users_str != "1" and hide_guest_str != "1": 552 | cursor.execute("SELECT * FROM plexusers;") 553 | records = cursor.fetchall() 554 | # Add our data to the screen 555 | global count 556 | count = 0 557 | for record in records: 558 | if count % 2 == 0: 559 | my_user_tree.insert(parent='', index='end', iid=str(count), text='', 560 | values=(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8], record[9], record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18]), 561 | tags=('evenrow',)) 562 | else: 563 | my_user_tree.insert(parent='', index='end', iid=str(count), text='', 564 | values=(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8], record[9], record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18]), 565 | tags=('oddrow',)) 566 | # increment counter 567 | count += 1 568 | # Commit changes 569 | mydb.commit() 570 | # Close connexion 571 | mydb.close() 572 | 573 | # Create a Treeview Frame 574 | user_tree_frame = Frame(user_tab) 575 | user_tree_frame.configure(background="#1F1F1F") 576 | user_tree_frame.pack(fill="x", expand=1, pady=10) 577 | 578 | # Create a Treeview Scrollbar 579 | user_tree_scroll = Scrollbar(user_tree_frame) 580 | user_tree_scroll.configure(background="#1F1F1F") 581 | user_tree_scroll.pack(side=RIGHT, fill=Y) 582 | 583 | # Create The Treeview 584 | my_user_tree = ttk.Treeview(user_tree_frame, yscrollcommand=user_tree_scroll.set, selectmode="extended") 585 | my_user_tree.pack(fill="x") 586 | 587 | # Configure the Scrollbar 588 | user_tree_scroll.config(command=my_user_tree.yview) 589 | 590 | # Define Our Columns 591 | my_user_tree['columns'] = ("First Name", "Last Name", "Username", "email", "server_name", "expire_date") #, "libraries") 592 | 593 | # Format Our Columns 594 | my_user_tree.column("#0", width=0, stretch=NO) 595 | my_user_tree.column("First Name", anchor=W, width=130) 596 | my_user_tree.column("Last Name", anchor=W, width=140) 597 | my_user_tree.column("Username", anchor=CENTER, width=180) 598 | my_user_tree.column("email", anchor=CENTER, width=260) 599 | my_user_tree.column("server_name", anchor=CENTER, width=160) 600 | my_user_tree.column("expire_date", anchor=CENTER, width=120) 601 | #my_user_tree.column("libraries", anchor=CENTER, width=140) 40 602 | 603 | # Create Headings 604 | my_user_tree.heading("#0", text="", anchor=W) 605 | my_user_tree.heading("First Name", text="First Name", anchor=W) 606 | my_user_tree.heading("Last Name", text="Last Name", anchor=W) 607 | my_user_tree.heading("Username", text="Username", anchor=CENTER) 608 | my_user_tree.heading("email", text="email", anchor=CENTER) 609 | my_user_tree.heading("server_name", text="server name", anchor=CENTER) 610 | my_user_tree.heading("expire_date", text="expire date", anchor=CENTER) 611 | #my_user_tree.heading("libraries", text="libraries", anchor=CENTER) 612 | 613 | # Select record 614 | def select_user_record(e): 615 | config_path = ".config/" 616 | api_path = "api/" 617 | # DB connection 618 | """ 619 | # Read config.ini file 620 | config_object = ConfigParser() 621 | config_object.read(config_path + "pum.ini") 622 | # Get the conf info 623 | userinfo = config_object["DATABASE"] 624 | db_host = userinfo["host"] 625 | db_user = userinfo["user"] 626 | db_passwd = userinfo["passwd"] 627 | db_db = userinfo["db"] 628 | """ 629 | # connect to MySQL 630 | mydb = mysql.connector.connect( 631 | host=db_host, 632 | user=db_user, 633 | passwd=db_passwd, 634 | database=db_db, 635 | auth_plugin='mysql_native_password') 636 | # Create a cursor and initialize it 637 | cursor = mydb.cursor() 638 | # Clear entry boxes 639 | # mettre state en disabled pour avoir les case grise, mais napplique plus le entry 640 | first_name_entry.delete(0,END) 641 | last_name_entry.delete(0, END) 642 | username_entry.config(disabledbackground="#282828", 643 | disabledforeground="white", 644 | state="normal") 645 | username_entry.delete(0, END) 646 | email_entry.config(disabledbackground="#282828", 647 | disabledforeground="white", 648 | state="normal") 649 | email_entry.delete(0, END) 650 | serverName_entry.config(disabledbackground="#282828", 651 | disabledforeground="white", 652 | state="normal") 653 | serverName_entry.delete(0, END) 654 | account_expire_date_entry.delete(0, END) 655 | sections_entry.config(disabledbackground="#282828", 656 | disabledforeground="white", 657 | state="normal") 658 | sections_entry.delete(0, END) 659 | allowSync_entry.config(disabledbackground="#282828", 660 | disabledforeground="white", 661 | state="normal") 662 | allowSync_entry.delete(0, END) 663 | camera_entry.config(disabledbackground="#282828", 664 | disabledforeground="white", 665 | state="normal") 666 | camera_entry.delete(0, END) 667 | channels_entry.config(disabledbackground="#282828", 668 | disabledforeground="white", 669 | state="normal") 670 | channels_entry.delete(0, END) 671 | filterMovies_entry.config(disabledbackground="#282828", 672 | disabledforeground="white", 673 | state="normal") 674 | filterMovies_entry.delete(0, END) 675 | filterMusic_entry.config(disabledbackground="#282828", 676 | disabledforeground="white", 677 | state="normal") 678 | filterMusic_entry.delete(0, END) 679 | filterTelevision_entry.config(disabledbackground="#282828", 680 | disabledforeground="white", 681 | state="normal") 682 | filterTelevision_entry.delete(0, END) 683 | title_entry.config(disabledbackground="#282828", 684 | disabledforeground="white", 685 | state="normal") 686 | title_entry.delete(0, END) 687 | is_on_plex_entry.config(disabledbackground="#282828", 688 | disabledforeground="white", 689 | state="normal") 690 | is_on_plex_entry.delete(0, END) 691 | userID_entry.config(disabledbackground="#282828", 692 | disabledforeground="white", 693 | state="normal") 694 | userID_entry.delete(0, END) 695 | account_creation_date_entry.delete(0, END) 696 | account_renewed_date_entry.delete(0, END) 697 | #userID_entry.delete(0, END) 698 | description_entry.delete(0, END) 699 | 700 | # Grab record number 701 | selected = my_user_tree.focus() 702 | # Grab record values 703 | values = my_user_tree.item(selected, 'values') 704 | # print(values) 705 | 706 | # Output entry boxes 707 | try: 708 | first_name_entry.insert(0, values[0]) 709 | last_name_entry.insert(0, values[1]) 710 | username_entry.insert(0, values[2]) 711 | username_entry.config(state="disabled", 712 | disabledbackground="#282828") 713 | email_entry.insert(0, values[3]) 714 | email_entry.config(state="disabled", 715 | disabledbackground="#282828") 716 | serverName_entry.insert(0, values[4]) 717 | serverName_entry.config(state="disabled", 718 | disabledbackground="#282828") 719 | account_expire_date_entry.insert(0, values[5]) 720 | 721 | #print("user sections unknown: " + values[6]) 722 | # get the libraries for selected user 723 | cursor.execute("SELECT library FROM plexlibraries WHERE email = %s AND serverName = %s;", [values[3], values[4]]) 724 | user_libraries = cursor.fetchall() 725 | # display libraries in clean way 726 | for libraries in user_libraries: 727 | sections_entry.insert(0, libraries[0] + " - ") 728 | sections_entry.config(state="disabled", 729 | disabledbackground="#282828") 730 | allowSync_entry.insert(0, values[7]) 731 | allowSync_entry.config(state="disabled", 732 | disabledbackground="#282828") 733 | camera_entry.insert(0, values[8]) 734 | camera_entry.config(state="disabled", 735 | disabledbackground="#282828") 736 | channels_entry.insert(0, values[9]) 737 | channels_entry.config(state="disabled", 738 | disabledbackground="#282828") 739 | # get the filterMovies for selected user 740 | cursor.execute("SELECT filterMovies FROM plexfilterMovies WHERE email = %s AND serverName = %s;", 741 | [values[3], values[4]]) 742 | user_filterMovies = cursor.fetchall() 743 | # display filterMovies in clean way 744 | for filterMovies in user_filterMovies: 745 | filterMovies_entry.insert(0, filterMovies[0] + " - ") 746 | filterMovies_entry.config(state="disabled", 747 | disabledbackground="#282828") 748 | # get the filterMusic for selected user 749 | cursor.execute("SELECT filterMusic FROM plexfilterMusic WHERE email = %s AND serverName = %s;", 750 | [values[3], values[4]]) 751 | user_filterMusic = cursor.fetchall() 752 | # display filterMovies in clean way 753 | for filterMusic in user_filterMusic: 754 | filterMusic_entry.insert(0, filterMusic[0] + " - ") 755 | filterMusic_entry.config(state="disabled", 756 | disabledbackground="#282828") 757 | # get the filterTelevision for selected user 758 | cursor.execute("SELECT filterTelevision FROM plexfilterTelevision WHERE email = %s AND serverName = %s;", 759 | [values[3], values[4]]) 760 | user_filterTelevision = cursor.fetchall() 761 | # display filterTelevision in clean way 762 | for filterTelevision in user_filterTelevision: 763 | filterTelevision_entry.insert(0, filterTelevision[0] + " - ") 764 | filterTelevision_entry.config(state="disabled", 765 | disabledbackground="#282828") 766 | title_entry.insert(0, values[13]) 767 | title_entry.config(state="disabled", 768 | disabledbackground="#282828") 769 | is_on_plex_entry.insert(0, values[14]) 770 | is_on_plex_entry.config(state="disabled", 771 | disabledbackground="#282828") 772 | 773 | account_creation_date_entry.insert(0, values[15]) 774 | account_renewed_date_entry.insert(0, values[16]) 775 | userID_entry.insert(0, values[17]) 776 | userID_entry.config(state="disabled", 777 | disabledbackground="#282828") 778 | description_entry.insert(0, values[18]) 779 | 780 | # keep values to avoid request if identical 781 | global old_account_creation_date_entry 782 | old_account_creation_date_entry = values[15] 783 | global old_account_renewed_date_entry 784 | old_account_renewed_date_entry = values[16] 785 | global old_account_expire_date_entry 786 | old_account_expire_date_entry = values[5] 787 | # Commit changes 788 | mydb.commit() 789 | # Close connexion 790 | mydb.close() 791 | except: 792 | pass 793 | 794 | # Update Record 795 | def update_user_record(): 796 | config_path = ".config/" 797 | api_path = "api/" 798 | # DB connection 799 | """ 800 | # Read config.ini file 801 | config_object = ConfigParser() 802 | config_object.read(config_path + "pum.ini") 803 | # Get the conf info 804 | userinfo = config_object["DATABASE"] 805 | db_host = userinfo["host"] 806 | db_user = userinfo["user"] 807 | db_passwd = userinfo["passwd"] 808 | db_db = userinfo["db"] 809 | """ 810 | # connect to MySQL 811 | mydb = mysql.connector.connect( 812 | host=db_host, 813 | user=db_user, 814 | passwd=db_passwd, 815 | database=db_db, 816 | auth_plugin='mysql_native_password') 817 | # Create a cursor and initialize it 818 | cursor = mydb.cursor() 819 | # Grab the record number 820 | selected = my_user_tree.focus() 821 | # Update record 822 | my_user_tree.item(selected, text="", values=( 823 | first_name_entry.get(), last_name_entry.get(), username_entry.get(), email_entry.get(), serverName_entry.get(), 824 | account_expire_date_entry.get(), sections_entry.get(), allowSync_entry.get(), camera_entry.get(), 825 | channels_entry.get(), filterMovies_entry.get(), filterMusic_entry.get(), filterTelevision_entry.get(), 826 | title_entry.get(), is_on_plex_entry.get(), account_creation_date_entry.get(), account_renewed_date_entry.get(), 827 | userID_entry.get(), description_entry.get(),)) 828 | 829 | 830 | if account_creation_date_entry.get() != "None" and account_creation_date_entry.get() != old_account_creation_date_entry: 831 | cursor.execute("UPDATE plexusers SET account_creation_date = %s WHERE userID = %s;", 832 | [ 833 | account_creation_date_entry.get(), 834 | userID_entry.get() 835 | ]) 836 | 837 | if account_renewed_date_entry.get() != "None" and account_renewed_date_entry.get() != old_account_renewed_date_entry: 838 | cursor.execute("UPDATE plexusers SET account_renewed_date = %s WHERE userID = %s AND serverName = %s;", 839 | [ 840 | account_renewed_date_entry.get(), 841 | userID_entry.get(), 842 | serverName_entry.get() 843 | ]) 844 | 845 | if account_expire_date_entry.get() != "None" and account_expire_date_entry.get() != old_account_expire_date_entry: 846 | cursor.execute("UPDATE plexusers SET account_expire_date = %s WHERE userID = %s AND serverName = %s;", 847 | [ 848 | account_expire_date_entry.get(), 849 | userID_entry.get(), 850 | serverName_entry.get() 851 | ]) 852 | 853 | cursor.execute("UPDATE plexusers SET first_name = %s, last_name = %s, description = %s WHERE email = %s;", 854 | [ 855 | first_name_entry.get(), 856 | last_name_entry.get(), 857 | description_entry.get(), 858 | email_entry.get() 859 | ]) 860 | # Commit changes 861 | mydb.commit() 862 | # Close connexion 863 | mydb.close() 864 | 865 | # delete user 866 | def delete_user(): 867 | # DB connection 868 | """ 869 | # Read config.ini file 870 | config_object = ConfigParser() 871 | config_object.read(config_path + "pum.ini") 872 | # Get the conf info 873 | userinfo = config_object["DATABASE"] 874 | db_host = userinfo["host"] 875 | db_user = userinfo["user"] 876 | db_passwd = userinfo["passwd"] 877 | db_db = userinfo["db"] 878 | """ 879 | # connect to MySQL 880 | mydb = mysql.connector.connect( 881 | host=db_host, 882 | user=db_user, 883 | passwd=db_passwd, 884 | database=db_db, 885 | auth_plugin='mysql_native_password') 886 | # Create a cursor and initialize it 887 | cursor = mydb.cursor() 888 | # Grab the record number 889 | selected = my_user_tree.focus() 890 | if not email_entry.get() == '': 891 | # message box to be sure 892 | delete_user_confirmation_box = askyesno("delete selected user", "you are going to delete " + email_entry.get() + " are you sure?") 893 | #print(delete_user_confirmation_box) 894 | # confirmation box 895 | if delete_user_confirmation_box: 896 | current_user_serverName = serverName_entry.get() 897 | current_user_email = email_entry.get() 898 | # delete user from db 899 | cursor.execute("DELETE FROM plexusers WHERE email = %s;", [email_entry.get()]) 900 | print("user " + email_entry.get() + " deleted from DB") 901 | first_name_entry.delete(0, END) 902 | last_name_entry.delete(0, END) 903 | username_entry.delete(0, END) 904 | email_entry.config(disabledbackground="#282828", 905 | disabledforeground="white", 906 | state="normal") 907 | email_entry.delete(0, END) 908 | serverName_entry.config(disabledbackground="#282828", 909 | disabledforeground="white", 910 | state="normal") 911 | serverName_entry.delete(0, END) 912 | account_expire_date_entry.delete(0, END) 913 | sections_entry.config(disabledbackground="#282828", 914 | disabledforeground="white", 915 | state="normal") 916 | sections_entry.delete(0, END) 917 | allowSync_entry.config(disabledbackground="#282828", 918 | disabledforeground="white", 919 | state="normal") 920 | allowSync_entry.delete(0, END) 921 | camera_entry.config(disabledbackground="#282828", 922 | disabledforeground="white", 923 | state="normal") 924 | camera_entry.delete(0, END) 925 | channels_entry.config(disabledbackground="#282828", 926 | disabledforeground="white", 927 | state="normal") 928 | channels_entry.delete(0, END) 929 | filterMovies_entry.config(disabledbackground="#282828", 930 | disabledforeground="white", 931 | state="normal") 932 | filterMovies_entry.delete(0, END) 933 | filterMusic_entry.config(disabledbackground="#282828", 934 | disabledforeground="white", 935 | state="normal") 936 | filterMusic_entry.delete(0, END) 937 | filterTelevision_entry.config(disabledbackground="#282828", 938 | disabledforeground="white", 939 | state="normal") 940 | filterTelevision_entry.delete(0, END) 941 | title_entry.config(disabledbackground="#282828", 942 | disabledforeground="white", 943 | state="normal") 944 | title_entry.delete(0, END) 945 | is_on_plex_entry.config(disabledbackground="#282828", 946 | disabledforeground="white", 947 | state="normal") 948 | is_on_plex_entry.delete(0, END) 949 | userID_entry.config(disabledbackground="#282828", 950 | disabledforeground="white", 951 | state="normal") 952 | userID_entry.delete(0, END) 953 | account_creation_date_entry.delete(0, END) 954 | account_renewed_date_entry.delete(0, END) 955 | # userID_entry.delete(0, END) 956 | description_entry.delete(0, END) 957 | 958 | # delete user from plex 959 | try: 960 | cursor.execute("SELECT * FROM plexservers WHERE serverName = %s;", [current_user_serverName]) 961 | delete_user_srv_data = cursor.fetchall() 962 | #print(current_user_serverName) 963 | #print(delete_user_srv_data) 964 | #print(delete_user_srv_data[0]) 965 | for current_servers in delete_user_srv_data: 966 | from plexapi.server import PlexServer 967 | from requests import Session 968 | CURRENT_PLEX_TOKEN = current_servers[1] 969 | CURRENT_PLEX_URL = current_servers[2] 970 | #print(current_servers[1]) 971 | #print(current_servers[2]) 972 | if CURRENT_PLEX_TOKEN == '' or CURRENT_PLEX_URL == '': 973 | return 974 | SESSION = Session() 975 | # Ignore verifying the SSL certificate 976 | SESSION.verify = False # '/path/to/certfile' 977 | # If verify is set to a path to a directory, 978 | # the directory must have been processed using the c_rehash utility supplied with OpenSSL. 979 | if not SESSION.verify: 980 | # Disable the warning that the request is insecure, we know that... 981 | from urllib3 import disable_warnings 982 | from urllib3.exceptions import InsecureRequestWarning 983 | disable_warnings(InsecureRequestWarning) 984 | SERVER = PlexServer(baseurl=CURRENT_PLEX_URL, token=CURRENT_PLEX_TOKEN, session=SESSION) 985 | ACCOUNT = SERVER.myPlexAccount() 986 | #PLEX_USERS = {user.id: user.title for user in ACCOUNT.users()} 987 | #PLEX_USERS.update({int(ACCOUNT.id): ACCOUNT.title}) 988 | #print(ACCOUNT.id) 989 | #print(ACCOUNT.title) 990 | #print(ACCOUNT.users) 991 | #print(current_user_email) 992 | #print(PLEX_USERS) 993 | ACCOUNT.removeFriend(current_user_email) 994 | print("user " + current_user_email + " deleted from Plex") 995 | except: 996 | pass 997 | # Commit changes 998 | mydb.commit() 999 | # Close connexion 1000 | mydb.close() 1001 | # clear my_server_tree 1002 | my_user_tree.delete(*my_user_tree.get_children()) 1003 | # get server data back 1004 | query_user_info() 1005 | 1006 | # add user 1007 | def add_user(): 1008 | #exec(open("./modules/add_plex_user.py").read()) 1009 | add_user_window = Toplevel(root) 1010 | add_user_window.title('Plex User Manager: adding user') 1011 | add_user_window.geometry("900x700") 1012 | add_user_window.configure(bg="#282828") 1013 | # create add user frame 1014 | add_user_frame = Frame(add_user_window) 1015 | add_user_frame.pack(fill=BOTH, expand=1) 1016 | # create add user canvas 1017 | add_user_canvas = Canvas(add_user_frame) 1018 | add_user_canvas.pack(side=LEFT, fill=BOTH, expand=1) 1019 | # Create a add user Scrollbar 1020 | add_user_tree_scroll = ttk.Scrollbar(add_user_frame, orient=VERTICAL, command=add_user_canvas.yview) 1021 | #add_user_tree_scroll.configure(background="#1F1F1F") 1022 | add_user_tree_scroll.pack(side=RIGHT, fill=Y) 1023 | # configure the canvas 1024 | add_user_canvas.configure(yscrollcommand=add_user_tree_scroll.set) 1025 | add_user_canvas.bind('', lambda e: add_user_canvas.configure(scrollregion=add_user_canvas.bbox("all"))) 1026 | # second frame inside the canvas 1027 | add_user_second_frame = Frame(add_user_canvas) 1028 | add_user_second_frame.configure(bg="#282828") 1029 | #add the second frame to the windows in the canvas 1030 | add_user_canvas.create_window((0,0), window=add_user_second_frame, anchor="nw") 1031 | 1032 | 1033 | 1034 | 1035 | """ 1036 | config_path = ".config/" 1037 | # DB connection 1038 | # Read config.ini file 1039 | config_object = ConfigParser() 1040 | config_object.read(config_path + "pum.ini") 1041 | # Get the conf info 1042 | userinfo = config_object["DATABASE"] 1043 | db_host = userinfo["host"] 1044 | db_user = userinfo["user"] 1045 | db_passwd = userinfo["passwd"] 1046 | db_db = userinfo["db"] 1047 | """ 1048 | 1049 | # connect to MySQL 1050 | mydb = mysql.connector.connect( 1051 | host=db_host, 1052 | user=db_user, 1053 | passwd=db_passwd, 1054 | database=db_db, 1055 | auth_plugin='mysql_native_password') 1056 | # Create a cursor and initialize it 1057 | cursor = mydb.cursor() 1058 | 1059 | # add the user 1060 | def add_user_command(): 1061 | import re 1062 | """ 1063 | config_path = ".config/" 1064 | # DB connection 1065 | # Read config.ini file 1066 | config_object = ConfigParser() 1067 | config_object.read(config_path + "pum.ini") 1068 | # Get the conf info 1069 | userinfo = config_object["DATABASE"] 1070 | db_host = userinfo["host"] 1071 | db_user = userinfo["user"] 1072 | db_passwd = userinfo["passwd"] 1073 | db_db = userinfo["db"] 1074 | """ 1075 | 1076 | # connect to MySQL 1077 | mydb = mysql.connector.connect( 1078 | host=db_host, 1079 | user=db_user, 1080 | passwd=db_passwd, 1081 | database=db_db, 1082 | auth_plugin='mysql_native_password') 1083 | # Create a cursor and initialize it 1084 | cursor = mydb.cursor() 1085 | # global selected_server 1086 | print("email : " + email_entry.get()) 1087 | # global selected_server 1088 | print("server : " + server_clicked.get()[2:-3]) 1089 | add_user_library_selected = '' 1090 | for add_user_library_selected_item in library_listbox.curselection(): 1091 | add_user_library_selected = add_user_library_selected + str(library_listbox.get(add_user_library_selected_item)) 1092 | #print("selected libraries " + add_user_library_selected) 1093 | add_user_library_selected = re.sub(r"[\(\)]", '', add_user_library_selected) 1094 | add_user_library_selected = add_user_library_selected.replace(',', ' ') 1095 | 1096 | #get server token and url 1097 | cursor.execute("SELECT * FROM plexservers WHERE serverName = %s;", [server_clicked.get()[2:-3]]) 1098 | selected_plex_info = cursor.fetchall() 1099 | #print(type(selected_plex_info)) 1100 | new_selected_plex_info = selected_plex_info[0] 1101 | PLEX_URL = str(new_selected_plex_info[2]) 1102 | PLEX_TOKEN = str(new_selected_plex_info[1]) 1103 | # write to plex_api config.ini 1104 | plex_config_object = ConfigParser() 1105 | plex_config_object.read(plexapi.CONFIG_PATH) 1106 | plex_config_object['auth']['server_baseurl'] = PLEX_URL 1107 | plex_config_object['auth']['server_token'] = PLEX_TOKEN 1108 | with open(plexapi.CONFIG_PATH, 'w') as plex_configfile: 1109 | plex_config_object.write(plex_configfile) 1110 | # export json from plex 1111 | os.system("python3 plex_api_invite.py --user " + email_entry.get() + " --libraries " + add_user_library_selected) 1112 | print("plex_api_invite.py --user " + email_entry.get() + " --libraries " + add_user_library_selected) 1113 | #print(PLEX_TOKEN) 1114 | #print(PLEX_URL) 1115 | # Commit changes 1116 | mydb.commit() 1117 | # Close connexion 1118 | mydb.close() 1119 | add_user_window.destroy() 1120 | 1121 | 1122 | # library selection 1123 | def select_library(server_clicked): 1124 | """ 1125 | config_path = ".config/" 1126 | # DB connection 1127 | # Read config.ini file 1128 | config_object = ConfigParser() 1129 | config_object.read(config_path + "pum.ini") 1130 | # Get the conf info 1131 | userinfo = config_object["DATABASE"] 1132 | db_host = userinfo["host"] 1133 | db_user = userinfo["user"] 1134 | db_passwd = userinfo["passwd"] 1135 | db_db = userinfo["db"] 1136 | """ 1137 | 1138 | # connect to MySQL 1139 | mydb = mysql.connector.connect( 1140 | host=db_host, 1141 | user=db_user, 1142 | passwd=db_passwd, 1143 | database=db_db, 1144 | auth_plugin='mysql_native_password') 1145 | # Create a cursor and initialize it 1146 | cursor = mydb.cursor() 1147 | 1148 | # library selection 1149 | cursor.execute("SELECT DISTINCT library FROM plexlibraries WHERE serverName = %s;", [server_clicked[0]]) 1150 | library_result = cursor.fetchall() 1151 | library_listbox.delete(0, END) 1152 | library_listbox.insert(0, *library_result) 1153 | 1154 | #for lib_result in library_result: 1155 | #print(library_result) 1156 | 1157 | 1158 | # Commit changes 1159 | mydb.commit() 1160 | # Close connexion 1161 | mydb.close() 1162 | 1163 | # Grant title 1164 | grant_label = Label(add_user_second_frame, text="Grant Library Access") 1165 | grant_label.grid(row=0, column=0, padx=10, pady=10, sticky=W) 1166 | grant_label.config(background="#282828", 1167 | foreground="white", 1168 | font="Helvetica 14 bold") 1169 | # grant_text_label entry 1170 | grant_text_label = Label(add_user_second_frame, text="Enter the email of a new user and invite him to Plex. New users will be asked to accept the invitation.") 1171 | grant_text_label.grid(row=1, column=0, padx=10, pady=10, sticky=W) 1172 | grant_text_label.config(background="#282828", foreground="white") 1173 | 1174 | email_entry = Entry(add_user_second_frame, width=30) 1175 | email_entry.insert(0, "user@email") 1176 | email_entry.grid(row=2, column=0, padx=10, pady=10, sticky=W) 1177 | 1178 | # server selection 1179 | cursor.execute("SELECT serverName FROM plexservers;") 1180 | server_records = cursor.fetchall() 1181 | server_selection_label = Label(add_user_second_frame, text="select server and libraries your new user can access ") 1182 | server_selection_label.grid(row=3, column=0, padx=10, pady=10, sticky=W) 1183 | server_selection_label.config(background="#282828", 1184 | foreground="white") 1185 | server_clicked = StringVar() 1186 | server_clicked.set("select server") 1187 | server_name_drop = OptionMenu(add_user_second_frame, server_clicked, *server_records, command=select_library) #, command=select_library 1188 | server_name_drop.grid(row=4, column=0, padx=10, pady=10, sticky=W) 1189 | global selected_server 1190 | selected_server = server_clicked.get()[2:-3] 1191 | 1192 | #library_selection_label = Label(add_user_second_frame, text="select library to add to user ") 1193 | #library_selection_label.grid(row=4, column=0, padx=10, pady=10, sticky=W) 1194 | #library_selection_label.config(background="#282828", 1195 | # foreground="white") 1196 | #library_frame_for_user = Frame(add_user_window) 1197 | #library_for_user_scrollbar = Scrollbar(add_user_window, orient=VERTICAL) 1198 | library_listbox = Listbox(add_user_second_frame, selectmode=MULTIPLE) #, yscrollcommand=library_for_user_scrollbar.set) 1199 | #library_for_user_scrollbar.config(command=library_listbox.yview) 1200 | library_listbox.grid(row=5, column=0, padx=10, pady=10, sticky=W) 1201 | #library_for_user_scrollbar.grid(sticky=E) 1202 | #library_listbox.insert(0, *library_result) 1203 | 1204 | # restriction text entry 1205 | restriction_text_label = Label(add_user_second_frame, 1206 | text="You can restrict this friend's access to certain features and content. These restrictions apply to all items shared with this friend. \nYou can change them at any time.", justify="left") 1207 | restriction_text_label.grid(row=6, column=0, padx=10, pady=10, sticky=W) 1208 | restriction_text_label.config(background="#282828", foreground="white") 1209 | 1210 | # donwload checkbox 1211 | download_checkbox = tkinter.Checkbutton(add_user_second_frame, text="Allow Downloads", background="#282828", activebackground="#383838", foreground="white", activeforeground="white") 1212 | download_checkbox.grid(row=7, column=0, padx=10, pady=10, sticky=W) 1213 | download_checkbox_text_label = Label(add_user_second_frame, text="Allow this friens to download content from your server.") 1214 | download_checkbox_text_label.grid(row=8, column=0, padx=10, pady=10, sticky=W) 1215 | download_checkbox_text_label.config(background="#282828", foreground="grey") 1216 | 1217 | # movies restrictions 1218 | content_restriction_movies_text_label = Label(add_user_second_frame, text="Content Restrictions - Movies") 1219 | content_restriction_movies_text_label.grid(row=9, column=0, padx=10, pady=10, sticky=W) 1220 | content_restriction_movies_text_label.config(background="#282828", foreground="white") 1221 | 1222 | allow_only_rating_movies_text_label = Label(add_user_second_frame, text="ALLOW ONLY RATINGS") 1223 | allow_only_rating_movies_text_label.grid(row=10, column=0, padx=10, pady=10, sticky=W) 1224 | allow_only_rating_movies_text_label.config(background="#282828", foreground="grey") 1225 | allow_only_rating_movies_entry = Entry(add_user_second_frame, width=30) 1226 | allow_only_rating_movies_entry.insert(0, "Allow all ratings - NOT WORKING") 1227 | allow_only_rating_movies_entry.grid(row=11, column=0, padx=10, pady=10, sticky=W) 1228 | 1229 | exclude_rating_movies_text_label = Label(add_user_second_frame, text="EXCLUDE RATINGS") 1230 | exclude_rating_movies_text_label.grid(row=12, column=0, padx=10, pady=10, sticky=W) 1231 | exclude_rating_movies_text_label.config(background="#282828", foreground="grey") 1232 | exclude_rating_movies_entry = Entry(add_user_second_frame, width=30) 1233 | exclude_rating_movies_entry.insert(0, "Don't exclude any ratings - NOT WORKING") 1234 | exclude_rating_movies_entry.grid(row=13, column=0, padx=10, pady=10, sticky=W) 1235 | 1236 | allow_only_labels_movies_text_label = Label(add_user_second_frame, text="ALLOW ONLY LABELS") 1237 | allow_only_labels_movies_text_label.grid(row=14, column=0, padx=10, pady=10, sticky=W) 1238 | allow_only_labels_movies_text_label.config(background="#282828", foreground="grey") 1239 | allow_only_labels_movies_entry = Entry(add_user_second_frame, width=30) 1240 | allow_only_labels_movies_entry.insert(0, "Show all labels - NOT WORKING") 1241 | allow_only_labels_movies_entry.grid(row=15, column=0, padx=10, pady=10, sticky=W) 1242 | 1243 | exclude_only_labels_movies_text_label = Label(add_user_second_frame, text="EXCLUDE LABELS") 1244 | exclude_only_labels_movies_text_label.grid(row=16, column=0, padx=10, pady=10, sticky=W) 1245 | exclude_only_labels_movies_text_label.config(background="#282828", foreground="grey") 1246 | exclude_only_labels_movies_entry = Entry(add_user_second_frame, width=30) 1247 | exclude_only_labels_movies_entry.insert(0, "Don't exclude any labels - NOT WORKING") 1248 | exclude_only_labels_movies_entry.grid(row=17, column=0, padx=10, pady=10, sticky=W) 1249 | 1250 | # tvshows restrictions 1251 | content_restriction_tvshows_text_label = Label(add_user_second_frame, text="Content Restrictions - tvshows") 1252 | content_restriction_tvshows_text_label.grid(row=18, column=0, padx=10, pady=10, sticky=W) 1253 | content_restriction_tvshows_text_label.config(background="#282828", foreground="white") 1254 | 1255 | allow_only_rating_tvshows_text_label = Label(add_user_second_frame, text="ALLOW ONLY RATINGS") 1256 | allow_only_rating_tvshows_text_label.grid(row=19, column=0, padx=10, pady=10, sticky=W) 1257 | allow_only_rating_tvshows_text_label.config(background="#282828", foreground="grey") 1258 | allow_only_rating_tvshows_entry = Entry(add_user_second_frame, width=30) 1259 | allow_only_rating_tvshows_entry.insert(0, "Allow all ratings - NOT WORKING") 1260 | allow_only_rating_tvshows_entry.grid(row=20, column=0, padx=10, pady=10, sticky=W) 1261 | 1262 | exclude_rating_tvshows_text_label = Label(add_user_second_frame, text="EXCLUDE RATINGS") 1263 | exclude_rating_tvshows_text_label.grid(row=21, column=0, padx=10, pady=10, sticky=W) 1264 | exclude_rating_tvshows_text_label.config(background="#282828", foreground="grey") 1265 | exclude_rating_tvshows_entry = Entry(add_user_second_frame, width=30) 1266 | exclude_rating_tvshows_entry.insert(0, "Don't exclude any ratings - NOT WORKING") 1267 | exclude_rating_tvshows_entry.grid(row=22, column=0, padx=10, pady=10, sticky=W) 1268 | 1269 | allow_only_labels_tvshows_text_label = Label(add_user_second_frame, text="ALLOW ONLY LABELS") 1270 | allow_only_labels_tvshows_text_label.grid(row=23, column=0, padx=10, pady=10, sticky=W) 1271 | allow_only_labels_tvshows_text_label.config(background="#282828", foreground="grey") 1272 | allow_only_labels_tvshows_entry = Entry(add_user_second_frame, width=30) 1273 | allow_only_labels_tvshows_entry.insert(0, "Show all labels - NOT WORKING") 1274 | allow_only_labels_tvshows_entry.grid(row=24, column=0, padx=10, pady=10, sticky=W) 1275 | 1276 | exclude_only_labels_tvshows_text_label = Label(add_user_second_frame, text="EXCLUDE LABELS") 1277 | exclude_only_labels_tvshows_text_label.grid(row=24, column=0, padx=10, pady=10, sticky=W) 1278 | exclude_only_labels_tvshows_text_label.config(background="#282828", foreground="grey") 1279 | exclude_only_labels_tvshows_entry = Entry(add_user_second_frame, width=30) 1280 | exclude_only_labels_tvshows_entry.insert(0, "Don't exclude any labels - NOT WORKING") 1281 | exclude_only_labels_tvshows_entry.grid(row=26, column=0, padx=10, pady=10, sticky=W) 1282 | 1283 | 1284 | create_user_button = Button(add_user_second_frame, text="SEND", command=add_user_command) 1285 | create_user_button.grid(row=27, column=0, padx=10, pady=10, sticky='W') 1286 | create_user_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1287 | 1288 | # Commit changes 1289 | mydb.commit() 1290 | # Close connexion 1291 | mydb.close() 1292 | 1293 | # Create Striped Row Tags 1294 | my_user_tree.tag_configure('oddrow', background="#303030") 1295 | my_user_tree.tag_configure('evenrow', background="#2B2B2B") 1296 | 1297 | # Add Record Entry Boxes 1298 | user_data_frame = LabelFrame(user_tab, text="Selected User Information") 1299 | user_data_frame.pack(fill="x", expand=1, padx=20) 1300 | 1301 | # Configure the user_data_frame color 1302 | user_data_frame.configure(background="#282828", 1303 | foreground="white") 1304 | 1305 | first_name_label = Label(user_data_frame, text="First Name") 1306 | first_name_label.grid(row=0, column=0, padx=10, pady=10) 1307 | first_name_label.config(background="#282828", 1308 | foreground="white") 1309 | first_name_entry = Entry(user_data_frame) 1310 | first_name_entry.grid(row=0, column=1, padx=10, pady=10) 1311 | 1312 | last_name_label = Label(user_data_frame, text="Last Name") 1313 | last_name_label.grid(row=0, column=2, padx=10, pady=10) 1314 | last_name_label.config(background="#282828", 1315 | foreground="white") 1316 | last_name_entry = Entry(user_data_frame) 1317 | last_name_entry.grid(row=0, column=3, padx=10, pady=10) 1318 | 1319 | username_label = Label(user_data_frame, text="Username") 1320 | username_label.grid(row=0, column=4, padx=10, pady=10) 1321 | username_label.config(background="#282828", 1322 | foreground="white") 1323 | username_entry = Entry(user_data_frame) 1324 | username_entry.grid(row=0, column=5, padx=10, pady=10) 1325 | username_entry.config(disabledbackground="#282828", 1326 | state="disabled") 1327 | 1328 | email_label = Label(user_data_frame, text="email") 1329 | email_label.grid(row=1, column=0, padx=10, pady=10) 1330 | email_label.config(background="#282828", 1331 | foreground="white") 1332 | email_entry = Entry(user_data_frame) 1333 | email_entry.grid(row=1, column=1, padx=10, pady=10) 1334 | email_entry.config(disabledbackground="#282828", 1335 | state="disabled") 1336 | 1337 | serverName_label = Label(user_data_frame, text="serverName") 1338 | serverName_label.grid(row=1, column=2, padx=10, pady=10) 1339 | serverName_label.config(background="#282828", 1340 | foreground="white") 1341 | serverName_entry = Entry(user_data_frame) 1342 | serverName_entry.grid(row=1, column=3, padx=10, pady=10) 1343 | serverName_entry.config(disabledbackground="#282828", 1344 | state="disabled") 1345 | 1346 | account_expire_date_label = Label(user_data_frame, text="expire date") 1347 | account_expire_date_label.grid(row=2, column=4, padx=10, pady=10) 1348 | account_expire_date_label.config(background="#282828", 1349 | foreground="white") 1350 | account_expire_date_entry = Entry(user_data_frame) 1351 | account_expire_date_entry.grid(row=2, column=5, padx=10, pady=10) 1352 | 1353 | sections_label = Label(user_data_frame, text="libraries") 1354 | sections_label.grid(row=3, column=0, padx=10, pady=10) 1355 | sections_label.config(background="#282828", 1356 | foreground="white") 1357 | sections_entry = Entry(user_data_frame) 1358 | sections_entry.grid(row=3, column=1, padx=10, pady=10) 1359 | sections_entry.config(disabledbackground="#282828", 1360 | state="disabled") 1361 | 1362 | title_label = Label(user_data_frame, text="title") 1363 | title_label.grid(row=3, column=2, padx=10, pady=10) 1364 | title_label.config(background="#282828", 1365 | foreground="white") 1366 | title_entry = Entry(user_data_frame) 1367 | title_entry.grid(row=3, column=3, padx=10, pady=10) 1368 | title_entry.config(disabledbackground="#282828", 1369 | state="disabled") 1370 | 1371 | userID_label = Label(user_data_frame, text="userID") 1372 | userID_label.grid(row=3, column=4, padx=10, pady=10) 1373 | userID_label.config(background="#282828", 1374 | foreground="white") 1375 | userID_entry = Entry(user_data_frame) 1376 | userID_entry.grid(row=3, column=5, padx=10, pady=10) 1377 | userID_entry.config(disabledbackground="#282828", 1378 | state="disabled") 1379 | 1380 | account_creation_date_label = Label(user_data_frame, text="account_creation_date") 1381 | account_creation_date_label.grid(row=2, column=0, padx=10, pady=10) 1382 | account_creation_date_label.config(background="#282828", 1383 | foreground="white") 1384 | # global account_creation_date_entry 1385 | account_creation_date_entry = Entry(user_data_frame) 1386 | account_creation_date_entry.grid(row=2, column=1, padx=10, pady=10) 1387 | 1388 | account_renewed_date_label = Label(user_data_frame, text="account_renewed_date") 1389 | account_renewed_date_label.grid(row=2, column=2, padx=10, pady=10) 1390 | account_renewed_date_label.config(background="#282828", 1391 | foreground="white") 1392 | account_renewed_date_entry = Entry(user_data_frame) 1393 | account_renewed_date_entry.grid(row=2, column=3, padx=10, pady=10) 1394 | 1395 | description_label = Label(user_data_frame, text="description") 1396 | description_label.grid(row=1, column=4, padx=10, pady=10) 1397 | description_label.config(background="#282828", 1398 | foreground="white") 1399 | description_entry = Entry(user_data_frame) 1400 | description_entry.grid(row=1, column=5, padx=10, pady=10) 1401 | 1402 | allowSync_label = Label(user_data_frame, text="allowSync") 1403 | allowSync_label.grid(row=4, column=0, padx=10, pady=10) 1404 | allowSync_label.config(background="#282828", 1405 | foreground="white") 1406 | allowSync_entry = Entry(user_data_frame) 1407 | allowSync_entry.grid(row=4, column=1, padx=10, pady=10) 1408 | allowSync_entry.config(disabledbackground="#282828", 1409 | state="disabled") 1410 | 1411 | camera_label = Label(user_data_frame, text="camera") 1412 | camera_label.grid(row=4, column=2, padx=10, pady=10) 1413 | camera_label.config(background="#282828", 1414 | foreground="white") 1415 | camera_entry = Entry(user_data_frame) 1416 | camera_entry.grid(row=4, column=3, padx=10, pady=10) 1417 | camera_entry.config(disabledbackground="#282828", 1418 | state="disabled") 1419 | 1420 | channels_label = Label(user_data_frame, text="channels") 1421 | channels_label.grid(row=4, column=4, padx=10, pady=10) 1422 | channels_label.config(background="#282828", 1423 | foreground="white") 1424 | channels_entry = Entry(user_data_frame) 1425 | channels_entry.grid(row=4, column=5, padx=10, pady=10) 1426 | channels_entry.config(disabledbackground="#282828", 1427 | state="disabled") 1428 | 1429 | filterMovies_label = Label(user_data_frame, text="filterMovies") 1430 | filterMovies_label.grid(row=5, column=0, padx=10, pady=10) 1431 | filterMovies_label.config(background="#282828", 1432 | foreground="white") 1433 | filterMovies_entry = Entry(user_data_frame) 1434 | filterMovies_entry.grid(row=5, column=1, padx=10, pady=10) 1435 | filterMovies_entry.config(disabledbackground="#282828", 1436 | state="disabled") 1437 | 1438 | filterMusic_label = Label(user_data_frame, text="filterMusic") 1439 | filterMusic_label.grid(row=5, column=2, padx=10, pady=10) 1440 | filterMusic_label.config(background="#282828", 1441 | foreground="white") 1442 | filterMusic_entry = Entry(user_data_frame) 1443 | filterMusic_entry.grid(row=5, column=3, padx=10, pady=10) 1444 | filterMusic_entry.config(disabledbackground="#282828", 1445 | state="disabled") 1446 | 1447 | filterTelevision_label = Label(user_data_frame, text="filterTelevision") 1448 | filterTelevision_label.grid(row=5, column=4, padx=10, pady=10) 1449 | filterTelevision_label.config(background="#282828", 1450 | foreground="white") 1451 | filterTelevision_entry = Entry(user_data_frame) 1452 | filterTelevision_entry.grid(row=5, column=5, padx=10, pady=10) 1453 | filterTelevision_entry.config(disabledbackground="#282828", 1454 | state="disabled") 1455 | 1456 | is_on_plex_label = Label(user_data_frame, text="is on plex") 1457 | is_on_plex_label.grid(row=6, column=4, padx=10, pady=10) 1458 | is_on_plex_label.config(background="#282828", 1459 | foreground="white") 1460 | is_on_plex_entry = Entry(user_data_frame) 1461 | is_on_plex_entry.grid(row=6, column=5, padx=10, pady=10) 1462 | is_on_plex_entry.config(disabledbackground="#282828", 1463 | state="disabled") 1464 | 1465 | update_button = Button(user_data_frame, text="Update Record", command=update_user_record) 1466 | update_button.grid(row=6, column=0, padx=10, pady=10) 1467 | update_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1468 | 1469 | delete_user_button = Button(user_data_frame, text="delete selected user", command=delete_user) 1470 | delete_user_button.grid(row=6, column=1, padx=10, pady=10) 1471 | delete_user_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1472 | 1473 | # Bind the treeview 1474 | my_user_tree.bind("", select_user_record) 1475 | 1476 | # Add user frame 1477 | plex_user_option_frame = LabelFrame(user_tab, text="Plex User Options") 1478 | plex_user_option_frame.pack(fill="x", expand=1, padx=20) 1479 | # Configure the plex_add_user_frame color 1480 | plex_user_option_frame.configure(background="#282828", 1481 | foreground="white") 1482 | # add user button 1483 | add_user_button = Button(plex_user_option_frame, text="Add user", command=add_user) 1484 | add_user_button.grid(row=200, column=200, padx=10, pady=10) 1485 | add_user_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1486 | 1487 | # user count 1488 | cursor.execute("SELECT COUNT(DISTINCT userID) FROM plexusers;") 1489 | user_count_result = cursor.fetchall() 1490 | # Add plex info frame 1491 | plex_user_info_frame = LabelFrame(user_tab, text="Plex User Information") 1492 | plex_user_info_frame.pack(fill="x", expand=1, padx=20) 1493 | # Configure the plex_info_frame color 1494 | plex_user_info_frame.configure(background="#282828", 1495 | foreground="white") 1496 | user_count_label = Label(plex_user_info_frame, text="number of users : ") 1497 | user_count_label.grid(row=0, column=0, padx=10, pady=10) 1498 | user_count_label.config(background="#282828", 1499 | foreground="white") 1500 | user_count_result_label = Label(plex_user_info_frame, text=user_count_result) 1501 | user_count_result_label.grid(row=0, column=1) 1502 | user_count_result_label.config(background="#282828", 1503 | foreground="white") 1504 | 1505 | 1506 | 1507 | # ********************************************************************************************************************** 1508 | # ********************************************* server panel *********************************************************** 1509 | # ********************************************************************************************************************** 1510 | 1511 | def query_server_info(): 1512 | mydb = mysql.connector.connect( 1513 | host=db_host, 1514 | user=db_user, 1515 | passwd=db_passwd, 1516 | database=db_db, 1517 | auth_plugin='mysql_native_password') 1518 | # Create a cursor and initialize it 1519 | cursor = mydb.cursor() 1520 | # Select info from db 1521 | cursor.execute("SELECT * FROM plexservers;") 1522 | records = cursor.fetchall() 1523 | # Add our data to the screen 1524 | global count 1525 | count = 0 1526 | for record in records: 1527 | if record[3] == 0: 1528 | server_status = "Online" 1529 | # status_color = "red" 1530 | else: 1531 | server_status = "Offline" 1532 | # status_color = "white" 1533 | if count % 2 == 0: 1534 | my_server_tree.insert(parent='', index='end', iid=str(count), text='', 1535 | values=(record[0], record[1], record[2], server_status), 1536 | tags=('evenrow',)) 1537 | else: 1538 | my_server_tree.insert(parent='', index='end', iid=str(count), text='', 1539 | values=(record[0], record[1], record[2], server_status), 1540 | tags=('oddrow',)) 1541 | # increment counter 1542 | count += 1 1543 | 1544 | # Select server record 1545 | def select_server_record(e): 1546 | # Clear entry boxes 1547 | 1548 | server_serverName_entry.config(disabledbackground="#282828", 1549 | disabledforeground="white", 1550 | state="normal") 1551 | server_serverName_entry.delete(0, END) 1552 | token_entry.config(disabledbackground="#282828", 1553 | disabledforeground="white", 1554 | state="normal") 1555 | token_entry.delete(0, END) 1556 | url_entry.config(disabledbackground="#282828", 1557 | disabledforeground="white", 1558 | state="normal") 1559 | url_entry.delete(0, END) 1560 | server_offline_entry.config(disabledbackground="#282828", 1561 | disabledforeground="white", 1562 | state="normal") 1563 | server_offline_entry.delete(0, END) 1564 | 1565 | # Grab record number 1566 | server_selected = my_server_tree.focus() 1567 | # Grab record values 1568 | server_values = my_server_tree.item(server_selected, 'values') 1569 | # Output entry boxes 1570 | server_serverName_entry.insert(0, server_values[0]) 1571 | server_serverName_entry.config(disabledbackground="#282828", state="disabled") 1572 | token_entry.insert(0, server_values[1]) 1573 | token_entry.config(disabledbackground="#282828", state="disabled") 1574 | url_entry.insert(0, server_values[2]) 1575 | url_entry.config(disabledbackground="#282828", state="disabled") 1576 | server_offline_entry.insert(0, server_values[3]) 1577 | server_offline_entry.config(disabledbackground="#282828", state="disabled") 1578 | 1579 | 1580 | # Update server Record 1581 | def delete_server_record(serverName_entry, url_entry): 1582 | """ 1583 | # Read config.ini file 1584 | config_object = ConfigParser() 1585 | config_object.read(config_path + "pum.ini") 1586 | # Get the conf info 1587 | userinfo = config_object["DATABASE"] 1588 | db_host = userinfo["host"] 1589 | db_user = userinfo["user"] 1590 | db_passwd = userinfo["passwd"] 1591 | db_db = userinfo["db"] 1592 | """ 1593 | # connect to MySQL 1594 | mydb = mysql.connector.connect( 1595 | host=db_host, 1596 | user=db_user, 1597 | passwd=db_passwd, 1598 | database=db_db, 1599 | auth_plugin='mysql_native_password') 1600 | # Create a cursor and initialize it 1601 | print(serverName_entry) #with python and tkinter, how to pass an entry.get() in a def? 1602 | #print(serverNameDisplay) 1603 | cursor = mydb.cursor() 1604 | cursor.execute("DELETE FROM plexservers WHERE ServerName = %s AND url = %s;", [serverName_entry, url_entry]) #[serverName_entry.get(), url_entry.get()]) 1605 | # Commit changes 1606 | mydb.commit() 1607 | # Close connexion 1608 | mydb.close() 1609 | # clear my_server_tree 1610 | my_server_tree.delete(*my_server_tree.get_children()) 1611 | # get server data back 1612 | query_server_info() 1613 | #server_serverName_entry.delete(0, END) 1614 | #url_entry.delete(0, END) 1615 | #token_entry.delete(0, END) 1616 | #server_offline_entry.delete(0, END) 1617 | 1618 | 1619 | 1620 | # Add server record 1621 | def add_server_record(): 1622 | NEW_PLEX_TOKEN = new_token_entry.get() 1623 | NEW_PLEX_URL = new_url_entry.get() 1624 | if NEW_PLEX_TOKEN == '' or NEW_PLEX_URL == '': 1625 | return 1626 | # Read config.ini file 1627 | """ 1628 | config_object = ConfigParser() 1629 | config_object.read(config_path + "pum.ini") 1630 | # Get the conf info 1631 | userinfo = config_object["DATABASE"] 1632 | db_host = userinfo["host"] 1633 | db_user = userinfo["user"] 1634 | db_passwd = userinfo["passwd"] 1635 | db_db = userinfo["db"] 1636 | """ 1637 | # connect to MySQL 1638 | mydb = mysql.connector.connect( 1639 | host=db_host, 1640 | user=db_user, 1641 | passwd=db_passwd, 1642 | database=db_db, 1643 | auth_plugin='mysql_native_password') 1644 | # Create a cursor and initialize it 1645 | cursor = mydb.cursor() 1646 | sess = requests.Session() 1647 | # Ignore verifying the SSL certificate 1648 | sess.verify = False # '/path/to/certfile' 1649 | # If verify is set to a path to a directory, 1650 | # the directory must have been processed using the c_rehash utility supplied 1651 | # with OpenSSL. 1652 | if sess.verify is False: 1653 | # Disable the warning that the request is insecure, we know that... 1654 | import urllib3 1655 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 1656 | # test if server online 1657 | try: 1658 | plex = PlexServer(NEW_PLEX_URL, NEW_PLEX_TOKEN, session=sess) 1659 | except: 1660 | print("error connecting to server, check your inpout") 1661 | tkinter.messagebox.showinfo(title="Connection error", message="input error or server Offline") 1662 | #cursor.execute("INSERT IGNORE INTO plexservers SET server_offline = 0, token = %s, url = %s;", [NEW_PLEX_TOKEN, NEW_PLEX_URL]) 1663 | else: 1664 | cursor.execute( 1665 | "INSERT IGNORE INTO plexservers SET server_offline = 0, serverName = %s, token = %s, url = %s;", 1666 | [plex.friendlyName, NEW_PLEX_TOKEN, NEW_PLEX_URL]) 1667 | 1668 | # Commit changes 1669 | mydb.commit() 1670 | # Close connexion 1671 | mydb.close() 1672 | # import data from new server 1673 | global NEW_PLEX_SERVER 1674 | NEW_PLEX_SERVER = "plex.friendlyName" 1675 | multithreading_import_data() 1676 | # clear my_server_tree 1677 | my_server_tree.delete(*my_server_tree.get_children()) 1678 | # get server data back 1679 | query_server_info() 1680 | 1681 | 1682 | # Create a Treeview Frame 1683 | server_tree_frame = Frame(server_tab) 1684 | #server_tree_frame.config(background="red") 1685 | #server_tree_frame.configure(background="#1F1F1F") 1686 | server_tree_frame.pack(fill="x", expand=1, pady=10) 1687 | 1688 | # Create a Treeview Scrollbar 1689 | server_tree_scroll = Scrollbar(server_tree_frame) 1690 | #server_tree_scroll.configure(background="#1F1F1F") 1691 | server_tree_scroll.pack(side=RIGHT, fill=Y) 1692 | 1693 | # Create The Treeview 1694 | my_server_tree = ttk.Treeview(server_tree_frame, yscrollcommand=server_tree_scroll.set, selectmode="extended") 1695 | my_server_tree.pack(fill="x") # need to check the fill="x" 1696 | 1697 | # Configure the Scrollbar 1698 | server_tree_scroll.config(command=my_server_tree.yview, background="#1F1F1F") 1699 | 1700 | # Define Our Columns 1701 | my_server_tree['columns'] = ("Server Name", "Token", "URL", "State") 1702 | 1703 | # Format Our Columns 1704 | my_server_tree.column("#0", width=0, stretch=NO) 1705 | my_server_tree.column("Server Name", anchor=W, width=150) 1706 | my_server_tree.column("Token", anchor=W, width=200) 1707 | my_server_tree.column("URL", anchor=CENTER, width=250) 1708 | my_server_tree.column("State", anchor=CENTER, width=70) 1709 | 1710 | # Create Headings 1711 | my_server_tree.heading("#0", text="", anchor=W) 1712 | my_server_tree.heading("Server Name", text="Server Name", anchor=W) 1713 | my_server_tree.heading("Token", text="Token", anchor=W) 1714 | my_server_tree.heading("URL", text="URL", anchor=CENTER) 1715 | my_server_tree.heading("State", text="State", anchor=CENTER) 1716 | 1717 | # Create Striped Row Tags 1718 | my_server_tree.tag_configure('oddrow', background="#303030") 1719 | my_server_tree.tag_configure('evenrow', background="#2B2B2B") 1720 | 1721 | # delete entry zone 1722 | # Add Record Entry Boxes 1723 | server_data_frame = LabelFrame(server_tab, text="Delete Server options") 1724 | server_data_frame.pack(fill="x", expand=1, padx=20) 1725 | 1726 | # Configure the server_data_frame color 1727 | server_data_frame.configure(background="#282828", 1728 | foreground="white") 1729 | 1730 | server_serverName_label = Label(server_data_frame, text="server name") 1731 | server_serverName_label.grid(row=0, column=0, padx=10, pady=10) 1732 | server_serverName_label.config(background="#282828", 1733 | foreground="white") 1734 | server_serverName_entry = Entry(server_data_frame, width=30) 1735 | server_serverName_entry.grid(row=0, column=1, padx=10, pady=10) 1736 | server_serverName_entry.config(state="disabled", 1737 | disabledbackground="#282828") 1738 | 1739 | token_label = Label(server_data_frame, text="token") 1740 | token_label.grid(row=1, column=2, padx=10, pady=10) 1741 | token_label.config(background="#282828", 1742 | foreground="white") 1743 | token_entry = Entry(server_data_frame, width=30) 1744 | token_entry.grid(row=1, column=3, padx=10, pady=10) 1745 | token_entry.config(state="disabled", 1746 | disabledbackground="#282828") 1747 | 1748 | url_label = Label(server_data_frame, text="URL") 1749 | url_label.grid(row=1, column=0, padx=10, pady=10) 1750 | url_label.config(background="#282828", 1751 | foreground="white") 1752 | url_entry = Entry(server_data_frame, width=30) 1753 | url_entry.grid(row=1, column=1, padx=10, pady=10) 1754 | url_entry.config(state="disabled", 1755 | disabledbackground="#282828") 1756 | 1757 | server_offline_label = Label(server_data_frame, text="state") 1758 | server_offline_label.grid(row=0, column=2, padx=10, pady=10) 1759 | server_offline_label.config(background="#282828", 1760 | foreground="white") 1761 | server_offline_entry = Entry(server_data_frame, width=30) 1762 | server_offline_entry.grid(row=0, column=3, padx=10, pady=10) 1763 | server_offline_entry.config(state="disabled", 1764 | disabledbackground="#282828") 1765 | 1766 | # delete server button 1767 | delete_server_button = Button(server_data_frame, text="Delete server", command=lambda:delete_server_record(server_serverName_entry.get(), url_entry.get())) 1768 | delete_server_button.grid(row=1, column=4, padx=10, pady=10) 1769 | delete_server_button.config(background="#e5a00d", activebackground="#383838", foreground="white", 1770 | activeforeground="white", border="0", font='Helvetica 10 bold') 1771 | 1772 | # Bind the treeview 1773 | my_server_tree.bind("", select_server_record) 1774 | 1775 | # add server zone 1776 | # Add Record Entry Boxes 1777 | server_add_data_frame = LabelFrame(server_tab, text="Add Server") 1778 | server_add_data_frame.pack(fill="x", expand=1, padx=20) 1779 | # Configure the server_data_frame color 1780 | server_add_data_frame.configure(background="#282828", 1781 | foreground="white") 1782 | # URL label 1783 | new_url_label = Label(server_add_data_frame, text="URL") 1784 | new_url_label.grid(row=0, column=0, padx=10, pady=10) 1785 | new_url_label.config(background="#282828", 1786 | foreground="white") 1787 | # URL entry box 1788 | new_url_entry = Entry(server_add_data_frame, width=30) 1789 | new_url_entry.insert(0, "http://192.168.1.200:32400") 1790 | new_url_entry.grid(row=0, column=1, padx=10, pady=10) 1791 | # Token label 1792 | new_token_label = Label(server_add_data_frame, text="token") 1793 | new_token_label.grid(row=0, column=2, padx=10, pady=10) 1794 | new_token_label.config(background="#282828", 1795 | foreground="white") 1796 | # Token entry box 1797 | new_token_entry = Entry(server_add_data_frame, width=30) 1798 | new_token_entry.insert(0, "myplextoken") 1799 | new_token_entry.grid(row=0, column=3, padx=10, pady=10) 1800 | # add server button 1801 | add_server_button = Button(server_add_data_frame, text="add server", command=add_server_record) 1802 | add_server_button.grid(row=0, column=4, padx=10, pady=10) 1803 | add_server_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", 1804 | border="0", font='Helvetica 10 bold') 1805 | 1806 | 1807 | 1808 | 1809 | # server count 1810 | cursor.execute("SELECT COUNT(DISTINCT serverName) FROM plexservers;") 1811 | server_count_result = cursor.fetchall() 1812 | # library count 1813 | cursor.execute("SELECT COUNT(DISTINCT library) FROM plexlibraries;") 1814 | library_count_result = cursor.fetchall() 1815 | # Add plex server info frame 1816 | plex_server_info_frame = LabelFrame(server_tab, text="Plex Server Information") 1817 | plex_server_info_frame.pack(fill="x", expand=1, padx=20) 1818 | # Configure the plex_server_info_frame color 1819 | plex_server_info_frame.configure(background="#282828", 1820 | foreground="white") 1821 | # display server count 1822 | server_count_label = Label(plex_server_info_frame, text="number of servers : ") 1823 | server_count_label.grid(row=0, column=0, padx=10, pady=10) 1824 | server_count_label.config(background="#282828", 1825 | foreground="white") 1826 | server_count_result_label = Label(plex_server_info_frame, text=server_count_result) 1827 | server_count_result_label.grid(row=0, column=1) 1828 | server_count_result_label.config(background="#282828", 1829 | foreground="white") 1830 | # display separation label 1831 | server_separation_label = Label(plex_server_info_frame, text=" / ") 1832 | server_separation_label.grid(row=0, column=2, padx=10, pady=10) 1833 | server_separation_label.config(background="#282828", 1834 | foreground="white") 1835 | # display library count 1836 | library_count_label = Label(plex_server_info_frame, text="number of libraries : ") 1837 | library_count_label.grid(row=0, column=3, padx=10, pady=10) 1838 | library_count_label.config(background="#282828", 1839 | foreground="white") 1840 | library_count_result_label = Label(plex_server_info_frame, text=library_count_result) 1841 | library_count_result_label.grid(row=0, column=4) 1842 | library_count_result_label.config(background="#282828", 1843 | foreground="white") 1844 | 1845 | # ********************************************************************************************************************** 1846 | # ********************************************* setting panel ********************************************************** 1847 | # ********************************************************************************************************************** 1848 | # setting gui 1849 | # conf frame 1850 | conf_frame = LabelFrame(conf_tab) 1851 | conf_frame.configure(background="#1F1F1F", border="0") # color code 1F1F1F 1852 | conf_frame.pack(padx=20, anchor="w", fill="x") 1853 | # set tabs 1854 | notebook2 = ttk.Notebook(conf_frame, style='TNotebook') 1855 | 1856 | general_tab: Frame = Frame(notebook2) # frame for general page 1857 | communication_tab: Frame = Frame(notebook2) # frame for communication page 1858 | #user_option_tab: Frame = Frame(notebook2) # frame for user_option page 1859 | #gui_option_tab: Frame = Frame(notebook2) # frame for gui_option page 1860 | backup_restore_tab: Frame = Frame(notebook2) # frame for backup_restore page 1861 | 1862 | notebook2.add(general_tab, text="General") 1863 | notebook2.add(communication_tab, text="Communication") 1864 | #notebook2.add(user_option_tab, text="User options") 1865 | #notebook2.add(gui_option_tab, text="GUI options") 1866 | notebook2.add(backup_restore_tab, text="Backup & Restore") 1867 | 1868 | general_tab.configure(background="#1F1F1F") 1869 | #general_tab.pack(fill='both') 1870 | communication_tab.configure(background="#1F1F1F") 1871 | #user_option_tab.configure(background="#1F1F1F") 1872 | #gui_option_tab.configure(background="#1F1F1F") 1873 | backup_restore_tab.configure(background="#1F1F1F") 1874 | # update_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1875 | notebook2.pack(expand=True, fill="both") # expand to space not used 1876 | 1877 | # GENERAL TAB ********************************************************************************************************** 1878 | # Hide Guest 1879 | # hide guest button 1880 | def hide_guest_command(): 1881 | config_object = ConfigParser() 1882 | config_object.read(".config/pum.ini") 1883 | config_object['CONF']['hide_guest'] = str(hide_guest_but.get()) 1884 | with open(".config/pum.ini", 'w') as configfile: 1885 | config_object.write(configfile) 1886 | global hide_guest_str 1887 | hide_guest_str = str(hide_guest_but.get()) 1888 | # clear my_user_tree 1889 | my_user_tree.delete(*my_user_tree.get_children()) 1890 | # get user data back 1891 | query_user_info() 1892 | 1893 | 1894 | # hide users with no lib button 1895 | def hide_no_lib_users_command(): 1896 | config_object = ConfigParser() 1897 | config_object.read(".config/pum.ini") 1898 | config_object['CONF']['hide_no_lib_users'] = str(hide_no_lib_users_but.get()) 1899 | with open(".config/pum.ini", 'w') as configfile: 1900 | config_object.write(configfile) 1901 | global hide_no_lib_users_str 1902 | hide_no_lib_users_str = str(hide_no_lib_users_but.get()) 1903 | # clear my_user_tree 1904 | my_user_tree.delete(*my_user_tree.get_children()) 1905 | # get user data back 1906 | query_user_info() 1907 | 1908 | 1909 | 1910 | def save_user_options_conf_command(): 1911 | config_object = ConfigParser() 1912 | config_object.read(".config/pum.ini") 1913 | config_object['CONF']['hide_guest'] = str(hide_guest_but.get()) 1914 | config_object['CONF']['hide_no_lib_users'] = str(hide_no_lib_users_but.get()) 1915 | config_object['CONF']['remove_user_access'] = str(remove_user_access_but.get()) 1916 | config_object['CONF']['sync_plex_delai'] = str(sync_plex_delai_entry.get()) 1917 | 1918 | #config_object['CONF']['delete_user'] = str(delete_user_but.get()) 1919 | #config_object['CONF']['delete_user_delay'] = str(delete_user_conf.get()) 1920 | with open(".config/pum.ini", 'w') as configfile: 1921 | config_object.write(configfile) 1922 | global hide_guest_str 1923 | hide_guest_str = str(hide_guest_but.get()) 1924 | global hide_no_lib_users_str 1925 | hide_no_lib_users_str = str(hide_no_lib_users_but.get()) 1926 | global sync_plex_delai_str 1927 | sync_plex_delai_str = str(sync_plex_delai_entry.get()) 1928 | # clear my_user_tree 1929 | my_user_tree.delete(*my_user_tree.get_children()) 1930 | # get user data back 1931 | query_user_info() 1932 | 1933 | # hide guest button 1934 | hide_guest_but = IntVar(value=hide_guest_str) 1935 | Checkbutton(general_tab, text="Hide Guest user", variable=hide_guest_but, onvalue=1, offvalue=0, 1936 | activeforeground="#e5a00d", activebackground="#1F1F1F", background="#1F1F1F", 1937 | foreground="white", selectcolor="grey", takefocus="0", bd="0").grid(row=0, column=0, padx=10, pady=10, sticky='w') 1938 | 1939 | # hide users with no lib button 1940 | hide_no_lib_users_but = IntVar(value=hide_no_lib_users_str) 1941 | Checkbutton(general_tab, text="Hide user without libraries", variable=hide_no_lib_users_but, onvalue=1, offvalue=0, 1942 | activeforeground="#e5a00d", activebackground="#1F1F1F", background="#1F1F1F", 1943 | foreground="white", selectcolor="grey", takefocus="0", bd="0").grid(row=1, column=0, padx=10, pady=10, sticky='w') 1944 | 1945 | # remove access when account has expired 1946 | remove_user_access_but = IntVar(value=remove_user_access) 1947 | Checkbutton(general_tab, text="remove access when account has expired", variable=remove_user_access_but, onvalue=1, 1948 | offvalue=0, activeforeground="#e5a00d", activebackground="#1F1F1F", background="#1F1F1F", 1949 | foreground="white", selectcolor="grey", takefocus="0", bd="0").grid(row=2, column=0, padx=10, pady=10, sticky='w') 1950 | remove_user_access_but_description_label = Label(general_tab, text="Librairies access will be removed from user") 1951 | remove_user_access_but_description_label.grid(row=3, column=0, padx=20, sticky='w') 1952 | remove_user_access_but_description_label.config(background="#1F1F1F", 1953 | foreground="grey") 1954 | 1955 | # sync app with plex entry 1956 | sync_plex_delai_label = Label(general_tab, text="delay in hours, between every plex sync (default is 24 Hours)") 1957 | sync_plex_delai_label.grid(row=4, column=0, padx=10, pady=10, sticky='w') 1958 | sync_plex_delai_label.config(background="#1F1F1F", 1959 | foreground="white") 1960 | sync_plex_delai_entry = Entry(general_tab) 1961 | sync_plex_delai_entry.insert(0, sync_plex_delai) 1962 | sync_plex_delai_entry.grid(row=4, column=1, padx=10, pady=10, sticky='w') 1963 | '''# delete user when expired 1964 | delete_user_but = IntVar(value=delete_user) 1965 | Checkbutton(general_tab, text="delete expired users", variable=delete_user_but, onvalue=1, offvalue=0, 1966 | activeforeground="#e5a00d", activebackground="#1F1F1F", background="#1F1F1F", foreground="white", 1967 | selectcolor="grey", takefocus="0", bd="0").grid(row=4, column=0, padx=10, pady=10, sticky='w') 1968 | delay_after_warning = Label(general_tab, text="Delay after warning", font=("helvetica, 10")) 1969 | delay_after_warning.grid(row=5, column=0, padx=10, pady=10, sticky='w') 1970 | delay_after_warning.config(background="#1F1F1F", 1971 | foreground="white")''' 1972 | 1973 | '''delete_user_conf = Entry(general_tab, background="#555555", foreground="white", relief=FLAT) 1974 | delete_user_conf.delete(0, END) 1975 | delete_user_conf.insert(0, delete_user_delay_str) 1976 | delete_user_conf.grid(row=4, column=0, padx=3, sticky='w') 1977 | delete_user_conf_label = Label(general_tab, text="Delay in days after expiration to delete user") 1978 | delete_user_conf_label.grid(row=5, column=0, sticky='w') 1979 | delete_user_conf_label.config(background="#1F1F1F", 1980 | foreground="grey") 1981 | ''' 1982 | save_user_options_settings_button = Button(general_tab, text="Save", command=save_user_options_conf_command) 1983 | save_user_options_settings_button.grid(row=20, column=0, padx=300, pady=10, sticky='W') 1984 | save_user_options_settings_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1985 | 1986 | # COMMUNICATION TAB **************************************************************************************************** 1987 | 1988 | 1989 | # Backup & Restore TAB ************************************************************************************************* 1990 | def backup_command(): 1991 | os.system("python3 api/backup.py") 1992 | 1993 | backup_button = Button(backup_restore_tab, text="Backup DB", command=backup_command) 1994 | backup_button.grid(row=0, column=0, padx=300, pady=10, sticky='W') 1995 | backup_button.config(background="#e5a00d", activebackground="#383838", foreground="white", activeforeground="white", border="0", font='Helvetica 10 bold') 1996 | 1997 | 1998 | # ********************************************************************************************************************** 1999 | # ********************************************* help & info panel ****************************************************** 2000 | # ********************************************************************************************************************** 2001 | # help & info frame 2002 | help_and_info_tab_frame = LabelFrame(help_and_info_tab) 2003 | help_and_info_tab_frame.configure(background="#1F1F1F", border="0") 2004 | help_and_info_tab_frame.pack(padx=20, anchor="w") 2005 | # help & info page 2006 | # get version number in VERSION file 2007 | with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "VERSION")) as handle: 2008 | for version in handle.readlines(): 2009 | version = version.strip() 2010 | # display version number 2011 | version_label = Label(help_and_info_tab_frame, text="Version: " + str(version)) 2012 | version_label.configure(background="#1F1F1F", border="0", foreground="white") 2013 | version_label.grid(row=0, column=0, padx=10, pady=10) 2014 | 2015 | 2016 | # Run to pull data from db on start 2017 | query_user_info() 2018 | query_server_info() 2019 | 2020 | # Commit changes 2021 | mydb.commit() 2022 | # Close connexion 2023 | mydb.close() 2024 | 2025 | root.mainloop() 2026 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------- 2 | # Potential requirements. 3 | # pip install -r requirements.txt 4 | #--------------------------------------------------------- 5 | requests==2.28.1 6 | PlexAPI==4.13 7 | urllib3==1.26.12 8 | mysql-connector-python==8.0.30 9 | --------------------------------------------------------------------------------