diff --git a/README.md b/README.md
index e025fd5..8cf6a1d 100644
--- a/README.md
+++ b/README.md
@@ -7,12 +7,16 @@ This extension requires Python3.8 and above to be installed on your system.
## AccessOwnership
This extension modifies access permissions & ownership on downloaded files.
Supports multiple categories and to configure access permissions & ownership separately for each category.
-Built in testfunction to test entered values.
+Built in function to test entered values.
+Built in function to get UID and GID for NZBGet instance.
+
## OS Support (so far)
-Linux / Debian12
+Linux / Debian13
+MacOS / 14
+Docker
## Installation
-For manual installation, unpack 'Extension-AccessOwnership-1.4.zip' into NZBget's scripts folder.
+For manual installation, unpack 'Extension-AccessOwnership-1.6.zip' into NZBget's scripts folder.
Follow instructions here:
https://nzbget.com/documentation/extension-scripts
diff --git a/main.py b/main.py
index cc18d2c..68ec7b9 100644
--- a/main.py
+++ b/main.py
@@ -9,112 +9,155 @@
import grp
# Exit codes used by NZBGet
-POSTPROCESS_SUCCESS=93
-POSTPROCESS_ERROR=94
+SCRIPT_SUCCESS=93
+SCRIPT_ERROR=94
# Debug messages from Extension
-#check_OS=
-check_SETTINGS="Check instructions in the extension settings"
+CHECK_SETTINGS="Check instructions in the extension settings"
# Check if all required script config options are present in config file
-required_options = ('NZBPO_DESTDIR', 'NZBPO_ACCESS', 'NZBPO_OWNER', 'NZBPO_GROUP')
+required_options = ('NZBPO_DESTDIR', 'NZBPO_ACCESS', 'NZBPO_OWNER', 'NZBPO_GROUP', 'NZBPO_OVERRIDEID')
for optname in required_options:
- if (not optname in os.environ):
- print('[ERROR] Option %s is missing in configuration file. Please check script settings' % optname[6:])
- sys.exit(POSTPROCESS_ERROR)
+ if (optname not in os.environ):
+ print(f"[ERROR] Option {optname[6:]} is missing in configuration file. Please check script settings")
+ sys.exit(SCRIPT_ERROR)
# Check if the script is executed from settings page with a custom command
command = os.environ.get("NZBCP_COMMAND")
+detect_mode = command == "Detect"
test_mode = command == "Test"
-if command != None and not test_mode:
+if command is not None and not any((detect_mode,test_mode)):
print('[ERROR] Invalid command ' + command)
- sys.exit(COMMAND_ERROR)
+ sys.exit(SCRIPT_ERROR)
+
+# Override and debugging
+overrideid = os.environ['NZBPO_OVERRIDEID'];
+
+# Detecting users Owner[UID] and Group[GID], executed from settings page
+if detect_mode:
+ try:
+ if pwd.getpwuid(os.getuid())[2] is not None:
+ print("User UID is detected:",[pwd.getpwuid(os.getuid())[2]],)
+ else:
+ print("Cannot detect logged in users UID")
+ check = SCRIPT_ERROR
+
+ if pwd.getpwuid(os.getuid())[3] is not None:
+ print("Group GID is detected:",[pwd.getpwuid(os.getuid())[3]],)
+ else:
+ print("Cannot detect logged in users GID")
+ check = SCRIPT_ERROR
+
+ try:
+ check
+ except NameError:
+ sys.exit(SCRIPT_SUCCESS)
+ else:
+ sys.exit(check)
+ except KeyError:
+ print("The requested key was not found.")
# Counters for added Categories
-countCategory = 1
+countcategory = 1
for i in range(1, 100):
if os.environ.get("NZBOP_Category" + str(i) + ".Name") is not None:
- countCategory +=1
-countCategoryExt = 1
+ countcategory +=1
+countcategoryext = 1
for i in range(1, 100):
if os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name") is not None:
- countCategoryExt +=1
-
+ countcategoryext +=1
+# Testing the validity of settings, executed from settings page
if test_mode:
if not os.environ.get("NZBPO_DestDir") == os.environ.get("NZBOP_DestDir"):
- print("Default Category: Invalid Path:",[os.environ.get("NZBPO_DestDir")],check_SETTINGS)
- check = POSTPROCESS_ERROR
- if not re.match('^[0-7]{3}+$', os.environ.get("NZBPO_Access")):
- print("Default Category: Invalid Access[mask]:",[os.environ.get("NZBPO_Access")],check_SETTINGS)
- check = POSTPROCESS_ERROR
-
- if not re.match('^[0-9]*$', os.environ.get("NZBPO_Owner")):
- print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],check_SETTINGS)
- check = POSTPROCESS_ERROR
+ print("Default Category: Invalid Path:",[os.environ.get("NZBPO_DestDir")],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+ if not re.match('^[0-7]{3}$', os.environ.get("NZBPO_Access")):
+ print("Default Category: Invalid Access[mask]:",[os.environ.get("NZBPO_Access")],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+
+ if not re.match('^[0-9]{1,}$', os.environ.get("NZBPO_Owner")):
+ print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
else:
try:
pwd.getpwuid(int(os.environ.get("NZBPO_Owner")))
except KeyError:
- print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"User not in system.",check_SETTINGS)
- check = POSTPROCESS_ERROR
+ if overrideid == "yes":
+ print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"<- ENABLED!!")
+ else:
+ print("Default Category: Invalid Owner[UID]:",[os.environ.get("NZBPO_Owner")],"User not in system.",CHECK_SETTINGS)
+ check = SCRIPT_ERROR
- if not re.match('^[0-9]*$', os.environ.get("NZBPO_Group")):
- print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],check_SETTINGS)
- check = POSTPROCESS_ERROR
+ if not re.match('^[0-9]{1,}$', os.environ.get("NZBPO_Group")):
+ print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
else:
try:
grp.getgrgid(int(os.environ.get("NZBPO_Group")))
except KeyError:
- print("Default Category: Invalid Group[UID]: Group[UID]",[os.environ.get("NZBPO_Group")],"Group not in system.",check_SETTINGS)
- check = POSTPROCESS_ERROR
+ if overrideid == "yes":
+ print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],"<- ENABLED!!")
+ else:
+ print("Default Category: Invalid Group[GID]:",[os.environ.get("NZBPO_Group")],"Group not in system.",CHECK_SETTINGS)
+ check = SCRIPT_ERROR
- for i in range(1, countCategoryExt):
+ for i in range(1, countcategoryext):
+ if os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name") is not None:
catextname = os.environ["NZBPO_CategoryExt" + str(i) + ".Name"];
catextdestdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
catextaccess = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
catextowner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
catextgroup = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];
- if not re.match('^[0-7]{3}+$', catextaccess):
- print(catextname,"Category: Invalid Access[mask]:",[catextaccess],check_SETTINGS)
- check = POSTPROCESS_ERROR
- if not re.match('^[0-9]*$', catextowner):
- print(catextname,"Category: Invalid Owner[UID]:",[catextowner],check_SETTINGS)
- check = POSTPROCESS_ERROR
+ if not re.match('^[0-7]{3}$', catextaccess):
+ print(catextname,"Category: Invalid Access[mask]:",[catextaccess],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+
+ if not re.match('^[0-9]{1,}$', catextowner):
+ print(catextname,"Category: Invalid Owner[UID]:",[catextowner],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
else:
try:
pwd.getpwuid(int(catextowner))
except KeyError:
- print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.",check_SETTINGS)
- check = POSTPROCESS_ERROR
-
- if not re.match('^[0-9]*$', catextgroup):
- print(catextname,"Category: Invalid Group[UID]:",[catextgroup],check_SETTINGS)
- check = POSTPROCESS_ERROR
+ if overrideid == "yes":
+ print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.","<- ENABLED!!")
+ else:
+ print(catextname,"Category: Invalid Owner[UID]:",[catextowner],"User not in system.",CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+
+ if not re.match('^[0-9]{1,}$', catextgroup):
+ print(catextname,"Category: Invalid Group[UID]:",[catextgroup],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
else:
try:
grp.getgrgid(int(catextgroup))
except KeyError:
- print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.",check_SETTINGS)
- check = POSTPROCESS_ERROR
-
- countCategoryName = 1
- for i in range(1, countCategory):
- if catextname == os.environ.get("NZBOP_Category" + str(i) + ".Name") is not None:
+ if overrideid == "yes":
+ print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.","<- ENABLED!!")
+ else:
+ print(catextname,"Category: Invalid Group[UID]:",[catextgroup],"Group not in system.",CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+
+ for i in range(1, countcategory):
+ if catextname == os.environ.get("NZBOP_Category" + str(i) + ".Name"):
if not catextdestdir == os.environ.get("NZBOP_Category" + str(i) + ".DestDir"):
- print(catextname,"Category: Invalid Path:",[catextdestdir],check_SETTINGS)
- check = POSTPROCESS_ERROR
- else:
- countCategoryName +=1
- if countCategoryName >= countCategoryExt:
- print(catextname,"Category: Invalid CategoryExt Name:",[catextname],"Name not same as in CATEGORIES",check_SETTINGS)
- check = POSTPROCESS_ERROR
+ print(catextname,"Category: Invalid Path:",[catextdestdir],CHECK_SETTINGS)
+ check = SCRIPT_ERROR
+
+ catnames = []
+ for i in range(1, countcategory):
+ catname = os.environ.get("NZBOP_Category" + str(i) + ".Name")
+ catnames.append(catname)
+ if catextname not in catnames:
+ print(catextname,"Category: Invalid CategoryExt Name:",[catextname],"Name not same as in CATEGORIES.",CHECK_SETTINGS)
+ check = SCRIPT_ERROR
try:
check
except NameError:
- sys.exit(POSTPROCESS_SUCCESS)
+ sys.exit(SCRIPT_SUCCESS)
else:
sys.exit(check)
@@ -127,7 +170,7 @@
catextaccess = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_ACCESS")
catextowner = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_OWNER")
catextgroup = os.environ.get("NZBPO_CATEGORYEXT" + str(i) + "_GROUP")
- if catextname == None or catextdestdir == None:
+ if catextname is None or catextdestdir is None:
break
categories.append({catextname, catextdestdir, catextaccess, catextowner, catextgroup})
@@ -141,40 +184,45 @@
group = os.environ['NZBPO_GROUP'];
-# If download is an added category, set the related destination dir
+# If download is an added category, read in the related values and dir
if not category == "":
- for i in range(1, countCategoryExt):
- if category == os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name"):
- destdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
- access = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
- owner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
- group = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];
-
+ for i in range(1, countcategoryext):
+ if category == os.environ.get("NZBPO_CategoryExt" + str(i) + ".Name"):
+ destdir = os.environ["NZBPO_CategoryExt" + str(i) + ".DestDir"];
+ access = os.environ["NZBPO_CategoryExt" + str(i) + ".Access"];
+ owner = os.environ["NZBPO_CategoryExt" + str(i) + ".Owner"];
+ group = os.environ["NZBPO_CategoryExt" + str(i) + ".Group"];
# Check if input values are valid
-if not re.match('^[0-7]{3}+$', access):
- print(category,"Category: Invalid Access[mask]:",[access],check_SETTINGS)
- sys.exit(POSTPROCESS_ERROR)
+if not re.match('^[0-7]{3}$', access):
+ print(category,"Category: Invalid Access[mask]:",[access],CHECK_SETTINGS)
+ sys.exit(SCRIPT_ERROR)
-if not re.match('^[0-9]*$', owner):
- print(category,"Category: Invalid Owner[UID]:",[owner],check_SETTINGS)
- sys.exit(POSTPROCESS_ERROR)
+if not re.match('^[0-9]{1,}$', owner):
+ print(category,"Category: Invalid Owner[UID]:",[owner],CHECK_SETTINGS)
+ sys.exit(SCRIPT_ERROR)
else:
try:
pwd.getpwuid(int(owner))
except KeyError:
- print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.",check_SETTINGS)
- sys.exit(POSTPROCESS_ERROR)
-
-if not re.match('^[0-9]*$', group):
- print(category,"Category: Invalid Group[UID]:",[group],check_SETTINGS)
- sys.exit(POSTPROCESS_ERROR)
+ if overrideid == "yes":
+ print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.","<- ENABLED!!")
+ else:
+ print(category,"Category: Invalid Owner[UID]:",[owner],"User not in system.",CHECK_SETTINGS)
+ sys.exit(SCRIPT_ERROR)
+
+if not re.match('^[0-9]{1,}$', group):
+ print(category,"Category: Invalid Group[UID]:",[group],CHECK_SETTINGS)
+ sys.exit(SCRIPT_ERROR)
else:
try:
grp.getgrgid(int(group))
except KeyError:
- print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.",check_SETTINGS)
- sys.exit(POSTPROCESS_ERROR)
+ if overrideid == "yes":
+ print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.","<- ENABLED!!")
+ else:
+ print(category,"Category: Invalid Group[UID]:",[group],"Group not in system.",CHECK_SETTINGS)
+ sys.exit(SCRIPT_ERROR)
# Apply chmod & chown to files in destination dir
@@ -193,4 +241,4 @@
for f in files :
os.chown(os.path.join(root,f), int(owner), int(group))
-sys.exit(POSTPROCESS_SUCCESS)
+sys.exit(SCRIPT_SUCCESS)
diff --git a/manifest.json b/manifest.json
index 98e6884..68d4ce1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,30 +1,44 @@
{
"main": "main.py",
"name": "AccessOwnership",
- "homepage": "https://github.com/nzbgetcom/Extension-AccessOwnership",
+ "homepage": "https://github.com/Lennong/Extension-AccessOwnership",
"kind": "POST-PROCESSING",
"displayName": "AccessOwnership",
- "version": "1.4",
+ "version": "1.6",
"nzbgetMinVersion": "24",
"author": "Lennong",
"license": "GNU",
- "about": "Sets access permissions & ownership to downloaded files.",
+ "about": "Sets access permissions & ownership to downloaded files",
"queueEvents": "",
"requirements": [
"This extension is compatible with NZBGet v24+.",
- "This extension requires Python3.8 and up to be installed on your system."
+ "This extension requires Python3.8 and up to be installed on your system.",
+ "This extension is supported on [Linux, MacOS, Docker].",
+ "",
+ "START INITIAL CONFIGURATION OF THIS EXTENSION BY SAVE AND RELOAD ONE TIME!"
],
"description": [
"This script modifies access permissions & ownership on downloaded files."
],
"options": [
+ {
+ "name": "OverrideID",
+ "displayName": "Override UID and GID",
+ "value": "no",
+ "description": [
+ "To override UID or GID check and allow non-system present UID and GID to be set on downloaded files",
+ "",
+ "NOTE: WARNING! Only use this if you know what you are doing!"
+ ],
+ "select": ["no", "yes"]
+ },
{
"name": "DestDir",
"displayName": "DestDir",
- "value": "",
+ "value": "${DestDir}",
"description": [
"Destination directory for downloads.",
- "Set the same 'DestDir' value as set under the 'PATHS' settings.",
+ "Keep as is or manually set the same 'DestDir' value as set under the 'PATHS' settings.",
"",
"NOTE: Enable this extension in the 'Extensions' setting under 'EXTENSION SCRIPTS' after this extension is configured and saved."
],
@@ -44,22 +58,24 @@
{
"name": "Owner",
"displayName": "Owner",
- "value": 1000,
+ "value": "",
"description": [
- "Sets Owner (uid) to downloaded files. Use positive integers only, starting with '0' and up.",
+ "Sets Owner [UID] to downloaded files. Use positive integers only, starting with '0' and up.",
"",
- "Default = '1000'."
+ "NOTE: Use 'Detect UID and GID' function to detect UID for user that runs this instance of NZBGet.",
+ "NOTE: If use other UID, only use UID present in system or set UID will not be applied on downloaded files."
],
"select": ["UID"]
},
{
"name": "Group",
"displayName": "Group",
- "value": 1000,
+ "value": "",
"description": [
- "Set Group (gid) to downloaded files. Use positive integers only, starting with '0' and up.",
+ "Set Group [GID] to downloaded files. Use positive integers only, starting with '0' and up.",
"",
- "Default = '1000'."
+ "NOTE: Use 'Detect UID and GID' function to detect GID for user that runs this instance of NZBGet.",
+ "NOTE: If use other GID, only use GID present in system or set GID will not be applied on downloaded files."
],
"select": ["GID"]
},
@@ -102,11 +118,12 @@
"section": "Categories",
"name": "Owner",
"displayName": "Owner",
- "value": 1000,
+ "value": "",
"description": [
- "Sets Owner (uid) to downloaded files in this category. Use positive integers only, starting with '0' and up.",
+ "Sets Owner [UID] to downloaded files in this category. Use positive integers only, starting with '0' and up.",
"",
- "Default = '1000'."
+ "NOTE: Use 'Detect UID and GID' function to detect UID for user that runs this instance of NZBGet.",
+ "NOTE: If use other UID, only use UID present in system or set UID will not be applied on downloaded files."
],
"select": ["UID"]
},
@@ -114,25 +131,38 @@
"section": "Categories",
"name": "Group",
"displayName": "Group",
- "value": 1000,
+ "value": "",
"description": [
- "Sets Group (gid) to downloaded files in this category. Use positive integers only, starting with '0' and up.",
+ "Sets Group [GID] to downloaded files in this category. Use positive integers only, starting with '0' and up.",
"",
- "Default = '1000'."
+ "NOTE: Use 'Detect UID and GID' function to detect GID for user that runs this instance of NZBGet.",
+ "NOTE: If use other GID, only use GID present in system or set GID will not be applied on downloaded files."
],
"select": ["GID"]
}
],
"commands": [
+ {
+ "name": "Detect",
+ "action": "Detect now",
+ "displayName": "Detect UID and GID",
+ "description": [
+ "Detects users Owner[UID] and Group[GID]",
+ "",
+ "NOTE: Use this tool to detect Detects UID and GID for settings:",
+ "OWNER: detects UID for user that runs this instance of NZBGet",
+ "GROUP: detects GID for user that runs this instance of NZBGet"
+ ]
+ },
{
"name": "Test",
- "action": "Check now",
- "displayName": "Test",
+ "action": "Test now",
+ "displayName": "Test Settings",
"description": [
"Click to test the settings:",
"",
- "CATEGORIES: extension coorelates with main program",
- "PATHS: extension coorelates with main program",
+ "CATEGORIES: extension coorelates with main settings",
+ "PATHS: extension coorelates with main settings",
"ACCESS: extension checks validity of mask input",
"OWNER: extension checks validity of UID input and present UID in system",
"GROUP: extension checks validity of GID input and present GID in system"