Make code more pythonic (#75)

* Make code more pythonic

* Make code more pythonic

---------

Co-authored-by: Nicola Canepa <canne74@gmail.com>
This commit is contained in:
Nicola
2025-12-29 11:03:04 +00:00
committed by GitHub
parent 8aaf54c572
commit 700830e37d
2 changed files with 158 additions and 169 deletions

View File

@@ -1,4 +1,6 @@
import getpass
import os import os
import pwd
class ContainerConfig: class ContainerConfig:
def __init__(self, def __init__(self,
@@ -8,17 +10,17 @@ class ContainerConfig:
): ):
self.root_dir = root_dir self.root_dir = root_dir
self.timezone = timezone self.timezone = timezone
self.config_dir = root_dir + '/config' self.config_dir = f'{root_dir}/config'
self.plex_claim = plex_claim self.plex_claim = plex_claim
self.movie_dir = root_dir + '/media/movies' self.movie_dir = f'{root_dir}/media/movies'
self.tv_dir = root_dir + '/media/tv' self.tv_dir = f'{root_dir}/media/tv'
self.music_dir = root_dir + '/media/music' self.music_dir = f'{root_dir}/media/music'
self.book_dir = root_dir + '/media/books' self.book_dir = f'{root_dir}/media/books'
self.comic_dir = root_dir + '/media/comics' self.comic_dir = f'{root_dir}/media/comics'
self.torrent_dir = root_dir + '/data/torrents' self.torrent_dir = f'{root_dir}/data/torrents'
self.usenet_dir = root_dir + '/data/usenet' self.usenet_dir = f'{root_dir}/data/usenet'
self.homarr_dir = root_dir + '/data/homarr/appdata' self.homarr_dir = f'{root_dir}/data/homarr/appdata'
self.UID = os.popen('id -u').read().rstrip('\n') self.UID = pwd.getpwnam(getpass.getuser()).pw_uid
def plex(self): def plex(self):
return ( return (
@@ -30,10 +32,10 @@ class ContainerConfig:
' - PUID=13010\n' ' - PUID=13010\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - VERSION=docker\n' ' - VERSION=docker\n'
' - PLEX_CLAIM=' + self.plex_claim + '\n' f' - PLEX_CLAIM={self.plex_claim}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/plex-config:/config\n' f' - {self.config_dir}/plex-config:/config\n'
' - ' + self.root_dir + '/data/media:/media\n' f' - {self.root_dir}/data/media:/media\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
) )
@@ -45,11 +47,11 @@ class ContainerConfig:
' depends_on:\n' ' depends_on:\n'
' - plex\n' ' - plex\n'
' environment:\n' ' environment:\n'
' - PUID='+ self.UID + '\n' f' - PUID={self.UID}\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/tautulli-config:/config\n' f' - {self.config_dir}/tautulli-config:/config\n'
' ports:\n' ' ports:\n'
' - "8181:8181"\n' ' - "8181:8181"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -61,13 +63,13 @@ class ContainerConfig:
' image: lscr.io/linuxserver/jellyfin:latest\n' ' image: lscr.io/linuxserver/jellyfin:latest\n'
' container_name: jellyfin\n' ' container_name: jellyfin\n'
' environment:\n' ' environment:\n'
' - PUID='+ self.UID + '\n' f' - PUID={self.UID}\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/jellyfin-config:/config\n' f' - {self.config_dir}/jellyfin-config:/config\n'
' - ' + self.root_dir + '/data/media:/data\n' f' - {self.root_dir}/data/media:/data\n'
' ports:\n' ' ports:\n'
' - "8096:8096"\n' ' - "8096:8096"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -82,10 +84,10 @@ class ContainerConfig:
' - PUID=13001\n' ' - PUID=13001\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/sonarr-config:/config\n' f' - {self.config_dir}/sonarr-config:/config\n'
' - ' + self.root_dir + '/data:/data\n' f' - {self.root_dir}/data:/data\n'
' ports:\n' ' ports:\n'
' - "8989:8989"\n' ' - "8989:8989"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -100,10 +102,10 @@ class ContainerConfig:
' - PUID=13002\n' ' - PUID=13002\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/radarr-config:/config\n' f' - {self.config_dir}/radarr-config:/config\n'
' - ' + self.root_dir + '/data:/data\n' f' - {self.root_dir}/data:/data\n'
' ports:\n' ' ports:\n'
' - "7878:7878"\n' ' - "7878:7878"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -117,10 +119,10 @@ class ContainerConfig:
' environment:\n' ' environment:\n'
' - PUID=13013\n' ' - PUID=13013\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/bazarr-config:/config\n' f' - {self.config_dir}/bazarr-config:/config\n'
' - ' + self.root_dir + '/data/media:/media\n' f' - {self.root_dir}/data/media:/media\n'
' ports:\n' ' ports:\n'
' - "6767:6767"\n' ' - "6767:6767"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -135,10 +137,10 @@ class ContainerConfig:
' - PUID=13003\n' ' - PUID=13003\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/lidarr-config:/config\n' f' - {self.config_dir}/lidarr-config:/config\n'
' - ' + self.root_dir + '/data:/data\n' f' - {self.root_dir}/data:/data\n'
' ports:\n' ' ports:\n'
' - "8686:8686"\n' ' - "8686:8686"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -153,10 +155,10 @@ class ContainerConfig:
' - PUID=13004\n' ' - PUID=13004\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/readarr-config:/config\n' f' - {self.config_dir}/readarr-config:/config\n'
' - ' + self.root_dir + '/data:/data\n' f' - {self.root_dir}/data:/data\n'
' ports:\n' ' ports:\n'
' - "8787:8787"\n' ' - "8787:8787"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -172,8 +174,8 @@ class ContainerConfig:
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/mylar-config:/config\n' f' - {self.config_dir}/mylar-config:/config\n'
' - ' + self.root_dir + '/data:/data\n' f' - {self.root_dir}/data:/data\n'
' ports:\n' ' ports:\n'
' - "8090:8090"\n' ' - "8090:8090"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -186,12 +188,12 @@ class ContainerConfig:
' container_name: audiobookshelf\n' ' container_name: audiobookshelf\n'
' environment:\n' ' environment:\n'
' - user=13014:13000\n' ' - user=13014:13000\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/audiobookshelf:/config\n' f' - {self.config_dir}/audiobookshelf:/config\n'
' - ' + self.root_dir + '/data/media/audiobooks:/audiobooks\n' f' - {self.root_dir}/data/media/audiobooks:/audiobooks\n'
' - ' + self.root_dir + '/data/media/podcasts:/podcasts\n' f' - {self.root_dir}/data/media/podcasts:/podcasts\n'
' - ' + self.root_dir + '/data/media/audiobookshelf-metadata:/metadata\n' f' - {self.root_dir}/data/media/audiobookshelf-metadata:/metadata\n'
' ports:\n' ' ports:\n'
' - "13378:80"\n' ' - "13378:80"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -206,9 +208,9 @@ class ContainerConfig:
' - PUID=13006\n' ' - PUID=13006\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/prowlarr-config:/config\n' f' - {self.config_dir}/prowlarr-config:/config\n'
' ports:\n' ' ports:\n'
' - "9696:9696"\n' ' - "9696:9696"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -221,9 +223,9 @@ class ContainerConfig:
' image: ghcr.io/homarr-labs/homarr:latest\n' ' image: ghcr.io/homarr-labs/homarr:latest\n'
' container_name: homarr\n' ' container_name: homarr\n'
' environment:\n' ' environment:\n'
' - SECRET_ENCRYPTION_KEY=' + key + '\n' f' - SECRET_ENCRYPTION_KEY={key}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.homarr_dir + ':/appdata\n' f' - {self.homarr_dir}:/appdata\n'
' - /var/run/docker.sock:/var/run/docker.sock\n' ' - /var/run/docker.sock:/var/run/docker.sock\n'
' ports:\n' ' ports:\n'
' - "7575:7575"\n' ' - "7575:7575"\n'
@@ -239,11 +241,11 @@ class ContainerConfig:
' - PUID=13007\n' ' - PUID=13007\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' - WEBUI_PORT=8080\n' ' - WEBUI_PORT=8080\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/qbittorrent-config:/config\n' f' - {self.config_dir}/qbittorrent-config:/config\n'
' - ' + self.torrent_dir + ':/data/torrents\n' f' - {self.torrent_dir}:/data/torrents\n'
' ports:\n' ' ports:\n'
' - "8080:8080"\n' ' - "8080:8080"\n'
' - "6881:6881"\n' ' - "6881:6881"\n'
@@ -260,9 +262,9 @@ class ContainerConfig:
' - PUID=13009\n' ' - PUID=13009\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/overseerr-config:/app/config\n' f' - {self.config_dir}/overseerr-config:/app/config\n'
' ports:\n' ' ports:\n'
' - "5055:5055"\n' ' - "5055:5055"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -277,9 +279,9 @@ class ContainerConfig:
' - PUID=13012\n' ' - PUID=13012\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/jellyseerr-config:/app/config\n' f' - {self.config_dir}/jellyseerr-config:/app/config\n'
' ports:\n' ' ports:\n'
' - "5056:5055"\n' ' - "5056:5055"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -294,10 +296,10 @@ class ContainerConfig:
' - PUID=13011\n' ' - PUID=13011\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/sabnzbd-config:/config\n' f' - {self.config_dir}/sabnzbd-config:/config\n'
' - ' + self.usenet_dir + ':/data/usenet\n' f' - {self.usenet_dir}:/data/usenet\n'
' ports:\n' ' ports:\n'
' - "8081:8080"\n' ' - "8081:8080"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -312,9 +314,9 @@ class ContainerConfig:
' - PUID=13008\n' ' - PUID=13008\n'
' - PGID=13000\n' ' - PGID=13000\n'
' - UMASK=002\n' ' - UMASK=002\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' volumes:\n' ' volumes:\n'
' - ' + self.config_dir + '/jackett-config:/config\n' f' - {self.config_dir}/jackett-config:/config\n'
' ports:\n' ' ports:\n'
' - 9117:9117\n' ' - 9117:9117\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'
@@ -329,7 +331,7 @@ class ContainerConfig:
' - LOG_LEVEL=${LOG_LEVEL:-info}\n' ' - LOG_LEVEL=${LOG_LEVEL:-info}\n'
' - LOG_HTML=${LOG_HTML:-false}\n' ' - LOG_HTML=${LOG_HTML:-false}\n'
' - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}\n' ' - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}\n'
' - TZ=' + self.timezone + '\n' f' - TZ={self.timezone}\n'
' ports:\n' ' ports:\n'
' - "8191:8191"\n' ' - "8191:8191"\n'
' restart: unless-stopped\n\n' ' restart: unless-stopped\n\n'

155
main.py
View File

@@ -16,7 +16,8 @@ def take_boolean_input(default=True):
return False return False
print('Please answer with y or n.', end=' ') print('Please answer with y or n.', end=' ')
def take_input(service_name, service_type): def take_input(service_name, service_type, name_to_show=None):
print(f'Use {name_to_show or service_name.capitalize()}? [Y/n]', end=" ")
choice = take_boolean_input() choice = take_boolean_input()
if choice: if choice:
services_classed[service_type].append(service_name) services_classed[service_type].append(service_name)
@@ -33,7 +34,7 @@ def take_directory_input():
print('Please make sure the path is absolute, meaning it starts at the root of your filesystem and starts with "/":', end=' ') print('Please make sure the path is absolute, meaning it starts at the root of your filesystem and starts with "/":', end=' ')
def get_system_timezone(): def get_system_timezone():
tz_path = "/etc/localtime" tz_path = '/etc/localtime'
if os.path.exists(tz_path): if os.path.exists(tz_path):
if os.path.islink(tz_path): if os.path.islink(tz_path):
@@ -41,130 +42,116 @@ def get_system_timezone():
return tz.split('zoneinfo/')[-1] return tz.split('zoneinfo/')[-1]
return None return None
print('Welcome to the EZarr CLI.') def main():
print('This CLI will ask you which services you\'d like to use and more. If you\'d like more information about a ' print('Welcome to the EZarr CLI.')
print("This CLI will ask you which services you'd like to use and more. If you'd like more information about a "
'certain service, look in the README.') 'certain service, look in the README.')
print('\n===SERVARR===') print('\n===SERVARR===')
services_classed['servarr'] = [] services_classed['servarr'] = []
print('Use Sonarr? [Y/n]', end=" ") take_input('sonarr', 'servarr')
take_input('sonarr', 'servarr') take_input('radarr', 'servarr')
print('Use Radarr? [Y/n]', end=" ") take_input('lidarr', 'servarr')
take_input('radarr', 'servarr') take_input('readarr', 'servarr')
print('Use Lidarr? [Y/n]', end=" ") take_input('mylar3', 'servarr')
take_input('lidarr', 'servarr') take_input('audiobookshelf', 'servarr')
print('Use Readarr? [Y/n]', end=" ") take_input('homarr', 'servarr')
take_input('readarr', 'servarr') if len(services_classed['servarr']) == 0:
print('Use Mylar3? [Y/n]', end=" ")
take_input('mylar3', 'servarr')
print('Use Audiobookshelf? [Y/n]', end=" ")
take_input('audiobookshelf', 'servarr')
print('Use Homarr? [Y/n]', end=" ")
take_input('homarr', 'servarr')
if len(services_classed['servarr']) == 0:
print('Warning: no media management services selected.') print('Warning: no media management services selected.')
if services_classed['servarr'].__contains__('sonarr') or services_classed['servarr'].__contains__('radarr'): if 'sonarr' in services_classed['servarr'] or 'radarr' in services_classed['servarr']:
print('Use Bazarr? [Y/n]', end=" ")
take_input('bazarr', 'servarr') take_input('bazarr', 'servarr')
print('\n===INDEXERS===') print('\n===INDEXERS===')
services_classed['indexer'] = [] services_classed['indexer'] = []
print('Use Prowlarr? [Y/n]', end=" ") take_input('prowlarr', 'indexer')
take_input('prowlarr', 'indexer') take_input('jackett', 'indexer')
print('Use Jackett? [Y/n]', end=" ") if len(services_classed['indexer']) == 0:
take_input('jackett', 'indexer')
if len(services_classed['indexer']) == 0:
print('Warning: no indexing service selected.') print('Warning: no indexing service selected.')
print('\n===CLOUDFLARE BYPASS===') print('\n===CLOUDFLARE BYPASS===')
services_classed['bypass'] = [] services_classed['bypass'] = []
print('Use Flaresolverr? [Y/n]', end=" ") take_input('flaresolverr', 'bypass')
take_input('flaresolverr', 'bypass')
print('\n===MEDIA SERVERS===') print('\n===MEDIA SERVERS===')
services_classed['ms'] = [] services_classed['ms'] = []
print('Use PleX? [Y/n]', end=" ") take_input('plex', 'ms')
take_input('plex', 'ms') if 'plex' in services_classed['ms']:
if services_classed['ms'].__contains__('plex'):
print('Use Tautulli? [Y/n]', end=" ")
take_input('tautulli', 'ms') take_input('tautulli', 'ms')
if services_classed['servarr'].__contains__('sonarr') or services_classed['servarr'].__contains__('radarr'): if 'sonarr' in services_classed['servarr'] or 'radarr' in services_classed['servarr']:
print('Use Overseerr? [Y/n]', end=" ")
take_input('overseerr', 'servarr') take_input('overseerr', 'servarr')
print('Use Jellyfin? [Y/n]', end=" ") take_input('jellyfin', 'ms')
take_input('jellyfin', 'ms') if ('jellyfin' in services_classed['ms']
if (services_classed['ms'].__contains__('jellyfin') and ('sonarr' in services_classed['servarr'] or 'radarr' in services_classed['servarr'])):
and (services_classed['servarr'].__contains__('sonarr') or services_classed['servarr'].__contains__('radarr'))):
print('Use Jellyseerr? [Y/n]', end=" ")
take_input('jellyseerr', 'servarr') take_input('jellyseerr', 'servarr')
if len(services_classed['ms']) == 0: if len(services_classed['ms']) == 0:
print('Warning: no media servers selected.') print('Warning: no media servers selected.')
print('\n===BITTORRENT===') print('\n===BITTORRENT===')
services_classed['torrent'] = [] services_classed['torrent'] = []
print('Use qBittorrent? [Y/n]', end=" ") take_input('qbittorrent', 'torrent', 'qBittorrent')
take_input('qbittorrent', 'torrent')
print('\n===USENET===') print('\n===USENET===')
services_classed['usenet'] = [] services_classed['usenet'] = []
print('Use SABnzbd? [Y/n]', end=" ") take_input('sabnzbd', 'usenet', 'SABnzbd')
take_input('sabnzbd', 'usenet')
if len(services_classed['torrent']) == 0 and len(services_classed['usenet']) == 0: if len(services_classed['torrent']) == 0 and len(services_classed['usenet']) == 0:
print('Warning: no usenet or BitTorrent clients selected.') print('Warning: no usenet or BitTorrent clients selected.')
services = [] services = []
for service_class in services_classed.keys(): for service_class in services_classed.keys():
services.extend(services_classed[service_class]) services.extend(services_classed[service_class])
if len(services) == 0: if len(services) == 0:
print('No services selected. Terminating.') print('No services selected. Terminating.')
exit(1) exit(1)
print('\n===CONFIGURATION===') print('\n===CONFIGURATION===')
print('Please enter your timezone (like "Europe/Amsterdam") or press enter to use your system\'s configured timezone:', end=' ') print('Please enter your timezone (like "Europe/Amsterdam") or press enter to use your system\'s configured timezone:', end=' ')
timezone = input() timezone = input()
if timezone == '': if timezone == '':
timezone = get_system_timezone() timezone = get_system_timezone()
if len(timezone) == 0: # if user pressed enter and reading timezone from /etc/localtime failed then default to Amsterdam if len(timezone) == 0: # if user pressed enter and reading timezone from /etc/localtime failed then default to Amsterdam
timezone = 'Europe/Amsterdam' timezone = 'Europe/Amsterdam'
plex_claim = '' plex_claim = ''
if services.__contains__('plex'): if 'plex' in services:
print('If you have a PleX claim token, enter it now. Otherwise, just press enter.', end=' ') print('If you have a PleX claim token, enter it now. Otherwise, just press enter.', end=' ')
plex_claim = input() plex_claim = input()
print('Where would you like to keep your files?', end=' ') print('Where would you like to keep your files?', end=' ')
root_dir = take_directory_input() root_dir = take_directory_input()
compose = open('docker-compose.yml', 'w') compose = open('docker-compose.yml', 'w')
compose.write( compose.write(
'---\n' '---\n'
'services:\n' 'services:\n'
) )
container_config = ContainerConfig(root_dir, timezone, plex_claim=plex_claim) container_config = ContainerConfig(root_dir, timezone, plex_claim=plex_claim)
for service in services: for service in services:
compose.write(getattr(container_config, service)()) compose.write(getattr(container_config, service)())
compose.close() compose.close()
print("Docker compose file generated successfully.") print("Docker compose file generated successfully.")
print("Do you want to also generate the required folder structure and permissions? (this is required for first time setup) [Y/n]: ") print("Do you want to also generate the required folder structure and permissions? (this is required for first time setup) [Y/n]: ")
generate_permissions = take_boolean_input() generate_permissions = take_boolean_input()
if generate_permissions: if generate_permissions:
permission_setup = UserGroupSetup(root_dir=root_dir) permission_setup = UserGroupSetup(root_dir=root_dir)
for service in services: for service in services:
try: try:
getattr(permission_setup, service)() getattr(permission_setup, service)()
except AttributeError: except AttributeError:
pass pass
else: else:
print("Permission and folder structure generation skipped by user.") print("Permission and folder structure generation skipped by user.")
print('Process complete. You can now run "docker compose up -d" to start your containers.') print('Process complete. You can now run "docker compose up -d" to start your containers.')
print('Thank you for using EZarr. If you experience any issues or have feature requests, add them to our issues.') print('Thank you for using EZarr. If you experience any issues or have feature requests, add them to our issues.')
print('For questions, you can also use the discussions tab.') print('For questions, you can also use the discussions tab.')
exit(0) exit(0)
if __name__ == '__main__':
main()