From c40979abdb8d70aeec6a25fbc11cac6c4e708c72 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 14 Jul 2024 11:29:17 -0700 Subject: [PATCH 1/8] add config file definitions --- examples/conf-builder.yml | 480 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 examples/conf-builder.yml diff --git a/examples/conf-builder.yml b/examples/conf-builder.yml new file mode 100644 index 0000000..2a1703a --- /dev/null +++ b/examples/conf-builder.yml @@ -0,0 +1,480 @@ +# Unpackerr Config File Definition +--- + +header: | + #################################################### + ## Unpackerr Example Configuration File ## + #################################################### + ## The following values are application defaults. ## + ## Environment Variables may override all values. ## + ## More configuration help: https://unpackerr.zip ## + ## Generator: https://notifiarr.com/unpackerr.php ## + #################################################### + +envvar_prefix: UN_ +order: + - header + - global_params + - webserver + - starr_header + - starr + - starr_footer + - folder_header + - folder + - folders + - webhook_header + - webhooks + - cmdhooks_header + - cmdhooks + +global_params: +- name: debug + envvar: DEBUG + default: false + comment: false + short: Turns on more logs. + desc: | + [true/false] Turn on debug messages in the output. Do not wrap this in quotes. + Recommend trying this so you know what it looks like. I personally leave it on. +- name: quiet + envvar: QUIET + default: false + comment: false + short: Do not print logs to stdout or stderr. + desc: > + Disable writing messages to stdout/stderr. This silences the app. Set a log + file below if you set this to true. Recommended when starting with systemctl. +- name: error_stderr + envvar: ERROR_STDERR + default: false + comment: false + short: Print ERROR lines to stderr instead of stdout. + desc: | + Send error output to stderr instead of stdout by setting error_stderr to true. + Recommend leaving this at false. Ignored if quiet (above) is true. +- name: activity + envvar: ACTIVITY + default: false + comment: false + short: Setting true will print only queue counts with activity. + desc: | + Setting activity to true will silence all app queue log lines with only zeros. + Set this to true when you want less log spam. +- name: log_queues + envvar: LOG_QUEUES + default: "1m" + comment: false + short: How often to print internal counters. Uses Go Duration. + desc: | + The Starr-application activity queue is logged on an interval. + Adjust that interval with this setting. + Default is a minute. 2m, 5m, 10m, 30m, 1h are also perfectly acceptable. +- name: log_file + envvar: LOG_FILE + default: /downloads/unpackerr.log + comment: true + short: Provide optional file path to write logs + desc: | + Write messages to a log file. This is the same data that is normally output to stdout. + This setting is great for Docker users that want to export their logs to a file. + The alternative is to use syslog to log the output of the application to a file. + Default is no log file; this is unset. + Except on macOS and Windows, the log file gets set to "~/.unpackerr/unpackerr.log" + log_files=0 turns off auto-rotation. + Default files is 10 and size(mb) is 10 Megabytes; both doubled if debug is true. +- name: log_files + envvar: LOG_FILES + default: 10 + comment: false + short: Log files to keep after rotating. `0` disables rotation +- name: log_file_mb + envvar: LOG_FILE_MB + default: 10 + comment: false + short: Max size of log files in megabytes +- name: interval + envvar: INTERVAL + default: 2m + comment: false + short: How often apps are polled, recommend `1m` to `5m` + desc: | + How often to poll starr apps (sonarr, radarr, etc). + Recommend 1m-5m. Uses Go Duration. +- name: start_delay + envvar: START_DELAY + default: 1m + comment: false + short: Files are queued at least this long before extraction. + desc: | + How long an item must be queued (download complete) before extraction will start. + One minute is the historic default and works well. Set higher if your downloads + take longer to finalize (or transfer locally). Uses Go Duration. +- name: retry_delay + envvar: RETRY_DELAY + default: 5m + comment: false + short: Failed extractions are retried after at least this long. + desc: | + How long to wait before removing the history for a failed extraction. + Once the history is deleted the item will be recognized as new and + extraction will start again. Uses Go Duration. +- name: max_retries + envvar: MAX_RETRIES + default: 3 + comment: false + short: Failed extractions are retried after at least this long. + desc: | + How many times to retry a failed extraction. Pauses retry_delay between attempts. +- name: parallel + envvar: PARALLEL + default: 1 + comment: false + short: Times to retry failed extractions. `0` = unlimited. + desc: | + How many files may be extracted in parallel. 1 works fine. + Do not wrap the number in quotes. Raise this only if you have fast disks and CPU. +- name: file_mode + envvar: FILE_MODE + default: '0644' + comment: false + short: Extracted files are written with this mode. + desc: | + Use these configurations to control the file modes used for newly extracted + files and folders. Recommend 0644/0755 or 0666/0777. +- name: dir_mode + envvar: DIR_MODE + default: '0755' + comment: false + short: Extracted folders are written with this mode + +webserver: +envvar_prefix: WEBSERVER +params: +- name: metrics + envvar: METRICS + default: false + short: Extracted folders are written with this mode + desc: The web server currently only supports metrics; set this to true if you wish to use it. +- name: listen_addr + envvar: LISTEN_ADDR + default: 0.0.0.0:5656 + short: ip:port to listen on; `0.0.0.0` is all IPs. + desc: This may be set to a port or an ip:port to bind a specific IP. 0.0.0.0 binds ALL IPs. +- name: log_file + envvar: LOG_FILE + default: '' + short: Provide optional file path to write HTTP logs. + desc: Recommend setting a log file for HTTP requests. Otherwise, they go with other logs. +- name: log_files + envvar: LOG_FILES + default: 10 + short: Log files to keep after rotating. `0` to disable. + desc: This app automatically rotates logs. Set these to the size and number to keep. +- name: log_file_mb + envvar: LOG_FILE_MB + default: 10 + short: Max size of HTTP log files in megabytes +- name: ssl_cert_file + envvar: SSL_CERT_FILE + default: '' + short: Path to SSL cert file to serve HTTPS. + desc: Set both of these to valid file paths to enable HTTPS/TLS. +- name: ssl_key_file + envvar: SSL_KEY_FILE + default: '' + short: Path to SSL key file to serve HTTPS. +- name: urlbase + envvar: URLBASE + default: / + short: Base URL path to serve HTTP content. + desc: Base URL from which to serve content. +- name: upstreams + envvar: UPSTREAMS + default: [] + kind: conlist + short: List of upstream proxy CIDRs or IPs to trust. + desc: | + Upstreams should be set to the IP or CIDR of your trusted upstream proxy. + Setting this correctly allows X-Forwarded-For to be used in logs. + In the future it may control auth proxy trust. Must be a list of strings. + example: upstreams = [ "127.0.0.1/32", "10.1.2.0/24" ] + +starr_header: | + ############################################################################### + ##-IMPORTANT-#######-READ THIS!!!-################ Seriously, read this. ###### + ############################################################################### + ## The following sections can be repeated if you have more than one Sonarr, ## + ## Radarr, Lidarr, Readarr, Whisparr, Folder, Webhook, and/or Command Hook. ## + ## You MUST uncomment the [[header]], url and api_key at for any Starr app. ## + ## The [[sonarr]] and [[radarr]] headers come uncommented. Uncomment the url ## + ## and api_key if they are in use. Comment them with a hash if they are not. ## + ## Uncomment the [[lidarr]] and/or [[readarr]] headers and values if in use. ## + ############################################################################### + ############################################################################### + ## ALL LINES BEGINNING WITH A HASH # ARE IGNORED COMMENTS ## + ## REMOVE THE HASH # FROM CONFIG LINES YOU WANT TO CHANGE ## + ############################################################################### + ############################################################################### + +starrs: +- sonarr: + comment: false + header: | + Leaving the [[sonarr]] header uncommented (no leading hash #) without also + uncommenting the api_key (remove the hash #) will produce a startup warning. + url: http://127.0.0.1:8989 +- radarr: + comment: false + header: | + Leaving the [[radarr]] header uncommented (no leading hash #) without also + uncommenting the api_key (remove the hash #) will produce a startup warning. + url: http://127.0.0.1:7878 +- lidarr: + comment: true + url: http://127.0.0.1:8686 +- readarr: + comment: true + url: http://127.0.0.1:8787 +- whisparr: + comment: true + url: http://127.0.0.1:6969 + +starr: +- name: url + envvar: URL + short: URL where this starr app can be accessed. +- name: api_key + envvar: API_KEY + default: 0123456789abcdef0123456789abcdef + short: Provide URL and API key if you use this app. +- name: paths + envvar: PATHS + default: ['/downloads'] + kind: list + short: File system path where downloaded items are located. + desc: | + List of paths where content is downloaded for this app. + Used as fallback if the path the Starr app reports does not exist or is not accessible. +- name: protocols + envvar: PROTOCOLS + default: torrent + short: 'Protocols to process. Alt: `torrent,usenet`' + desc: 'Default protocols is torrent. Alternative: torrent,usenet' +- name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for the app to respond. + desc: How long to wait for a reply from the backend. +- name: delete_delay + envvar: DELETE_DELAY + default: false + short: Extracts are deleted this long after import, `-1s` to disable. + desc: How long to wait after import before deleting the extracted items. +- name: delete_orig + envvar: DELETE_ORIG + default: false + short: Delete archives after import? Recommend keeping this false. + desc: | + If you use this app with NZB you may wish to delete archives after extraction. + General recommendation is: do not enable this for torrent use. + Setting this to true deletes the entire original download folder after import. +- name: syncthing + envvar: SYNCTHING + default: false + short: Setting this to true makes unpackerr wait for syncthing to finish. + desc: If you use Syncthing, setting this to true will make unpackerr wait for syncs to finish. + +starr_footer: | + ################################################################################## + ### ### STOP HERE ### STOP HERE ### STOP HERE ### STOP HERE #### STOP HERE ### # + ### Only using Starr apps? The things above. The below configs are OPTIONAL. ### # + ################################################################################## + +folder_header: | + ##-Folders-####################################################################### + ## This application can also watch folders for things to extract. If you copy a ## + ## subfolder into a watched folder (defined below) any extractable items in the ## + ## folder will be decompressed. This has nothing to do with Starr applications. ## + ################################################################################## + +folder: +- name: interval + envvar: INTERVAL + default: 1s + short: How often poller checks for new folders. Use `1ms` to disable it. + desc: How often poller checks for new folders. Use `1ms` to disable it. +- name: buffer + envvar: BUFFER + default: 20000 + short: How many new folder items can be tracked at once. + desc: How many new folder items can be tracked at once. + +folders: +- name: path + envvar: PATH + default: '' + example: /some/folder/to/watch + short: Folder to watch for archives. **Not for Starr apps.** +- name: extract_path + envvar: EXTRACT_PATH + default: '' + short: Where to extract to. Default is the same as `path`. + desc: Path to extract files to. The default (leaving this blank) is the same as `path` (above). +- name: delete_after + envvar: DELETE_AFTER + default: 10m + short: Delete extracted files and/or archives after this duration; `0` disables. + desc: | + Delete extracted or original files this long after extraction. + The default is 0. Set to 0 to disable all deletes. Uncomment it to enable deletes. Uses Go Duration. +- name: disable_recursion + envvar: DISABLE_RECURSION + default: false + short: Setting this to true disables extracting archives inside archives. + desc: Unpackerr extracts archives inside archives. Set this to true to disable recursive extractions. +- name: delete_files + envvar: DELETE_FILES + default: false + short: Delete extracted files after successful extraction. + desc: Delete extracted files after successful extraction? Honors delete_after. +- name: delete_original + envvar: DELETE_ORIGINAL + default: false + short: Delete archives after successful extraction. + desc: Delete original items after successful extraction? true/false, no quotes. Honors delete_after. +- name: disable_log + envvar: DISABLE_LOG + default: false + short: Turns off creation of extraction logs files for this folder. + desc: Disable extraction log (unpackerred.txt) file creation? +- name: move_back + envvar: MOVE_BACK + default: false + short: Move extracted items back into original folder. + desc: Move extracted files into original folder? If false, files go into an _unpackerred folder. +- name: extract_isos + envvar: EXTRACT_ISOS + default: false + short: Setting this to true enables .iso file extraction. + desc: Set this to true if you want this app to extract ISO files with .iso extension. + +webhook_header: | + ################ + ### Webhooks ### + ################ + # Sends a webhook when an extraction queues, starts, finishes, and/or is deleted. + # Created to integrate with notifiarr.com. + # Also works natively with Discord.com, Telegram.org, and Slack.com webhooks. + # Can possibly be used with other services by providing a custom template_path. + ###### Don't forget to uncomment [[webhook]] and url at a minimum !!!! + +webhooks: +- name: url + envvar: URL + default: '' + example: https://notifiarr.com/api/v1/notification/unpackerr/api_key_from_notifiarr_com + short: URL to send POST webhook to. +- name: name + envvar: NAME + default: '' + short: Provide an optional name to hide the URL in logs. + desc: Provide an optional name to hide the URL in logs. +- name: silent + envvar: SILENT + default: false + short: Hide successful POSTs from logs. + desc: Do not log success (less log spam). +- name: events + envvar: EVENTS + default: [0] + short: List of event ids to send notification for, `0` == all. + desc: List of event ids to send notification for, 0 == all. +- name: nickname + envvar: NICKNAME + default: '' + short: Passed into templates for telegram, discord and slack hooks. + desc: | + ### Advanced Optional Webhook Configuration + Used in Discord and Slack templates as bot name, in Telegram as chat_id. +- name: channel + envvar: CHANNEL + default: '' + short: Passed into templates for slack.com webhooks. + desc: Also passed into templates. Used in Slack templates for destination channel. +- name: exclude + envvar: EXCLUDE + default: [] + short: 'List of apps to exclude: radarr, sonarr, folders, etc.' + desc: List of apps to exclude, ie. ["radarr", "lidarr"] +- name: template_path + envvar: TEMPLATE_PATH + default: '' + short: Instead of an internal template, provide your own. + desc: Override internal webhook template for discord.com or other hooks. +- name: template + envvar: TEMPLATE + default: '' + short: Instead of auto template selection, force a built-in template. + desc: 'Override automatic template detection. Values: notifiarr, discord, telegram, gotify, pushover, slack' +- name: ignore_ssl + envvar: IGNORE_SSL + default: false + short: Ignore invalid SSL certificates. + desc: Set this to true to ignore the SSL certificate on the server. +- name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for server response. + desc: You can adjust how long to wait for a server response. +- name: content_type + envvar: CONTENT_TYPE + default: application/json + short: Content-Type header sent to webhook. + desc: If your custom template uses another MIME type, set this. + +cmdhooks_header: | + ##################### + ### Command Hooks ### + ##################### + # Executes a script or command when an extraction queues, starts, finishes, and/or is deleted. + # All data is passed in as environment variables. Try /usr/bin/env to see what variables are available. + ###### Don't forget to uncomment [[cmdhook]] at a minimum !!!! + +cmdhooks: +- name: command + envvar: COMMAND + default: '' + example: /my/cool/app + short: Command to run. +- name: name + envvar: NAME + default: '' + short: Provide an optional name to hide the URL in logs. + desc: Provide an optional name to hide the URL in logs. +- name: shell + envvar: SHELL + default: false + short: Run command inside a shell. + desc: Runs the command inside /bin/sh ('nix) or cmd.exe (Windows). +- name: silent + envvar: SILENT + default: false + short: Hide command output from logs. + desc: Do not log command's output. +- name: events + envvar: EVENTS + default: [0] + short: List of event ids to run command for, `0` == all. + desc: List of event ids to run command for, 0 == all. +- name: exclude + envvar: EXCLUDE + default: [] + short: 'List of apps to exclude: radarr, sonarr, folders, etc.' + desc: | + ### Optional Command Hook Configuration + List of apps to exclude, ie. ["radarr", "lidarr"] +- name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for command to run. + desc: You can adjust how long to wait for command to run. From c3b5fc23a291e658994821e2b65d19acb0e950fe Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 14 Jul 2024 11:38:11 -0700 Subject: [PATCH 2/8] fixes --- examples/conf-builder.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/conf-builder.yml b/examples/conf-builder.yml index 2a1703a..479a732 100644 --- a/examples/conf-builder.yml +++ b/examples/conf-builder.yml @@ -245,7 +245,8 @@ starr: short: URL where this starr app can be accessed. - name: api_key envvar: API_KEY - default: 0123456789abcdef0123456789abcdef + default: '' + example: 0123456789abcdef0123456789abcdef short: Provide URL and API key if you use this app. - name: paths envvar: PATHS @@ -387,6 +388,7 @@ webhooks: - name: events envvar: EVENTS default: [0] + kind: list short: List of event ids to send notification for, `0` == all. desc: List of event ids to send notification for, 0 == all. - name: nickname @@ -404,6 +406,7 @@ webhooks: - name: exclude envvar: EXCLUDE default: [] + kind: list short: 'List of apps to exclude: radarr, sonarr, folders, etc.' desc: List of apps to exclude, ie. ["radarr", "lidarr"] - name: template_path @@ -464,11 +467,13 @@ cmdhooks: - name: events envvar: EVENTS default: [0] + kind: list short: List of event ids to run command for, `0` == all. desc: List of event ids to run command for, 0 == all. - name: exclude envvar: EXCLUDE default: [] + kind: list short: 'List of apps to exclude: radarr, sonarr, folders, etc.' desc: | ### Optional Command Hook Configuration From 65459abf518633d09a195b1f019610bf59900769 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 14 Jul 2024 15:35:57 -0700 Subject: [PATCH 3/8] conf builder --- examples/conf-builder.yml | 485 ---------------------------------- init/config/README.md | 3 + init/config/conf-builder.go | 160 ++++++++++++ init/config/conf-builder.yml | 494 +++++++++++++++++++++++++++++++++++ 4 files changed, 657 insertions(+), 485 deletions(-) delete mode 100644 examples/conf-builder.yml create mode 100644 init/config/README.md create mode 100644 init/config/conf-builder.go create mode 100644 init/config/conf-builder.yml diff --git a/examples/conf-builder.yml b/examples/conf-builder.yml deleted file mode 100644 index 479a732..0000000 --- a/examples/conf-builder.yml +++ /dev/null @@ -1,485 +0,0 @@ -# Unpackerr Config File Definition ---- - -header: | - #################################################### - ## Unpackerr Example Configuration File ## - #################################################### - ## The following values are application defaults. ## - ## Environment Variables may override all values. ## - ## More configuration help: https://unpackerr.zip ## - ## Generator: https://notifiarr.com/unpackerr.php ## - #################################################### - -envvar_prefix: UN_ -order: - - header - - global_params - - webserver - - starr_header - - starr - - starr_footer - - folder_header - - folder - - folders - - webhook_header - - webhooks - - cmdhooks_header - - cmdhooks - -global_params: -- name: debug - envvar: DEBUG - default: false - comment: false - short: Turns on more logs. - desc: | - [true/false] Turn on debug messages in the output. Do not wrap this in quotes. - Recommend trying this so you know what it looks like. I personally leave it on. -- name: quiet - envvar: QUIET - default: false - comment: false - short: Do not print logs to stdout or stderr. - desc: > - Disable writing messages to stdout/stderr. This silences the app. Set a log - file below if you set this to true. Recommended when starting with systemctl. -- name: error_stderr - envvar: ERROR_STDERR - default: false - comment: false - short: Print ERROR lines to stderr instead of stdout. - desc: | - Send error output to stderr instead of stdout by setting error_stderr to true. - Recommend leaving this at false. Ignored if quiet (above) is true. -- name: activity - envvar: ACTIVITY - default: false - comment: false - short: Setting true will print only queue counts with activity. - desc: | - Setting activity to true will silence all app queue log lines with only zeros. - Set this to true when you want less log spam. -- name: log_queues - envvar: LOG_QUEUES - default: "1m" - comment: false - short: How often to print internal counters. Uses Go Duration. - desc: | - The Starr-application activity queue is logged on an interval. - Adjust that interval with this setting. - Default is a minute. 2m, 5m, 10m, 30m, 1h are also perfectly acceptable. -- name: log_file - envvar: LOG_FILE - default: /downloads/unpackerr.log - comment: true - short: Provide optional file path to write logs - desc: | - Write messages to a log file. This is the same data that is normally output to stdout. - This setting is great for Docker users that want to export their logs to a file. - The alternative is to use syslog to log the output of the application to a file. - Default is no log file; this is unset. - Except on macOS and Windows, the log file gets set to "~/.unpackerr/unpackerr.log" - log_files=0 turns off auto-rotation. - Default files is 10 and size(mb) is 10 Megabytes; both doubled if debug is true. -- name: log_files - envvar: LOG_FILES - default: 10 - comment: false - short: Log files to keep after rotating. `0` disables rotation -- name: log_file_mb - envvar: LOG_FILE_MB - default: 10 - comment: false - short: Max size of log files in megabytes -- name: interval - envvar: INTERVAL - default: 2m - comment: false - short: How often apps are polled, recommend `1m` to `5m` - desc: | - How often to poll starr apps (sonarr, radarr, etc). - Recommend 1m-5m. Uses Go Duration. -- name: start_delay - envvar: START_DELAY - default: 1m - comment: false - short: Files are queued at least this long before extraction. - desc: | - How long an item must be queued (download complete) before extraction will start. - One minute is the historic default and works well. Set higher if your downloads - take longer to finalize (or transfer locally). Uses Go Duration. -- name: retry_delay - envvar: RETRY_DELAY - default: 5m - comment: false - short: Failed extractions are retried after at least this long. - desc: | - How long to wait before removing the history for a failed extraction. - Once the history is deleted the item will be recognized as new and - extraction will start again. Uses Go Duration. -- name: max_retries - envvar: MAX_RETRIES - default: 3 - comment: false - short: Failed extractions are retried after at least this long. - desc: | - How many times to retry a failed extraction. Pauses retry_delay between attempts. -- name: parallel - envvar: PARALLEL - default: 1 - comment: false - short: Times to retry failed extractions. `0` = unlimited. - desc: | - How many files may be extracted in parallel. 1 works fine. - Do not wrap the number in quotes. Raise this only if you have fast disks and CPU. -- name: file_mode - envvar: FILE_MODE - default: '0644' - comment: false - short: Extracted files are written with this mode. - desc: | - Use these configurations to control the file modes used for newly extracted - files and folders. Recommend 0644/0755 or 0666/0777. -- name: dir_mode - envvar: DIR_MODE - default: '0755' - comment: false - short: Extracted folders are written with this mode - -webserver: -envvar_prefix: WEBSERVER -params: -- name: metrics - envvar: METRICS - default: false - short: Extracted folders are written with this mode - desc: The web server currently only supports metrics; set this to true if you wish to use it. -- name: listen_addr - envvar: LISTEN_ADDR - default: 0.0.0.0:5656 - short: ip:port to listen on; `0.0.0.0` is all IPs. - desc: This may be set to a port or an ip:port to bind a specific IP. 0.0.0.0 binds ALL IPs. -- name: log_file - envvar: LOG_FILE - default: '' - short: Provide optional file path to write HTTP logs. - desc: Recommend setting a log file for HTTP requests. Otherwise, they go with other logs. -- name: log_files - envvar: LOG_FILES - default: 10 - short: Log files to keep after rotating. `0` to disable. - desc: This app automatically rotates logs. Set these to the size and number to keep. -- name: log_file_mb - envvar: LOG_FILE_MB - default: 10 - short: Max size of HTTP log files in megabytes -- name: ssl_cert_file - envvar: SSL_CERT_FILE - default: '' - short: Path to SSL cert file to serve HTTPS. - desc: Set both of these to valid file paths to enable HTTPS/TLS. -- name: ssl_key_file - envvar: SSL_KEY_FILE - default: '' - short: Path to SSL key file to serve HTTPS. -- name: urlbase - envvar: URLBASE - default: / - short: Base URL path to serve HTTP content. - desc: Base URL from which to serve content. -- name: upstreams - envvar: UPSTREAMS - default: [] - kind: conlist - short: List of upstream proxy CIDRs or IPs to trust. - desc: | - Upstreams should be set to the IP or CIDR of your trusted upstream proxy. - Setting this correctly allows X-Forwarded-For to be used in logs. - In the future it may control auth proxy trust. Must be a list of strings. - example: upstreams = [ "127.0.0.1/32", "10.1.2.0/24" ] - -starr_header: | - ############################################################################### - ##-IMPORTANT-#######-READ THIS!!!-################ Seriously, read this. ###### - ############################################################################### - ## The following sections can be repeated if you have more than one Sonarr, ## - ## Radarr, Lidarr, Readarr, Whisparr, Folder, Webhook, and/or Command Hook. ## - ## You MUST uncomment the [[header]], url and api_key at for any Starr app. ## - ## The [[sonarr]] and [[radarr]] headers come uncommented. Uncomment the url ## - ## and api_key if they are in use. Comment them with a hash if they are not. ## - ## Uncomment the [[lidarr]] and/or [[readarr]] headers and values if in use. ## - ############################################################################### - ############################################################################### - ## ALL LINES BEGINNING WITH A HASH # ARE IGNORED COMMENTS ## - ## REMOVE THE HASH # FROM CONFIG LINES YOU WANT TO CHANGE ## - ############################################################################### - ############################################################################### - -starrs: -- sonarr: - comment: false - header: | - Leaving the [[sonarr]] header uncommented (no leading hash #) without also - uncommenting the api_key (remove the hash #) will produce a startup warning. - url: http://127.0.0.1:8989 -- radarr: - comment: false - header: | - Leaving the [[radarr]] header uncommented (no leading hash #) without also - uncommenting the api_key (remove the hash #) will produce a startup warning. - url: http://127.0.0.1:7878 -- lidarr: - comment: true - url: http://127.0.0.1:8686 -- readarr: - comment: true - url: http://127.0.0.1:8787 -- whisparr: - comment: true - url: http://127.0.0.1:6969 - -starr: -- name: url - envvar: URL - short: URL where this starr app can be accessed. -- name: api_key - envvar: API_KEY - default: '' - example: 0123456789abcdef0123456789abcdef - short: Provide URL and API key if you use this app. -- name: paths - envvar: PATHS - default: ['/downloads'] - kind: list - short: File system path where downloaded items are located. - desc: | - List of paths where content is downloaded for this app. - Used as fallback if the path the Starr app reports does not exist or is not accessible. -- name: protocols - envvar: PROTOCOLS - default: torrent - short: 'Protocols to process. Alt: `torrent,usenet`' - desc: 'Default protocols is torrent. Alternative: torrent,usenet' -- name: timeout - envvar: TIMEOUT - default: 10s - short: How long to wait for the app to respond. - desc: How long to wait for a reply from the backend. -- name: delete_delay - envvar: DELETE_DELAY - default: false - short: Extracts are deleted this long after import, `-1s` to disable. - desc: How long to wait after import before deleting the extracted items. -- name: delete_orig - envvar: DELETE_ORIG - default: false - short: Delete archives after import? Recommend keeping this false. - desc: | - If you use this app with NZB you may wish to delete archives after extraction. - General recommendation is: do not enable this for torrent use. - Setting this to true deletes the entire original download folder after import. -- name: syncthing - envvar: SYNCTHING - default: false - short: Setting this to true makes unpackerr wait for syncthing to finish. - desc: If you use Syncthing, setting this to true will make unpackerr wait for syncs to finish. - -starr_footer: | - ################################################################################## - ### ### STOP HERE ### STOP HERE ### STOP HERE ### STOP HERE #### STOP HERE ### # - ### Only using Starr apps? The things above. The below configs are OPTIONAL. ### # - ################################################################################## - -folder_header: | - ##-Folders-####################################################################### - ## This application can also watch folders for things to extract. If you copy a ## - ## subfolder into a watched folder (defined below) any extractable items in the ## - ## folder will be decompressed. This has nothing to do with Starr applications. ## - ################################################################################## - -folder: -- name: interval - envvar: INTERVAL - default: 1s - short: How often poller checks for new folders. Use `1ms` to disable it. - desc: How often poller checks for new folders. Use `1ms` to disable it. -- name: buffer - envvar: BUFFER - default: 20000 - short: How many new folder items can be tracked at once. - desc: How many new folder items can be tracked at once. - -folders: -- name: path - envvar: PATH - default: '' - example: /some/folder/to/watch - short: Folder to watch for archives. **Not for Starr apps.** -- name: extract_path - envvar: EXTRACT_PATH - default: '' - short: Where to extract to. Default is the same as `path`. - desc: Path to extract files to. The default (leaving this blank) is the same as `path` (above). -- name: delete_after - envvar: DELETE_AFTER - default: 10m - short: Delete extracted files and/or archives after this duration; `0` disables. - desc: | - Delete extracted or original files this long after extraction. - The default is 0. Set to 0 to disable all deletes. Uncomment it to enable deletes. Uses Go Duration. -- name: disable_recursion - envvar: DISABLE_RECURSION - default: false - short: Setting this to true disables extracting archives inside archives. - desc: Unpackerr extracts archives inside archives. Set this to true to disable recursive extractions. -- name: delete_files - envvar: DELETE_FILES - default: false - short: Delete extracted files after successful extraction. - desc: Delete extracted files after successful extraction? Honors delete_after. -- name: delete_original - envvar: DELETE_ORIGINAL - default: false - short: Delete archives after successful extraction. - desc: Delete original items after successful extraction? true/false, no quotes. Honors delete_after. -- name: disable_log - envvar: DISABLE_LOG - default: false - short: Turns off creation of extraction logs files for this folder. - desc: Disable extraction log (unpackerred.txt) file creation? -- name: move_back - envvar: MOVE_BACK - default: false - short: Move extracted items back into original folder. - desc: Move extracted files into original folder? If false, files go into an _unpackerred folder. -- name: extract_isos - envvar: EXTRACT_ISOS - default: false - short: Setting this to true enables .iso file extraction. - desc: Set this to true if you want this app to extract ISO files with .iso extension. - -webhook_header: | - ################ - ### Webhooks ### - ################ - # Sends a webhook when an extraction queues, starts, finishes, and/or is deleted. - # Created to integrate with notifiarr.com. - # Also works natively with Discord.com, Telegram.org, and Slack.com webhooks. - # Can possibly be used with other services by providing a custom template_path. - ###### Don't forget to uncomment [[webhook]] and url at a minimum !!!! - -webhooks: -- name: url - envvar: URL - default: '' - example: https://notifiarr.com/api/v1/notification/unpackerr/api_key_from_notifiarr_com - short: URL to send POST webhook to. -- name: name - envvar: NAME - default: '' - short: Provide an optional name to hide the URL in logs. - desc: Provide an optional name to hide the URL in logs. -- name: silent - envvar: SILENT - default: false - short: Hide successful POSTs from logs. - desc: Do not log success (less log spam). -- name: events - envvar: EVENTS - default: [0] - kind: list - short: List of event ids to send notification for, `0` == all. - desc: List of event ids to send notification for, 0 == all. -- name: nickname - envvar: NICKNAME - default: '' - short: Passed into templates for telegram, discord and slack hooks. - desc: | - ### Advanced Optional Webhook Configuration - Used in Discord and Slack templates as bot name, in Telegram as chat_id. -- name: channel - envvar: CHANNEL - default: '' - short: Passed into templates for slack.com webhooks. - desc: Also passed into templates. Used in Slack templates for destination channel. -- name: exclude - envvar: EXCLUDE - default: [] - kind: list - short: 'List of apps to exclude: radarr, sonarr, folders, etc.' - desc: List of apps to exclude, ie. ["radarr", "lidarr"] -- name: template_path - envvar: TEMPLATE_PATH - default: '' - short: Instead of an internal template, provide your own. - desc: Override internal webhook template for discord.com or other hooks. -- name: template - envvar: TEMPLATE - default: '' - short: Instead of auto template selection, force a built-in template. - desc: 'Override automatic template detection. Values: notifiarr, discord, telegram, gotify, pushover, slack' -- name: ignore_ssl - envvar: IGNORE_SSL - default: false - short: Ignore invalid SSL certificates. - desc: Set this to true to ignore the SSL certificate on the server. -- name: timeout - envvar: TIMEOUT - default: 10s - short: How long to wait for server response. - desc: You can adjust how long to wait for a server response. -- name: content_type - envvar: CONTENT_TYPE - default: application/json - short: Content-Type header sent to webhook. - desc: If your custom template uses another MIME type, set this. - -cmdhooks_header: | - ##################### - ### Command Hooks ### - ##################### - # Executes a script or command when an extraction queues, starts, finishes, and/or is deleted. - # All data is passed in as environment variables. Try /usr/bin/env to see what variables are available. - ###### Don't forget to uncomment [[cmdhook]] at a minimum !!!! - -cmdhooks: -- name: command - envvar: COMMAND - default: '' - example: /my/cool/app - short: Command to run. -- name: name - envvar: NAME - default: '' - short: Provide an optional name to hide the URL in logs. - desc: Provide an optional name to hide the URL in logs. -- name: shell - envvar: SHELL - default: false - short: Run command inside a shell. - desc: Runs the command inside /bin/sh ('nix) or cmd.exe (Windows). -- name: silent - envvar: SILENT - default: false - short: Hide command output from logs. - desc: Do not log command's output. -- name: events - envvar: EVENTS - default: [0] - kind: list - short: List of event ids to run command for, `0` == all. - desc: List of event ids to run command for, 0 == all. -- name: exclude - envvar: EXCLUDE - default: [] - kind: list - short: 'List of apps to exclude: radarr, sonarr, folders, etc.' - desc: | - ### Optional Command Hook Configuration - List of apps to exclude, ie. ["radarr", "lidarr"] -- name: timeout - envvar: TIMEOUT - default: 10s - short: How long to wait for command to run. - desc: You can adjust how long to wait for command to run. diff --git a/init/config/README.md b/init/config/README.md new file mode 100644 index 0000000..f8e082f --- /dev/null +++ b/init/config/README.md @@ -0,0 +1,3 @@ +- All params must have a default, even if it's `[]` or `''`. +- Examples override params, but get commented out. +- All list params are commented. diff --git a/init/config/conf-builder.go b/init/config/conf-builder.go new file mode 100644 index 0000000..ebf0f00 --- /dev/null +++ b/init/config/conf-builder.go @@ -0,0 +1,160 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + + "github.com/BurntSushi/toml" + "gopkg.in/yaml.v3" +) + +type Param struct { + Name string `yaml:"name"` + EnvVar string `yaml:"envvar"` + Default any `yaml:"default"` + Example any `yaml:"example"` + Short string `yaml:"short"` + Desc string `yaml:"desc"` + Kind string `yaml:"kind"` // "", list, conlist +} + +type Header struct { + Text string `yaml:"text"` + Prefix string `yaml:"envvar_prefix"` + Params []*Param `yaml:"params"` + Kind string `yaml:"kind"` // "", list + NoHeader bool `yaml:"no_header"` // Do not print [section] header. +} + +type Def struct { + Comment bool `yaml:"comment"` // just the header. + Text string `yaml:"text"` + Defaults map[string]any `yaml:"defaults"` +} + +type Defs map[string]*Def + +type ConfigFile struct { + Defs map[string]Defs `yaml:"defs"` + Prefix string `yaml:"envvar_prefix"` + Order []string `yaml:"order"` + Sections map[string]*Header `yaml:"sections"` +} + +func main() { + file, err := os.Open("./conf-builder.yml") + if err != nil { + panic(err) + } + + config := &ConfigFile{} + // Decode conf-builder file into Go data structure. + if err = yaml.NewDecoder(file).Decode(config); err != nil { + panic(err) + } + + // Loop the 'Order' list. + for _, section := range config.Order { + // If Order contains a missing section, panic. + if config.Sections[section] == nil { + panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.") + } + + if config.Defs[section] != nil { + printDefinedSection(config.Sections[section], config.Defs[section]) + } else { + printSection(section, config.Sections[section], false) + } + } +} + +// Not all sections have defs, and it may be nil. Defs only work on 'list' sections. +func printSection(name string, section *Header, noComment bool) { + // Print section header text. + if section.Text != "" { + fmt.Printf("%s", section.Text) + } + + comment := "#" + if noComment { + // this only happens when a defined section has a comment override on the repeating headers. + comment = "" + } + + if !section.NoHeader { // Print the [section] or [[section]] header. + if section.Kind == "list" { // list sections are commented by default. + fmt.Println(comment + "[[" + name + "]]") // list sections use double-brackets. + } else { + fmt.Println("[" + name + "]") // non-list sections use single brackets. + } + } + + for _, param := range section.Params { + // Print an empty newline for each param if the section has no header and the param has a description. + if section.NoHeader && param.Desc != "" { + fmt.Println() + } + + // Add ## to the beginning of each line in the description. + // Uses the newline \n character to figure out where each line begins. + if param.Desc != "" { + fmt.Println("##", strings.ReplaceAll(strings.TrimSpace(param.Desc), "\n", "\n## ")) + } + + // If example is not empty, use that commented out, otherwise use the default. + if comment = ""; param.Example != nil { + comment = "#" + } + + if section.Kind == "list" { + // If the 'kind' is a 'list', we comment all the parameters. + fmt.Printf("# %s = %s\n", param.Name, param.Value()) + } else { + fmt.Printf("%s%s = %s\n", comment, param.Name, param.Value()) + } + } + + // Each section needs a newline at the end. + fmt.Println() +} + +func (p *Param) Value() string { + // If example is not empty, use that commented out, otherwise use the default. + out, _ := toml.Marshal(p.Default) + if p.Example != nil { + out, _ = toml.Marshal(p.Example) + } + + // The toml marshaller uses only regular quotes " which kinda suck, so replace them with single quotes ' on file paths. + if strings.Contains(p.Name, "path") || strings.HasSuffix(p.Name, "file") || p.Name == "command" { + return string(bytes.ReplaceAll(out, []byte{'"'}, []byte("'"))) + } + + return string(out) +} + +// printDefinedSection duplicates sections from overrides, and prints it once for each override. +func printDefinedSection(section *Header, defs Defs) { + for name, def := range defs { + // Loop each defined section Defaults, and see if one of the param names match. + for overrideName, override := range def.Defaults { + for _, defined := range section.Params { + // If the name of the default (override) matches this param name, overwrite the value. + if defined.Name == overrideName { + defined.Default = override + } + } + } + + // Make a brand new section and pass it back in. + printSection(name, &Header{ + Text: def.Text, + Prefix: section.Prefix, + Params: section.Params, + Kind: section.Kind, + NoHeader: false, + }, !def.Comment) // Only defined sections can comment the header. + } +} diff --git a/init/config/conf-builder.yml b/init/config/conf-builder.yml new file mode 100644 index 0000000..9ede0eb --- /dev/null +++ b/init/config/conf-builder.yml @@ -0,0 +1,494 @@ +# Unpackerr Config File Definition +--- + +envvar_prefix: UN_ + +order: + - global + - webserver + - folders + - starr_header + - starr + - folder + - webhook + - cmdhook + +defs: + starr: + sonarr: + text: | + ## Leaving the [[sonarr]] header uncommented (no leading hash #) without also + ## uncommenting the api_key (remove the hash #) will produce a startup warning. + defaults: + url: http://127.0.0.1:8989 + radarr: + text: | + ## Leaving the [[radarr]] header uncommented (no leading hash #) without also + ## uncommenting the api_key (remove the hash #) will produce a startup warning. + defaults: + url: http://127.0.0.1:7878 + lidarr: + comment: true + defaults: + url: http://127.0.0.1:8686 + readarr: + comment: true + defaults: + url: http://127.0.0.1:8787 + whisparr: + comment: true + defaults: + url: http://127.0.0.1:6969 + +sections: + + global: + no_header: true + text: | + ####################################################### + ## Unpackerr Example Configuration File ## + ####################################################### + ## The displayed values are application defaults. ## + ## Environment Variables may override all values. ## + ## More configuration help: https://unpackerr.zip ## + ## Config Generator: https://notifiarr.com/unpackerr ## + ####################################################### + params: + - name: debug + envvar: DEBUG + default: false + short: Turns on more logs. + desc: | + [true/false] Turn on debug messages in the output. Do not wrap this in quotes. + Recommend trying this so you know what it looks like. I personally leave it on. + - name: quiet + envvar: QUIET + default: false + short: Do not print logs to stdout or stderr. + desc: | + Disable writing messages to stdout/stderr. This silences the app. Set a log + file below if you set this to true. Recommended when starting with systemctl. + - name: error_stderr + envvar: ERROR_STDERR + default: false + short: Print ERROR lines to stderr instead of stdout. + desc: | + Send error output to stderr instead of stdout by setting error_stderr to true. + Recommend leaving this at false. Ignored if quiet (above) is true. + - name: activity + envvar: ACTIVITY + default: false + short: Setting true will print only queue counts with activity. + desc: | + Setting activity to true will silence all app queue log lines with only zeros. + Set this to true when you want less log spam. + - name: log_queues + envvar: LOG_QUEUES + default: "1m" + short: How often to print internal counters. Uses Go Duration. + desc: | + The Starr-application activity queue is logged on an interval. + Adjust that interval with this setting. + Default is a minute. 2m, 5m, 10m, 30m, 1h are also perfectly acceptable. + - name: log_file + envvar: LOG_FILE + default: '' + example: /downloads/unpackerr.log + short: Provide optional file path to write logs + desc: | + Write messages to a log file. This is the same data that is normally output to stdout. + This setting is great for Docker users that want to export their logs to a file. + The alternative is to use syslog to log the output of the application to a file. + Default is no log file; this is unset. + Except on macOS and Windows, the log file gets set to "~/.unpackerr/unpackerr.log" + log_files=0 turns off auto-rotation. + Default files is 10 and size(mb) is 10 Megabytes; both doubled if debug is true. + - name: log_files + envvar: LOG_FILES + default: 10 + short: Log files to keep after rotating. `0` disables rotation + - name: log_file_mb + envvar: LOG_FILE_MB + default: 10 + short: Max size of log files in megabytes + - name: interval + envvar: INTERVAL + default: 2m + short: How often apps are polled, recommend `1m` to `5m` + desc: | + How often to poll starr apps (sonarr, radarr, etc). + Recommend 1m-5m. Uses Go Duration. + - name: start_delay + envvar: START_DELAY + default: 1m + short: Files are queued at least this long before extraction. + desc: | + How long an item must be queued (download complete) before extraction will start. + One minute is the historic default and works well. Set higher if your downloads + take longer to finalize (or transfer locally). Uses Go Duration. + - name: retry_delay + envvar: RETRY_DELAY + default: 5m + short: Failed extractions are retried after at least this long. + desc: | + How long to wait before removing the history for a failed extraction. + Once the history is deleted the item will be recognized as new and + extraction will start again. Uses Go Duration. + - name: max_retries + envvar: MAX_RETRIES + default: 3 + short: Failed extractions are retried after at least this long. + desc: | + How many times to retry a failed extraction. Pauses retry_delay between attempts. + - name: parallel + envvar: PARALLEL + default: 1 + short: Times to retry failed extractions. `0` = unlimited. + desc: | + How many files may be extracted in parallel. 1 works fine. + Do not wrap the number in quotes. Raise this only if you have fast disks and CPU. + - name: file_mode + envvar: FILE_MODE + default: '0644' + short: Extracted files are written with this mode. + desc: | + Use these configurations to control the file modes used for newly extracted + files and folders. Recommend 0644/0755 or 0666/0777. + - name: dir_mode + envvar: DIR_MODE + default: '0755' + short: Extracted folders are written with this mode + + webserver: + envvar_prefix: WEBSERVER_ + params: + - name: metrics + envvar: METRICS + default: false + short: Extracted folders are written with this mode + desc: The web server currently only supports metrics; set this to true if you wish to use it. + - name: listen_addr + envvar: LISTEN_ADDR + default: 0.0.0.0:5656 + short: ip:port to listen on; `0.0.0.0` is all IPs. + desc: This may be set to a port or an ip:port to bind a specific IP. 0.0.0.0 binds ALL IPs. + - name: log_file + envvar: LOG_FILE + default: '' + short: Provide optional file path to write HTTP logs. + desc: Recommend setting a log file for HTTP requests. Otherwise, they go with other logs. + - name: log_files + envvar: LOG_FILES + default: 10 + short: Log files to keep after rotating. `0` to disable. + desc: This app automatically rotates logs. Set these to the size and number to keep. + - name: log_file_mb + envvar: LOG_FILE_MB + default: 10 + short: Max size of HTTP log files in megabytes + - name: ssl_cert_file + envvar: SSL_CERT_FILE + default: '' + short: Path to SSL cert file to serve HTTPS. + desc: Set both of these to valid file paths to enable HTTPS/TLS. + - name: ssl_key_file + envvar: SSL_KEY_FILE + default: '' + short: Path to SSL key file to serve HTTPS. + - name: urlbase + envvar: URLBASE + default: / + short: Base URL path to serve HTTP content. + desc: Base URL from which to serve content. + - name: upstreams + envvar: UPSTREAMS + default: [] + kind: conlist + short: List of upstream proxy CIDRs or IPs to trust. + desc: | + Upstreams should be set to the IP or CIDR of your trusted upstream proxy. + Setting this correctly allows X-Forwarded-For to be used in logs. + In the future it may control auth proxy trust. Must be a list of strings. + example: upstreams = [ "127.0.0.1/32", "10.1.2.0/24" ] + + starr_header: + no_header: true + text: | + ############################################################################### + ##-IMPORTANT-#######-READ THIS!!!-################ Seriously, read this. ###### + ############################################################################### + ## The following sections can be repeated if you have more than one Sonarr, ## + ## Radarr, Lidarr, Readarr, Whisparr, Folder, Webhook, and/or Command Hook. ## + ## You MUST uncomment the [[header]], url and api_key at for any Starr app. ## + ## The [[sonarr]] and [[radarr]] headers come uncommented. Uncomment the url ## + ## and api_key if they are in use. Comment them with a hash if they are not. ## + ## Uncomment the [[lidarr]] and/or [[readarr]] headers and values if in use. ## + ############################################################################### + ############################################################################### + ## ALL LINES BEGINNING WITH A HASH # ARE IGNORED COMMENTS ## + ## REMOVE THE HASH # FROM CONFIG LINES YOU WANT TO CHANGE ## + ############################################################################### + ############################################################################### + + starr: + envvar_prefix: '$APP_' # $APP must be replaced by parser. + kind: list + params: + - name: url + envvar: URL + short: URL where this starr app can be accessed. + - name: api_key + envvar: API_KEY + default: '' + example: 0123456789abcdef0123456789abcdef + short: Provide URL and API key if you use this app. + - name: paths + envvar: PATHS_ + default: ['/downloads'] + kind: list + short: File system path where downloaded items are located. + desc: | + List of paths where content is downloaded for this app. + Used as fallback if the path the Starr app reports does not exist or is not accessible. + - name: protocols + envvar: PROTOCOLS + default: torrent + short: 'Protocols to process. Alt: `torrent,usenet`' + desc: 'Default protocols is torrent. Alternative: torrent,usenet' + - name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for the app to respond. + desc: How long to wait for a reply from the backend. + - name: delete_delay + envvar: DELETE_DELAY + default: false + short: Extracts are deleted this long after import, `-1s` to disable. + desc: How long to wait after import before deleting the extracted items. + - name: delete_orig + envvar: DELETE_ORIG + default: false + short: Delete archives after import? Recommend keeping this false. + desc: | + If you use this app with NZB you may wish to delete archives after extraction. + General recommendation is: do not enable this for torrent use. + Setting this to true deletes the entire original download folder after import. + - name: syncthing + envvar: SYNCTHING + default: false + short: Setting this to true makes unpackerr wait for syncthing to finish. + desc: If you use Syncthing, setting this to true will make unpackerr wait for syncs to finish. + + # Global folder configuration. + folders: + text: | + ## Global Folder configuration that affects all watched folders. + envvar_prefix: FOLDERS_ + params: + - name: interval + envvar: INTERVAL + default: 0s + short: How often poller checks for new folders. Use `1ms` to disable it. + desc: | + How often poller checks for new folders. + The default of `0s` will disable the poller on all systems except Docker. + Set this value to `1ms` to disable it in Docker. + - name: buffer + envvar: BUFFER + default: 20000 + short: How many new folder events can be immediately queued. + desc: How many new folder events can be immediately queued. + + # Per-folder configuration (list). + folder: + text: | + ################################################################################## + ### ### STOP HERE ### STOP HERE ### STOP HERE ### STOP HERE #### STOP HERE ### # + ### Only using Starr apps? The things above. The below configs are OPTIONAL. ### # + ################################################################################## + + + ##-Folders-####################################################################### + ## This application can also watch folders for things to extract. If you copy a ## + ## subfolder into a watched folder (defined below) any extractable items in the ## + ## folder will be decompressed. This has nothing to do with Starr applications. ## + ################################################################################## + envvar_prefix: FOLDER_ + kind: list + params: + - name: path + envvar: PATH + default: '' + example: /some/folder/to/watch + short: Folder to watch for archives. **Not for Starr apps.** + - name: extract_path + envvar: EXTRACT_PATH + default: '' + short: Where to extract to. Default is the same as `path`. + desc: Path to extract files to. The default (leaving this blank) is the same as `path` (above). + - name: delete_after + envvar: DELETE_AFTER + default: 10m + short: Delete extracted files and/or archives after this duration; `0` disables. + desc: | + Delete extracted or original files this long after extraction. + The default is 0. Set to 0 to disable all deletes. Uncomment it to enable deletes. Uses Go Duration. + - name: disable_recursion + envvar: DISABLE_RECURSION + default: false + short: Setting this to true disables extracting archives inside archives. + desc: Unpackerr extracts archives inside archives. Set this to true to disable recursive extractions. + - name: delete_files + envvar: DELETE_FILES + default: false + short: Delete extracted files after successful extraction. + desc: Delete extracted files after successful extraction? Honors delete_after. + - name: delete_original + envvar: DELETE_ORIGINAL + default: false + short: Delete archives after successful extraction. + desc: Delete original items after successful extraction? true/false, no quotes. Honors delete_after. + - name: disable_log + envvar: DISABLE_LOG + default: false + short: Turns off creation of extraction logs files for this folder. + desc: Disable extraction log (unpackerred.txt) file creation? + - name: move_back + envvar: MOVE_BACK + default: false + short: Move extracted items back into original folder. + desc: Move extracted files into original folder? If false, files go into an _unpackerred folder. + - name: extract_isos + envvar: EXTRACT_ISOS + default: false + short: Setting this to true enables .iso file extraction. + desc: Set this to true if you want this app to extract ISO files with .iso extension. + + webhook: + text: | + ################ + ### Webhooks ### + ################ + # Sends a webhook when an extraction queues, starts, finishes, and/or is deleted. + # Created to integrate with notifiarr.com. + # Also works natively with Discord.com, Telegram.org, and Slack.com webhooks. + # Can possibly be used with other services by providing a custom template_path. + ###### Don't forget to uncomment [[webhook]] and url at a minimum !!!! + envvar_prefix: WEBHOOK_ + kind: list + params: + - name: url + envvar: URL + default: '' + example: https://notifiarr.com/api/v1/notification/unpackerr/api_key_from_notifiarr_com + short: URL to send POST webhook to. + - name: name + envvar: NAME + default: '' + short: Provide an optional name to hide the URL in logs. + desc: Provide an optional name to hide the URL in logs. + - name: silent + envvar: SILENT + default: false + short: Hide successful POSTs from logs. + desc: Do not log success (less log spam). + - name: events + envvar: EVENTS_ + default: [0] + kind: list + short: List of event ids to send notification for, `0` == all. + desc: List of event ids to send notification for, 0 == all. + - name: nickname + envvar: NICKNAME + default: '' + short: Passed into templates for telegram, discord and slack hooks. + desc: | + ### Advanced Optional Webhook Configuration + Used in Discord and Slack templates as bot name, in Telegram as chat_id. + - name: channel + envvar: CHANNEL + default: '' + short: Passed into templates for slack.com webhooks. + desc: Also passed into templates. Used in Slack templates for destination channel. + - name: exclude + envvar: EXCLUDE_ + default: [] + kind: list + short: 'List of apps to exclude: radarr, sonarr, folders, etc.' + desc: List of apps to exclude, ie. ["radarr", "lidarr"] + - name: template_path + envvar: TEMPLATE_PATH + default: '' + short: Instead of an internal template, provide your own. + desc: Override internal webhook template for discord.com or other hooks. + - name: template + envvar: TEMPLATE + default: '' + short: Instead of auto template selection, force a built-in template. + desc: 'Override automatic template detection. Values: notifiarr, discord, telegram, gotify, pushover, slack' + - name: ignore_ssl + envvar: IGNORE_SSL + default: false + short: Ignore invalid SSL certificates. + desc: Set this to true to ignore the SSL certificate on the server. + - name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for server response. + desc: You can adjust how long to wait for a server response. + - name: content_type + envvar: CONTENT_TYPE + default: application/json + short: Content-Type header sent to webhook. + desc: If your custom template uses another MIME type, set this. + + cmdhook: + text: | + ##################### + ### Command Hooks ### + ##################### + # Executes a script or command when an extraction queues, starts, finishes, and/or is deleted. + # All data is passed in as environment variables. Try /usr/bin/env to see what variables are available. + ###### Don't forget to uncomment [[cmdhook]] at a minimum !!!! + envvar_prefix: CMDHOOK_ + kind: list + params: + - name: command + envvar: COMMAND + default: '' + example: /my/cool/app + short: Command to run. + - name: name + envvar: NAME + default: '' + short: Provide an optional name to hide the URL in logs. + desc: Provide an optional name to hide the URL in logs. + - name: shell + envvar: SHELL + default: false + short: Run command inside a shell. + desc: Runs the command inside /bin/sh ('nix) or cmd.exe (Windows). + - name: silent + envvar: SILENT + default: false + short: Hide command output from logs. + desc: Do not log command's output. + - name: events + envvar: EVENTS_ + default: [0] + kind: list + short: List of event ids to run command for, `0` == all. + desc: List of event ids to run command for, 0 == all. + - name: exclude + envvar: EXCLUDE_ + default: [] + kind: list + short: 'List of apps to exclude: radarr, sonarr, folders, etc.' + desc: | + ### Optional Command Hook Configuration + List of apps to exclude, ie. ["radarr", "lidarr"] + - name: timeout + envvar: TIMEOUT + default: 10s + short: How long to wait for command to run. + desc: You can adjust how long to wait for command to run. From e35f5d1f0d38350836adac6d713eebdc39166e3c Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 14 Jul 2024 18:43:17 -0700 Subject: [PATCH 4/8] lint --- .golangci.yml | 13 +++- init/config/compose-builder.go | 115 +++++++++++++++++++++++++++++++++ init/config/conf-builder.go | 52 +-------------- init/config/conf-builder.yml | 6 +- init/config/docs-builder.go | 7 ++ init/config/main.go | 71 ++++++++++++++++++++ 6 files changed, 211 insertions(+), 53 deletions(-) create mode 100644 init/config/compose-builder.go create mode 100644 init/config/docs-builder.go create mode 100644 init/config/main.go diff --git a/.golangci.yml b/.golangci.yml index a0511f4..4e0b502 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,14 +12,20 @@ linters: - tagliatelle - cyclop - testpackage -run: - timeout: 3m issues: max-issues-per-linter: 0 max-same-issues: 0 + exclude-rules: + # Exclude some linters from testing files. + - linters: + - forbidigo + - lll + path: 'init/.*' output: sort-results: true +run: + timeout: 3m linters-settings: ireturn: @@ -41,4 +47,5 @@ linters-settings: - github.com/radovskyb/watcher - github.com/prometheus/client_golang/ - github.com/spf13/pflag - - github.com/julienschmidt/httprouter \ No newline at end of file + - github.com/julienschmidt/httprouter + - github.com/BurntSushi/toml \ No newline at end of file diff --git a/init/config/compose-builder.go b/init/config/compose-builder.go new file mode 100644 index 0000000..81df512 --- /dev/null +++ b/init/config/compose-builder.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "strings" +) + +const ( + space = " " + composeHeader = `### Unpackerr docker-compose.yml Example +### Please read this page for help using this example: +### https://unpackerr.zip/docs/install/compose +### Generator: https://notifiarr.com/unpackerr +################################################################## +services: + + unpackerr: + image: golift/unpackerr + container_name: unpackerr + volumes: + # You need at least this one volume mapped so Unpackerr can find your files to extract. + # Make sure this matches your Starr apps; the folder mount (/downloads or /data) should be identical. + - /mnt/HostDownloads:/downloads + restart: always + # Get the user:group correct so unpackerr can read and write to your files. + user: ${PUID}:${PGID} + #user: 1000:100 + # What you see below are defaults for this compose. You only need to modify things specific to your environment. + # Remove apps and feature configs you do not use or need. + # ie. Remove all lines that begin with UN_CMDHOOK, UN_WEBHOOK, UN_FOLDER, UN_WEBSERVER, and other apps you do not use. + environment: + - TZ=${TZ}` +) + +func printCompose(config *Config) { + fmt.Println(composeHeader) + + // Loop the 'Order' list. + for _, section := range config.Order { + // If Order contains a missing section, panic. + if config.Sections[section] == nil { + panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.") + } + + if config.Defs[section] == nil { + config.Sections[section].printCompose(strings.Title(section), config.Prefix) //nolint:staticcheck + } else { + config.Sections[section].printComposeDefined(config.Prefix, config.Defs[section]) + } + } +} + +func (h *Header) printCompose(title, prefix string) { + if len(h.Params) > 0 { + fmt.Println(space, "##", title) + } + + for _, param := range h.Params { + if h.Kind == list { + fmt.Print(param.Compose(prefix + h.Prefix + "0_")) + } else { + fmt.Print(param.Compose(prefix + h.Prefix)) + } + } +} + +func (h *Header) printComposeDefined(prefix string, defs Defs) { + for section, def := range defs { + // Loop each defined section Defaults, and see if one of the param names match. + for overrideName, override := range def.Defaults { + for _, defined := range h.Params { + // If the name of the default (override) matches this param name, overwrite the value. + if defined.Name == overrideName { + defined.Default = override + } + } + } + + // Make a brand new section and print it. + (&Header{ + Text: def.Text, + Prefix: def.Prefix, + Params: h.Params, + Kind: h.Kind, + }).printCompose(strings.Title(section), prefix) //nolint:staticcheck + } +} + +func (p *Param) Compose(prefix string) string { + val := p.Default + if p.Example != nil { + val = p.Example + } + + switch p.Kind { + default: + return fmt.Sprint(space, " - ", prefix, p.EnvVar, "=", val, "\n") + case list: + var out string + + for idx, sv := range val.([]any) { //nolint:forcetypeassert + out += fmt.Sprint(space, " - ", prefix, p.EnvVar, idx, "=", sv, "\n") + } + + return out + case "conlist": + out := []string{} + + for _, sv := range val.([]any) { //nolint:forcetypeassert + out = append(out, fmt.Sprint(sv)) + } + + return fmt.Sprint(space, " - ", prefix, p.EnvVar, "=", strings.Join(out, ","), "\n") + } +} diff --git a/init/config/conf-builder.go b/init/config/conf-builder.go index ebf0f00..13554ff 100644 --- a/init/config/conf-builder.go +++ b/init/config/conf-builder.go @@ -3,58 +3,12 @@ package main import ( "bytes" "fmt" - "os" "strings" "github.com/BurntSushi/toml" - "gopkg.in/yaml.v3" ) -type Param struct { - Name string `yaml:"name"` - EnvVar string `yaml:"envvar"` - Default any `yaml:"default"` - Example any `yaml:"example"` - Short string `yaml:"short"` - Desc string `yaml:"desc"` - Kind string `yaml:"kind"` // "", list, conlist -} - -type Header struct { - Text string `yaml:"text"` - Prefix string `yaml:"envvar_prefix"` - Params []*Param `yaml:"params"` - Kind string `yaml:"kind"` // "", list - NoHeader bool `yaml:"no_header"` // Do not print [section] header. -} - -type Def struct { - Comment bool `yaml:"comment"` // just the header. - Text string `yaml:"text"` - Defaults map[string]any `yaml:"defaults"` -} - -type Defs map[string]*Def - -type ConfigFile struct { - Defs map[string]Defs `yaml:"defs"` - Prefix string `yaml:"envvar_prefix"` - Order []string `yaml:"order"` - Sections map[string]*Header `yaml:"sections"` -} - -func main() { - file, err := os.Open("./conf-builder.yml") - if err != nil { - panic(err) - } - - config := &ConfigFile{} - // Decode conf-builder file into Go data structure. - if err = yaml.NewDecoder(file).Decode(config); err != nil { - panic(err) - } - +func printConfFile(config *Config) { // Loop the 'Order' list. for _, section := range config.Order { // If Order contains a missing section, panic. @@ -84,7 +38,7 @@ func printSection(name string, section *Header, noComment bool) { } if !section.NoHeader { // Print the [section] or [[section]] header. - if section.Kind == "list" { // list sections are commented by default. + if section.Kind == list { // list sections are commented by default. fmt.Println(comment + "[[" + name + "]]") // list sections use double-brackets. } else { fmt.Println("[" + name + "]") // non-list sections use single brackets. @@ -108,7 +62,7 @@ func printSection(name string, section *Header, noComment bool) { comment = "#" } - if section.Kind == "list" { + if section.Kind == list { // If the 'kind' is a 'list', we comment all the parameters. fmt.Printf("# %s = %s\n", param.Name, param.Value()) } else { diff --git a/init/config/conf-builder.yml b/init/config/conf-builder.yml index 9ede0eb..0027279 100644 --- a/init/config/conf-builder.yml +++ b/init/config/conf-builder.yml @@ -16,26 +16,31 @@ order: defs: starr: sonarr: + prefix: SONARR_ text: | ## Leaving the [[sonarr]] header uncommented (no leading hash #) without also ## uncommenting the api_key (remove the hash #) will produce a startup warning. defaults: url: http://127.0.0.1:8989 radarr: + prefix: RADARR_ text: | ## Leaving the [[radarr]] header uncommented (no leading hash #) without also ## uncommenting the api_key (remove the hash #) will produce a startup warning. defaults: url: http://127.0.0.1:7878 lidarr: + prefix: LIDARR_ comment: true defaults: url: http://127.0.0.1:8686 readarr: + prefix: READARR_ comment: true defaults: url: http://127.0.0.1:8787 whisparr: + prefix: WHISPARR_ comment: true defaults: url: http://127.0.0.1:6969 @@ -231,7 +236,6 @@ sections: ############################################################################### starr: - envvar_prefix: '$APP_' # $APP must be replaced by parser. kind: list params: - name: url diff --git a/init/config/docs-builder.go b/init/config/docs-builder.go new file mode 100644 index 0000000..3916d37 --- /dev/null +++ b/init/config/docs-builder.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func printDocusaurus(_ *Config) { + fmt.Println("not yet") +} diff --git a/init/config/main.go b/init/config/main.go new file mode 100644 index 0000000..b8675ea --- /dev/null +++ b/init/config/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +const ( + list = "list" +) + +type Config struct { + Defs map[string]Defs `yaml:"defs"` + Prefix string `yaml:"envvar_prefix"` + Order []string `yaml:"order"` + Sections map[string]*Header `yaml:"sections"` +} + +type Header struct { + Text string `yaml:"text"` + Prefix string `yaml:"envvar_prefix"` + Params []*Param `yaml:"params"` + Kind string `yaml:"kind"` // "", list + NoHeader bool `yaml:"no_header"` // Do not print [section] header. +} + +type Param struct { + Name string `yaml:"name"` + EnvVar string `yaml:"envvar"` + Default any `yaml:"default"` + Example any `yaml:"example"` + Short string `yaml:"short"` + Desc string `yaml:"desc"` + Kind string `yaml:"kind"` // "", list, conlist +} + +type Def struct { + Comment bool `yaml:"comment"` // just the header. + Prefix string `yaml:"prefix"` + Text string `yaml:"text"` + Defaults map[string]any `yaml:"defaults"` +} + +type Defs map[string]*Def + +func main() { + file, err := os.Open("./conf-builder.yml") + if err != nil { + panic(err) + } + + config := &Config{} + // Decode conf-builder file into Go data structure. + if err = yaml.NewDecoder(file).Decode(config); err != nil { + panic(err) + } + + switch { + default: + fallthrough + case len(os.Args) <= 1: + fallthrough + case os.Args[1] == "conf": + printConfFile(config) + case os.Args[1] == "compose", os.Args[1] == "docker": + printCompose(config) + case os.Args[1] == "docs": + printDocusaurus(config) + } +} From f124d2f5784cf2a88efa841c2ff27daa25cb0a00 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Jul 2024 02:24:05 -0700 Subject: [PATCH 5/8] add config generator --- .golangci.yml | 4 +- init/config/compose-builder.go | 56 ++--- init/config/conf-builder.go | 81 ++++---- init/config/conf-builder.yml | 364 ++++++++++++++++++++++++++++++--- init/config/docs-builder.go | 146 ++++++++++++- init/config/main.go | 60 ++++-- 6 files changed, 595 insertions(+), 116 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 4e0b502..02b94fa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,7 +21,7 @@ issues: - linters: - forbidigo - lll - path: 'init/.*' + path: 'init/config/.*.\.go' output: sort-results: true run: @@ -48,4 +48,4 @@ linters-settings: - github.com/prometheus/client_golang/ - github.com/spf13/pflag - github.com/julienschmidt/httprouter - - github.com/BurntSushi/toml \ No newline at end of file + - github.com/BurntSushi/toml diff --git a/init/config/compose-builder.go b/init/config/compose-builder.go index 81df512..f0c258d 100644 --- a/init/config/compose-builder.go +++ b/init/config/compose-builder.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "fmt" "strings" ) @@ -43,47 +44,48 @@ func printCompose(config *Config) { } if config.Defs[section] == nil { - config.Sections[section].printCompose(strings.Title(section), config.Prefix) //nolint:staticcheck + fmt.Print(config.Sections[section].makeCompose(config.Sections[section].Title, config.Prefix, false)) } else { - config.Sections[section].printComposeDefined(config.Prefix, config.Defs[section]) + fmt.Print(config.Sections[section].makeComposeDefined(config.Prefix, config.Defs[section], config.DefOrder[section], false)) } } } -func (h *Header) printCompose(title, prefix string) { - if len(h.Params) > 0 { - fmt.Println(space, "##", title) +func (h *Header) makeCompose(title, prefix string, bare bool) string { + var buf bytes.Buffer + + if len(h.Params) > 0 && bare { + buf.WriteString("## " + title + "\n") + } else if len(h.Params) > 0 { + buf.WriteString(space + " ## " + title + "\n") + } + + pfx := space + " - " + if bare { + pfx = "" } for _, param := range h.Params { if h.Kind == list { - fmt.Print(param.Compose(prefix + h.Prefix + "0_")) + buf.WriteString(param.Compose(pfx + prefix + h.Prefix + "0_")) } else { - fmt.Print(param.Compose(prefix + h.Prefix)) + buf.WriteString(param.Compose(pfx + prefix + h.Prefix)) } } + + return buf.String() } -func (h *Header) printComposeDefined(prefix string, defs Defs) { - for section, def := range defs { - // Loop each defined section Defaults, and see if one of the param names match. - for overrideName, override := range def.Defaults { - for _, defined := range h.Params { - // If the name of the default (override) matches this param name, overwrite the value. - if defined.Name == overrideName { - defined.Default = override - } - } - } +func (h *Header) makeComposeDefined(prefix string, defs Defs, order []section, bare bool) string { + var buf bytes.Buffer + for _, section := range order { + newHeader := createDefinedSection(defs[section], h) // Make a brand new section and print it. - (&Header{ - Text: def.Text, - Prefix: def.Prefix, - Params: h.Params, - Kind: h.Kind, - }).printCompose(strings.Title(section), prefix) //nolint:staticcheck + buf.WriteString(newHeader.makeCompose(h.Title, prefix, bare)) } + + return buf.String() } func (p *Param) Compose(prefix string) string { @@ -94,12 +96,12 @@ func (p *Param) Compose(prefix string) string { switch p.Kind { default: - return fmt.Sprint(space, " - ", prefix, p.EnvVar, "=", val, "\n") + return fmt.Sprint(prefix, p.EnvVar, "=", val, "\n") case list: var out string for idx, sv := range val.([]any) { //nolint:forcetypeassert - out += fmt.Sprint(space, " - ", prefix, p.EnvVar, idx, "=", sv, "\n") + out += fmt.Sprint(prefix, p.EnvVar, idx, "=", sv, "\n") } return out @@ -110,6 +112,6 @@ func (p *Param) Compose(prefix string) string { out = append(out, fmt.Sprint(sv)) } - return fmt.Sprint(space, " - ", prefix, p.EnvVar, "=", strings.Join(out, ","), "\n") + return fmt.Sprint(prefix, p.EnvVar, "=", strings.Join(out, ","), "\n") } } diff --git a/init/config/conf-builder.go b/init/config/conf-builder.go index 13554ff..78e2aa0 100644 --- a/init/config/conf-builder.go +++ b/init/config/conf-builder.go @@ -17,61 +17,66 @@ func printConfFile(config *Config) { } if config.Defs[section] != nil { - printDefinedSection(config.Sections[section], config.Defs[section]) + fmt.Print(config.Sections[section].makeDefinedSection(config.Defs[section], config.DefOrder[section], false)) } else { - printSection(section, config.Sections[section], false) + fmt.Print(config.Sections[section].makeSection(section, false, false)) } } } // Not all sections have defs, and it may be nil. Defs only work on 'list' sections. -func printSection(name string, section *Header, noComment bool) { +func (h *Header) makeSection(name section, showHeader, showValue bool) string { + var buf bytes.Buffer + // Print section header text. - if section.Text != "" { - fmt.Printf("%s", section.Text) + if h.Text != "" { + buf.WriteString(h.Text) } comment := "#" - if noComment { + if showHeader { // this only happens when a defined section has a comment override on the repeating headers. comment = "" } - if !section.NoHeader { // Print the [section] or [[section]] header. - if section.Kind == list { // list sections are commented by default. - fmt.Println(comment + "[[" + name + "]]") // list sections use double-brackets. + if !h.NoHeader { // Print the [section] or [[section]] header. + if h.Kind == list { // list sections are commented by default. + buf.WriteString(comment + "[[" + string(name) + "]]" + "\n") // list sections use double-brackets. } else { - fmt.Println("[" + name + "]") // non-list sections use single brackets. + buf.WriteString("[" + string(name) + "]" + "\n") // non-list sections use single brackets. } } - for _, param := range section.Params { + for _, param := range h.Params { // Print an empty newline for each param if the section has no header and the param has a description. - if section.NoHeader && param.Desc != "" { - fmt.Println() + if h.NoHeader && param.Desc != "" { + buf.WriteString("\n") } // Add ## to the beginning of each line in the description. // Uses the newline \n character to figure out where each line begins. if param.Desc != "" { - fmt.Println("##", strings.ReplaceAll(strings.TrimSpace(param.Desc), "\n", "\n## ")) - } - - // If example is not empty, use that commented out, otherwise use the default. - if comment = ""; param.Example != nil { - comment = "#" + buf.WriteString("## " + strings.ReplaceAll(strings.TrimSpace(param.Desc), "\n", "\n## ") + "\n") } - if section.Kind == list { + switch { + default: + fallthrough + case showValue: + buf.WriteString(fmt.Sprintf("%s = %s\n", param.Name, param.Value())) + case param.Example != nil: + // If example is not empty, use that commented out, otherwise use the default. + fallthrough + case h.Kind == list: // If the 'kind' is a 'list', we comment all the parameters. - fmt.Printf("# %s = %s\n", param.Name, param.Value()) - } else { - fmt.Printf("%s%s = %s\n", comment, param.Name, param.Value()) + buf.WriteString(fmt.Sprintf("#%s = %s\n", param.Name, param.Value())) } } // Each section needs a newline at the end. - fmt.Println() + buf.WriteString("\n") + + return buf.String() } func (p *Param) Value() string { @@ -89,26 +94,16 @@ func (p *Param) Value() string { return string(out) } -// printDefinedSection duplicates sections from overrides, and prints it once for each override. -func printDefinedSection(section *Header, defs Defs) { - for name, def := range defs { - // Loop each defined section Defaults, and see if one of the param names match. - for overrideName, override := range def.Defaults { - for _, defined := range section.Params { - // If the name of the default (override) matches this param name, overwrite the value. - if defined.Name == overrideName { - defined.Default = override - } - } - } +// makeDefinedSection duplicates sections from overrides, and prints it once for each override. +func (h *Header) makeDefinedSection(defs Defs, order []section, showValue bool) string { + var buf bytes.Buffer + for _, section := range order { + newHeader := createDefinedSection(defs[section], h) // Make a brand new section and pass it back in. - printSection(name, &Header{ - Text: def.Text, - Prefix: section.Prefix, - Params: section.Params, - Kind: section.Kind, - NoHeader: false, - }, !def.Comment) // Only defined sections can comment the header. + // Only defined sections can comment the header. + buf.WriteString(newHeader.makeSection(section, !defs[section].Comment, showValue)) } + + return buf.String() } diff --git a/init/config/conf-builder.yml b/init/config/conf-builder.yml index 0027279..d5a34b2 100644 --- a/init/config/conf-builder.yml +++ b/init/config/conf-builder.yml @@ -2,7 +2,6 @@ --- envvar_prefix: UN_ - order: - global - webserver @@ -12,10 +11,146 @@ order: - folder - webhook - cmdhook +def_order: + starr: + - sonarr + - radarr + - lidarr + - readarr + - whisparr +recommendations: + apps: &APPS + - name: Sonarr + value: sonarr + - name: Radarr + value: radarr + - name: Lidarr + value: lidarr + - name: Readarr + value: readarr + - name: Whisparr + value: whisparr + - name: Folder + value: folder + on_off: &BOOLEAN + - name: Yes + value: true + - name: No + value: false + event_ids: &EVENT_IDS + - name: All Events + value: 0 + - name: Queues + value: 1 + - name: Extracting + value: 2 + - name: Extract Failed + value: 3 + - name: Extracted + value: 4 + - name: Imported + value: 5 + - name: Deleting + value: 6 + - name: Delete Failed + value: 7 + - name: Deleted + value: 8 + - name: Nothing Extracted + value: 9 + global: &GLOBAL_INTERVALS + - name: 1 minute + value: 1m + - name: 2 minutes + value: 2m + - name: 3 minutes + value: 3m + - name: 4 minutes + value: 4m + - name: 5 minutes + value: 5m + - name: 10 minutes + value: 10m + - name: 15m minutes + value: 15m + - name: 20 minutes + value: 20m + timeout: &TIMEOUTS + - name: 10 seconds + value: 10s + - name: 15 seconds + value: 15s + - name: 20 seconds + value: 20s + - name: 30 seconds + value: 30s + - name: 45 seconds + value: 45s + - name: 1 minute + value: 1m + - name: 1.5 minutes + value: 90s + - name: 2 minutes + value: 2m + - name: 3 minutes + value: 3m + - name: 5 minutes + value: 5m + queues: &QUEUE_INTERVALS + - name: 1 minute + value: 1m + - name: 2 minutes + value: 2m + - name: 3 minutes + value: 3m + - name: 4 minutes + value: 4m + - name: 5 minutes + value: 5m + - name: 10 minutes + value: 10m + - name: 15m minutes + value: 15m + - name: 20 minutes + value: 20m + - name: 2 hours + value: 2h + - name: 6 hours + value: 6h + - name: 12 hours + value: 12h + - name: 24 hours + value: 24h + folders: &FOLDER_INTERVALS + - name: Disabled + value: 1ms + - name: 1/2 second + value: 500ms + - name: 1 second + value: 1s + - name: 2 seconds + value: 2s + - name: 3 seconds + value: 3s + - name: 5 seconds + value: 5s + - name: 8 seconds + value: 8s + - name: 10 seconds + value: 10s + - name: 15 seconds + value: 15s + - name: 30 seconds + value: 30s + - name: 1 minute + value: 1m + - name: 2 minutes + value: 2m defs: starr: sonarr: + title: Sonarr Settings prefix: SONARR_ text: | ## Leaving the [[sonarr]] header uncommented (no leading hash #) without also @@ -23,6 +158,7 @@ defs: defaults: url: http://127.0.0.1:8989 radarr: + title: Radarr Settings prefix: RADARR_ text: | ## Leaving the [[radarr]] header uncommented (no leading hash #) without also @@ -30,16 +166,19 @@ defs: defaults: url: http://127.0.0.1:7878 lidarr: + title: Lidarr Settings prefix: LIDARR_ comment: true defaults: url: http://127.0.0.1:8686 readarr: + title: Readarr Settings prefix: READARR_ comment: true defaults: url: http://127.0.0.1:8787 whisparr: + title: Whisparr Settings prefix: WHISPARR_ comment: true defaults: @@ -48,6 +187,7 @@ defs: sections: global: + title: "Global Settings" no_header: true text: | ####################################################### @@ -58,17 +198,23 @@ sections: ## More configuration help: https://unpackerr.zip ## ## Config Generator: https://notifiarr.com/unpackerr ## ####################################################### + docs: | + These values must exist at the top of the config file. + If you put them anywhere else they may be attached to a `[header]` inadvertently. + When using environment variables, you can simply omit the ones you don't set or change from default. params: - name: debug envvar: DEBUG default: false + recommend: *BOOLEAN short: Turns on more logs. desc: | - [true/false] Turn on debug messages in the output. Do not wrap this in quotes. + Turn on debug messages in the output. Do not wrap this in quotes. Recommend trying this so you know what it looks like. I personally leave it on. - name: quiet envvar: QUIET default: false + recommend: *BOOLEAN short: Do not print logs to stdout or stderr. desc: | Disable writing messages to stdout/stderr. This silences the app. Set a log @@ -76,6 +222,7 @@ sections: - name: error_stderr envvar: ERROR_STDERR default: false + recommend: *BOOLEAN short: Print ERROR lines to stderr instead of stdout. desc: | Send error output to stderr instead of stdout by setting error_stderr to true. @@ -83,6 +230,7 @@ sections: - name: activity envvar: ACTIVITY default: false + recommend: *BOOLEAN short: Setting true will print only queue counts with activity. desc: | Setting activity to true will silence all app queue log lines with only zeros. @@ -91,6 +239,7 @@ sections: envvar: LOG_QUEUES default: "1m" short: How often to print internal counters. Uses Go Duration. + recommend: *QUEUE_INTERVALS desc: | The Starr-application activity queue is logged on an interval. Adjust that interval with this setting. @@ -111,21 +260,32 @@ sections: - name: log_files envvar: LOG_FILES default: 10 + recommend: &NUMBERS + - value: 1 + - value: 2 + - value: 3 + - value: 5 + - value: 10 + - value: 15 + - value: 20 short: Log files to keep after rotating. `0` disables rotation - name: log_file_mb envvar: LOG_FILE_MB default: 10 + recommend: *NUMBERS short: Max size of log files in megabytes - name: interval envvar: INTERVAL default: 2m - short: How often apps are polled, recommend `1m` to `5m` + recommend: *GLOBAL_INTERVALS + short: How often apps are polled, recommend `1m` to `5m`. desc: | How often to poll starr apps (sonarr, radarr, etc). Recommend 1m-5m. Uses Go Duration. - name: start_delay envvar: START_DELAY default: 1m + recommend: *GLOBAL_INTERVALS short: Files are queued at least this long before extraction. desc: | How long an item must be queued (download complete) before extraction will start. @@ -134,6 +294,7 @@ sections: - name: retry_delay envvar: RETRY_DELAY default: 5m + recommend: *GLOBAL_INTERVALS short: Failed extractions are retried after at least this long. desc: | How long to wait before removing the history for a failed extraction. @@ -142,12 +303,14 @@ sections: - name: max_retries envvar: MAX_RETRIES default: 3 + recommend: *NUMBERS short: Failed extractions are retried after at least this long. desc: | How many times to retry a failed extraction. Pauses retry_delay between attempts. - name: parallel envvar: PARALLEL default: 1 + recommend: *NUMBERS short: Times to retry failed extractions. `0` = unlimited. desc: | How many files may be extracted in parallel. 1 works fine. @@ -155,6 +318,12 @@ sections: - name: file_mode envvar: FILE_MODE default: '0644' + recommend: + - value: '0600' + - value: '0640' + - value: '0660' + - value: '0644' + - value: '0640' short: Extracted files are written with this mode. desc: | Use these configurations to control the file modes used for newly extracted @@ -162,14 +331,28 @@ sections: - name: dir_mode envvar: DIR_MODE default: '0755' + recommend: + - value: '0700' + - value: '0750' + - value: '0755' + - value: '0770' + - value: '0775' short: Extracted folders are written with this mode webserver: + title: Web Server + docs: | + :::note Metrics + The web server currently only provides prometheus metrics, which you can display in + [Grafana](https://grafana.com/grafana/dashboards/18817-unpackerr/). + It provides no UI. This may change in the future. The web server was added in v0.12.0. + ::: envvar_prefix: WEBSERVER_ params: - name: metrics envvar: METRICS default: false + recommend: *BOOLEAN short: Extracted folders are written with this mode desc: The web server currently only supports metrics; set this to true if you wish to use it. - name: listen_addr @@ -185,11 +368,13 @@ sections: - name: log_files envvar: LOG_FILES default: 10 + recommend: *NUMBERS short: Log files to keep after rotating. `0` to disable. desc: This app automatically rotates logs. Set these to the size and number to keep. - name: log_file_mb envvar: LOG_FILE_MB default: 10 + recommend: *NUMBERS short: Max size of HTTP log files in megabytes - name: ssl_cert_file envvar: SSL_CERT_FILE @@ -257,21 +442,28 @@ sections: - name: protocols envvar: PROTOCOLS default: torrent + recommend: + - value: torrent + - value: torrent,usenet + - value: usenet short: 'Protocols to process. Alt: `torrent,usenet`' desc: 'Default protocols is torrent. Alternative: torrent,usenet' - name: timeout envvar: TIMEOUT default: 10s + recommend: *TIMEOUTS short: How long to wait for the app to respond. desc: How long to wait for a reply from the backend. - name: delete_delay envvar: DELETE_DELAY - default: false + default: 5m + recommend: *GLOBAL_INTERVALS short: Extracts are deleted this long after import, `-1s` to disable. desc: How long to wait after import before deleting the extracted items. - name: delete_orig envvar: DELETE_ORIG default: false + recommend: *BOOLEAN short: Delete archives after import? Recommend keeping this false. desc: | If you use this app with NZB you may wish to delete archives after extraction. @@ -280,11 +472,13 @@ sections: - name: syncthing envvar: SYNCTHING default: false + recommend: *BOOLEAN short: Setting this to true makes unpackerr wait for syncthing to finish. desc: If you use Syncthing, setting this to true will make unpackerr wait for syncs to finish. # Global folder configuration. folders: + title: Folder Settings text: | ## Global Folder configuration that affects all watched folders. envvar_prefix: FOLDERS_ @@ -292,7 +486,8 @@ sections: - name: interval envvar: INTERVAL default: 0s - short: How often poller checks for new folders. Use `1ms` to disable it. + short: How often poller checks for new folders. Use `1ms` to disable it. + recommend: *FOLDER_INTERVALS desc: | How often poller checks for new folders. The default of `0s` will disable the poller on all systems except Docker. @@ -301,74 +496,86 @@ sections: envvar: BUFFER default: 20000 short: How many new folder events can be immediately queued. - desc: How many new folder events can be immediately queued. + desc: How many new folder events can be immediately queued. Don't change this. # Per-folder configuration (list). folder: + title: Watch Folders text: | ################################################################################## ### ### STOP HERE ### STOP HERE ### STOP HERE ### STOP HERE #### STOP HERE ### # ### Only using Starr apps? The things above. The below configs are OPTIONAL. ### # ################################################################################## - - + + ##-Folders-####################################################################### ## This application can also watch folders for things to extract. If you copy a ## ## subfolder into a watched folder (defined below) any extractable items in the ## ## folder will be decompressed. This has nothing to do with Starr applications. ## ################################################################################## + docs: | + Folders are a way to watch a folder for things to extract. You can use this to + monitor your download client's "move to" path if you're not using it with an Starr app. envvar_prefix: FOLDER_ kind: list params: - name: path envvar: PATH default: '' - example: /some/folder/to/watch + example: /downloads/auto_extract short: Folder to watch for archives. **Not for Starr apps.** - name: extract_path envvar: EXTRACT_PATH default: '' - short: Where to extract to. Default is the same as `path`. + short: Where to extract to. Uses `path` if not set. desc: Path to extract files to. The default (leaving this blank) is the same as `path` (above). - name: delete_after envvar: DELETE_AFTER default: 10m - short: Delete extracted files and/or archives after this duration; `0` disables. + recommend: *GLOBAL_INTERVALS + short: Delete requested files after this duration; `0` disables. desc: | Delete extracted or original files this long after extraction. The default is 0. Set to 0 to disable all deletes. Uncomment it to enable deletes. Uses Go Duration. - name: disable_recursion envvar: DISABLE_RECURSION default: false + recommend: *BOOLEAN short: Setting this to true disables extracting archives inside archives. desc: Unpackerr extracts archives inside archives. Set this to true to disable recursive extractions. - name: delete_files envvar: DELETE_FILES default: false + recommend: *BOOLEAN short: Delete extracted files after successful extraction. desc: Delete extracted files after successful extraction? Honors delete_after. - name: delete_original envvar: DELETE_ORIGINAL default: false + recommend: *BOOLEAN short: Delete archives after successful extraction. - desc: Delete original items after successful extraction? true/false, no quotes. Honors delete_after. + desc: Delete original items after successful extraction? Honors delete_after. - name: disable_log envvar: DISABLE_LOG default: false + recommend: *BOOLEAN short: Turns off creation of extraction logs files for this folder. desc: Disable extraction log (unpackerred.txt) file creation? - name: move_back envvar: MOVE_BACK default: false + recommend: *BOOLEAN short: Move extracted items back into original folder. desc: Move extracted files into original folder? If false, files go into an _unpackerred folder. - name: extract_isos envvar: EXTRACT_ISOS default: false + recommend: *BOOLEAN short: Setting this to true enables .iso file extraction. desc: Set this to true if you want this app to extract ISO files with .iso extension. webhook: + title: Web Hooks text: | ################ ### Webhooks ### @@ -378,6 +585,17 @@ sections: # Also works natively with Discord.com, Telegram.org, and Slack.com webhooks. # Can possibly be used with other services by providing a custom template_path. ###### Don't forget to uncomment [[webhook]] and url at a minimum !!!! + docs: | + This application can send a `POST` webhook to a URL when an extraction begins, and again + when it finishes. Configure 1 or more webhook URLs with the parameters below. + Works great with [notifiarr.com](https://notifiarr.com). You can use + [requestbin.com](https://requestbin.com/r/) to test and _see_ the payload. + notes: | + - _`Nickname` should equal the `chat_id` value in Telegram webhooks._ + - _`Channel` is used as destination channel for Slack. It's not used in others._ + - _`Nickname` and `Channel` may be used as custom values in custom templates._ + - _`Name` is only used in logs, but it's also available as a template value as `{{name}}`._ + - Built-In Templates: `pushover`, `telegram`, `discord`, `notifiarr`, `slack`, `gotify`. envvar_prefix: WEBHOOK_ kind: list params: @@ -390,24 +608,35 @@ sections: envvar: NAME default: '' short: Provide an optional name to hide the URL in logs. - desc: Provide an optional name to hide the URL in logs. + desc: | + Provide an optional name to hide the URL in logs. + If a name is not provided then the URL is used. - name: silent envvar: SILENT default: false + recommend: *BOOLEAN short: Hide successful POSTs from logs. desc: Do not log success (less log spam). - name: events envvar: EVENTS_ - default: [0] + default: + - 0 + example: + - 1 + - 4 + - 6 kind: list - short: List of event ids to send notification for, `0` == all. - desc: List of event ids to send notification for, 0 == all. + recommend: *EVENT_IDS + short: List of event ids to send notification for, `0` for all. + desc: | + List of event ids to send notification for, [0] for all. + The default is [0] and this is an example: - name: nickname envvar: NICKNAME - default: '' + default: 'Unpackerr' short: Passed into templates for telegram, discord and slack hooks. desc: | - ### Advanced Optional Webhook Configuration + ===> Advanced Optional Webhook Configuration <=== Used in Discord and Slack templates as bot name, in Telegram as chat_id. - name: channel envvar: CHANNEL @@ -417,9 +646,11 @@ sections: - name: exclude envvar: EXCLUDE_ default: [] + example: ["readarr", "lidarr"] + recommend: *APPS kind: list short: 'List of apps to exclude: radarr, sonarr, folders, etc.' - desc: List of apps to exclude, ie. ["radarr", "lidarr"] + desc: 'List of apps to exclude. None by default. This is an example:' - name: template_path envvar: TEMPLATE_PATH default: '' @@ -428,25 +659,85 @@ sections: - name: template envvar: TEMPLATE default: '' + recommend: + - value: "notifiarr" + - value: "discord" + - value: "telegram" + - value: "gotify" + - value: "pushover" + - value: "slack" short: Instead of auto template selection, force a built-in template. desc: 'Override automatic template detection. Values: notifiarr, discord, telegram, gotify, pushover, slack' - name: ignore_ssl envvar: IGNORE_SSL default: false + recommend: *BOOLEAN short: Ignore invalid SSL certificates. desc: Set this to true to ignore the SSL certificate on the server. - name: timeout envvar: TIMEOUT default: 10s + recommend: *TIMEOUTS short: How long to wait for server response. desc: You can adjust how long to wait for a server response. - name: content_type envvar: CONTENT_TYPE default: application/json + recommend: + - value: "application/json" + - value: "application/x-yaml" + - value: "application/xml" + - value: "application/x-www-form-urlencoded" short: Content-Type header sent to webhook. desc: If your custom template uses another MIME type, set this. cmdhook: + title: Command Hooks + docs: | + Unpackerr can execute commands (or scripts) before and after an archive extraction. + The only thing required is a command. Name is optional, and used in logs only. + Setting `shell` to `true` executes your command after `/bin/sh -c` or `cmd.exe /c` + on Windows. + tail: | + All extraction data is input to the command using environment variables, see example below. + Extracted files variables names begin with `UN_DATA_FILES_`. + Try `/usr/bin/env` as an example command to see what variables are available. + +
+ Example Output Variables + + ```none + UN_DATA_OUTPUT=folder/subfolder_unpackerred + UN_PATH=folder/subfolder + UN_DATA_START=2021-10-04T23:04:27.849216-07:00 + UN_REVISION= + UN_EVENT=extracted + UN_GO=go1.17 + UN_DATA_ARCHIVES=folder/subfolder_unpackerred/Funjetting.rar,folder/subfolder_unpackerred/Funjetting.r00,folder/subfolder/files.zip + UN_DATA_ARCHIVE_2=folder/subfolder/files.zip + UN_DATA_ARCHIVE_1=folder/subfolder_unpackerred/Funjetting.r00 + UN_DATA_ARCHIVE_0=folder/subfolder_unpackerred/Funjetting.rar + UN_DATA_FILES=folder/subfolder/Funjetting.mp3,folder/subfolder/Funjetting.r00,folder/subfolder/Funjetting.rar,folder/subfolder/_unpackerred.subfolder.txt + UN_DATA_FILE_1=folder/subfolder/Funjetting.r00 + UN_DATA_BYTES=2407624 + PWD=/Users/david/go/src/github.com/Unpackerr/unpackerr + UN_DATA_FILE_0=folder/subfolder/Funjetting.mp3 + UN_OS=darwin + UN_DATA_FILE_3=folder/subfolder/_unpackerred.subfolder.txt + UN_DATA_FILE_2=folder/subfolder/Funjetting.rar + UN_BRANCH= + UN_TIME=2021-10-04T23:04:27.869613-07:00 + UN_VERSION= + UN_DATA_QUEUE=0 + SHLVL=1 + UN_APP=Folder + UN_STARTED=2021-10-04T23:03:22.849253-07:00 + UN_ARCH=amd64 + UN_DATA_ELAPSED=20.365752ms + UN_DATA_ERROR= + ``` + +
text: | ##################### ### Command Hooks ### @@ -460,39 +751,54 @@ sections: - name: command envvar: COMMAND default: '' - example: /my/cool/app + example: /downloads/scripts/command.sh short: Command to run. - name: name envvar: NAME default: '' - short: Provide an optional name to hide the URL in logs. - desc: Provide an optional name to hide the URL in logs. + short: Name for logs, otherwise uses first word in command. + desc: | + Provide an optional name to hide the URL in logs. + If a name is not provided the first word in the command is used. - name: shell envvar: SHELL default: false + recommend: *BOOLEAN short: Run command inside a shell. desc: Runs the command inside /bin/sh ('nix) or cmd.exe (Windows). - name: silent envvar: SILENT default: false + recommend: *BOOLEAN short: Hide command output from logs. desc: Do not log command's output. - name: events envvar: EVENTS_ - default: [0] + default: + - 0 + recommend: *EVENT_IDS + example: + - 1 + - 4 + - 7 kind: list - short: List of event ids to run command for, `0` == all. - desc: List of event ids to run command for, 0 == all. + short: List of event ids to run command for, `0` for all. + desc: | + List of event ids to run command for, [0] for all. + The default is [0] and this is an example: - name: exclude envvar: EXCLUDE_ default: [] + example: ["readarr", "lidarr"] + recommend: *APPS kind: list short: 'List of apps to exclude: radarr, sonarr, folders, etc.' desc: | - ### Optional Command Hook Configuration - List of apps to exclude, ie. ["radarr", "lidarr"] + ===> Optional Command Hook Configuration <=== + List of apps to exclude. None by default. This is an example: - name: timeout envvar: TIMEOUT default: 10s - short: How long to wait for command to run. - desc: You can adjust how long to wait for command to run. + recommend: *TIMEOUTS + short: How long to wait for the command to run. + desc: You can adjust how long to wait for the command to run. diff --git a/init/config/docs-builder.go b/init/config/docs-builder.go index 3916d37..132ad74 100644 --- a/init/config/docs-builder.go +++ b/init/config/docs-builder.go @@ -1,7 +1,147 @@ package main -import "fmt" +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "reflect" + "strings" + "time" -func printDocusaurus(_ *Config) { - fmt.Println("not yet") + "github.com/BurntSushi/toml" +) + +const ( + outputDir = "build/" + dirMode = 0o755 + fileMode = 0o644 +) + +func printDocusaurus(config *Config) { + // Loop the 'Order' list. + if err := makeGenerated(config); err != nil { + panic(err) + } + + for _, section := range config.Order { + // If Order contains a missing section, panic. + if config.Sections[section] == nil { + panic(section + ": in order, but missing from sections. This is a bug in conf-builder.yml.") + } + + if len(config.Sections[section].Params) < 1 { + continue + } + + if config.Defs[section] != nil { + data := config.Sections[section].makeDefinedDocs(config.Prefix, config.Defs[section], config.DefOrder[section]) + if err := output(string(section), data); err != nil { + panic(err) + } + } else { + data := config.Sections[section].makeDocs(config.Prefix, section) + if err := output(string(section), data); err != nil { + panic(err) + } + } + } +} + +func output(file, content string) error { + _ = os.Mkdir(outputDir, dirMode) + date := "---\n# Generated: " + time.Now().Round(time.Second).String() + "\n---\n\n" + //nolint:wrapcheck + return os.WriteFile(filepath.Join(outputDir, file+".md"), []byte(date+content), fileMode) +} + +// makeGenerated writes a special file that the website can import. +// Adds all param sections except global into a docusaurus import format. +func makeGenerated(config *Config) error { + var ( + first bytes.Buffer + second bytes.Buffer + ) + + for _, section := range config.Order { + if len(config.Sections[section].Params) > 0 && section != "global" { + title := "G" + string(section) + first.WriteString("import " + title + " from './" + string(section) + ".md';\n") + second.WriteString("<" + title + "/>\n") + } + } + + return output("generated", first.String()+"\n"+second.String()) +} + +func (h *Header) makeDocs(prefix string, section section) string { + buf := bytes.Buffer{} + buf.WriteString("## " + h.Title + "\n\n
\n") + + conf := h.makeSection(section, true, true) + env := h.makeCompose(h.Title, prefix, true) + header := "[" + string(section) + "]" + + if h.Kind == list { + header = "[[" + string(section) + "]]" + } + + if h.NoHeader { + buf.WriteString(" Examples. Prefix: " + prefix + "\n\n") + } else { + buf.WriteString(" Examples. Prefix: " + prefix + h.Prefix + ", Header: " + header + "\n\n") + } + + buf.WriteString("- Using the config file:\n\n```yaml\n") + buf.WriteString(strings.TrimSpace(conf) + "\n```\n\n") + buf.WriteString("- Using environment variables:\n\n```js\n") + buf.WriteString(env + "```\n\n
\n\n") + buf.WriteString(h.Docs + "\n") // Docs comes before the table. + buf.WriteString(h.makeDocsTable(prefix) + "\n") + buf.WriteString(h.Tail) // Tail goes after the docs and table. + + if h.Notes != "" { // Notes become a sub header. + buf.WriteString("### " + h.Title + " Notes\n\n" + h.Notes) + } + + return buf.String() +} + +func (h *Header) makeDocsTable(prefix string) string { + const ( + tableHeader = "|Config Name|Variable Name|Default / Note|\n|---|---|---|\n" + tableFormat = "|%s|`%s`|%v / %s|\n" + ) + + buf := bytes.Buffer{} + buf.WriteString(tableHeader) + + for _, param := range h.Params { + envVar := prefix + h.Prefix + param.EnvVar + if param.Kind == list { + envVar += "0" + } + + def := "No Default" + + if rv := reflect.ValueOf(param.Default); rv.Kind() == reflect.Bool || !rv.IsZero() { + if t, _ := toml.Marshal(param.Default); len(t) > 0 { + def = "`" + string(t) + "`" + } + } + + buf.WriteString(fmt.Sprintf(tableFormat, param.Name, envVar, def, param.Short)) + } + + return buf.String() +} + +func (h *Header) makeDefinedDocs(prefix string, defs Defs, order []section) string { + var buf bytes.Buffer + + for _, section := range order { + buf.WriteString(createDefinedSection(defs[section], h).makeDocs(prefix, section)) + } + + return buf.String() } diff --git a/init/config/main.go b/init/config/main.go index b8675ea..1af5802 100644 --- a/init/config/main.go +++ b/init/config/main.go @@ -10,15 +10,27 @@ const ( list = "list" ) +type section string + +type Option struct { + Name string `yaml:"name"` + Value any `yaml:"value"` +} + type Config struct { - Defs map[string]Defs `yaml:"defs"` - Prefix string `yaml:"envvar_prefix"` - Order []string `yaml:"order"` - Sections map[string]*Header `yaml:"sections"` + DefOrder map[section][]section `yaml:"def_order"` + Defs map[section]Defs `yaml:"defs"` + Prefix string `yaml:"envvar_prefix"` + Order []section `yaml:"order"` + Sections map[section]*Header `yaml:"sections"` } type Header struct { + Tail string `yaml:"tail"` + Title string `yaml:"title"` Text string `yaml:"text"` + Docs string `yaml:"docs"` + Notes string `yaml:"notes"` Prefix string `yaml:"envvar_prefix"` Params []*Param `yaml:"params"` Kind string `yaml:"kind"` // "", list @@ -26,23 +38,25 @@ type Header struct { } type Param struct { - Name string `yaml:"name"` - EnvVar string `yaml:"envvar"` - Default any `yaml:"default"` - Example any `yaml:"example"` - Short string `yaml:"short"` - Desc string `yaml:"desc"` - Kind string `yaml:"kind"` // "", list, conlist + Name string `yaml:"name"` + EnvVar string `yaml:"envvar"` + Default any `yaml:"default"` + Example any `yaml:"example"` + Short string `yaml:"short"` + Desc string `yaml:"desc"` + Kind string `yaml:"kind"` // "", list, conlist + Recommend []Option `yaml:"recommend"` } type Def struct { Comment bool `yaml:"comment"` // just the header. + Title string `yaml:"title"` Prefix string `yaml:"prefix"` Text string `yaml:"text"` Defaults map[string]any `yaml:"defaults"` } -type Defs map[string]*Def +type Defs map[section]*Def func main() { file, err := os.Open("./conf-builder.yml") @@ -69,3 +83,25 @@ func main() { printDocusaurus(config) } } + +func createDefinedSection(def *Def, section *Header) *Header { + newSection := &Header{ + Text: def.Text, + Prefix: def.Prefix, + Title: def.Title, + Params: section.Params, + Kind: section.Kind, + } + + // Loop each defined section Defaults, and see if one of the param names match. + for overrideName, override := range def.Defaults { + for _, defined := range newSection.Params { + // If the name of the default (override) matches this param name, overwrite the value. + if defined.Name == overrideName { + defined.Default = override + } + } + } + + return newSection +} From e3f4c116890e1cee73488772ea04621eeef3eb89 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Jul 2024 02:25:12 -0700 Subject: [PATCH 6/8] tidy --- go.mod | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 49060e7..0fd5d18 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Unpackerr/unpackerr go 1.22 require ( + github.com/BurntSushi/toml v1.4.0 github.com/fsnotify/fsnotify v1.7.0 github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d github.com/getlantern/systray v1.2.2 @@ -20,11 +21,10 @@ require ( golift.io/starr v1.0.0 golift.io/version v0.0.2 golift.io/xtractr v0.2.3-0.20240710043203-2d7c8a38d931 - + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/BurntSushi/toml v1.4.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bodgit/plumbing v1.3.0 // indirect @@ -68,5 +68,4 @@ require ( golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) From c43f6d5643e090b31b601319342facda042f451f Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Jul 2024 15:20:32 -0700 Subject: [PATCH 7/8] add url input --- init/config/docs-builder.go | 2 +- init/config/main.go | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/init/config/docs-builder.go b/init/config/docs-builder.go index 132ad74..84524f1 100644 --- a/init/config/docs-builder.go +++ b/init/config/docs-builder.go @@ -13,7 +13,7 @@ import ( ) const ( - outputDir = "build/" + outputDir = "generated/" dirMode = 0o755 fileMode = 0o644 ) diff --git a/init/config/main.go b/init/config/main.go index 1af5802..ac74f2b 100644 --- a/init/config/main.go +++ b/init/config/main.go @@ -1,13 +1,21 @@ package main import ( + "fmt" + "io" + "net/http" "os" + "strings" + "time" "gopkg.in/yaml.v3" ) const ( list = "list" + // inputFile = "https://raw.githubusercontent.com/Unpackerr/unpackerr/main/init/config/conf-builder.yml" + inputFile = "https://raw.githubusercontent.com/Unpackerr/unpackerr/dn2_conf_builder/init/config/conf-builder.yml" + opTimeout = 6 * time.Second ) type section string @@ -59,10 +67,11 @@ type Def struct { type Defs map[section]*Def func main() { - file, err := os.Open("./conf-builder.yml") + file, err := openFile() if err != nil { panic(err) } + defer file.Close() config := &Config{} // Decode conf-builder file into Go data structure. @@ -84,6 +93,32 @@ func main() { } } +// openFile opens a file or url for the parser. +func openFile() (io.ReadCloser, error) { + fileName := inputFile + if len(os.Args) > 2 { //nolint:mnd + fileName = os.Args[len(os.Args)-1] // take last arg as file. + } + + if strings.HasPrefix(fileName, "http") { + http.DefaultClient.Timeout = opTimeout + + resp, err := http.Get(fileName) //nolint:noctx // because we set a timeout. + if err != nil { + return nil, fmt.Errorf("%s: %w", fileName, err) + } + + return resp.Body, nil + } + + file, err := os.Open(fileName) + if err != nil { + return nil, fmt.Errorf("%s: %w", fileName, err) + } + + return file, nil +} + func createDefinedSection(def *Def, section *Header) *Header { newSection := &Header{ Text: def.Text, From 31a776b766ad56500263667c15ef7dbe65d70aeb Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Jul 2024 15:38:18 -0700 Subject: [PATCH 8/8] new name --- init/config/docs-builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/config/docs-builder.go b/init/config/docs-builder.go index 84524f1..2314681 100644 --- a/init/config/docs-builder.go +++ b/init/config/docs-builder.go @@ -71,7 +71,7 @@ func makeGenerated(config *Config) error { } } - return output("generated", first.String()+"\n"+second.String()) + return output("index", first.String()+"\n"+second.String()) } func (h *Header) makeDocs(prefix string, section section) string {