diff --git a/.gitignore b/.gitignore
index 0f1ec4b..e69de29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +0,0 @@
-*
-
-!*/
-!.gitignore
-!*/docker-compose.y*ml
-!*/.*env*
-
-qbittorrent/config/qBittorrent/qBittorrent-data.conf
-!qbittorrent/config/qBittorrent/*.json
-
-!stash/cron/**/*
-!stash/config/config.yml
-!stash/stash-box-config/*
-!stash/config/plugins/**/*
-!servarr/slskd/slskd.yml
-qbittorrent/config/qBittorrent/qBittorrent-data.conf
diff --git a/glances/docker-compose.yml b/glances/docker-compose.yml
index 26e5229..6fd09f4 100644
--- a/glances/docker-compose.yml
+++ b/glances/docker-compose.yml
@@ -1,17 +1,14 @@
services:
glances:
- #image: nicolargo/glances:3.4.0.5-full
- image: nicolargo/glances:latest-full
container_name: glances-arr
+ image: nicolargo/glances:latest-full
restart: always
- pid: host
ports:
- - 61208:61208
+ - '61208:61208'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- # Uncomment the below line if you want glances to display host OS detail instead of container's
- /etc/os-release:/etc/os-release:ro
- - /mnt/hdd_1tb:/mnt/hdd_1tb
- - /:/host
+ - /:/host:ro
environment:
- - "GLANCES_OPT=-w"
+ - GLANCES_OPT=-w
+ pid: host
diff --git a/kavita/docker-compose.yml b/kavita/docker-compose.yml
index 3a01190..08731fd 100644
--- a/kavita/docker-compose.yml
+++ b/kavita/docker-compose.yml
@@ -1,15 +1,12 @@
services:
- kavita:
- image: jvmilazz0/kavita:latest # Using the stable branch from the official dockerhub repo.
- container_name: kavita
- volumes:
- - /mnt/system/Etc/NotPorn:/images # Manga is just an example you can have the name you want. See the following
-# - ./comics:/comics # Use as many as you want
-# - ./books:/books #
- - ./data:/kavita/config # Change './data if you want to have the config files in a different place.
- # /kavita/config must not be changed
- environment:
- - TZ=America/Sao_Paulo
- ports:
- - "5000:5000" # Change the public port (the first 5000) if you have conflicts with other services
- restart: unless-stopped
+ kavita:
+ container_name: kavita
+ image: jvmilazz0/kavita:latest
+ restart: unless-stopped
+ ports:
+ - '5000:5000'
+ volumes:
+ - /mnt/system/f95:/images
+ - /home/ccalifice/docker-data/kavita/config:/kavita/config
+ environment:
+ - TZ=America/Sao_Paulo
diff --git a/komodo/docker-compose.yml b/komodo/docker-compose.yml
index ed28e6a..ff86dbf 100644
--- a/komodo/docker-compose.yml
+++ b/komodo/docker-compose.yml
@@ -1,6 +1,6 @@
services:
## Deploy Periphery container using this block,
- ## or deploy the Periphery binary with systemd using
+ ## or deploy the Periphery binary with systemd using
## https://github.com/moghtech/komodo/tree/main/scripts
periphery:
image: ghcr.io/moghtech/komodo-periphery:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
diff --git a/qbittorrent/config/qBittorrent/categories.json b/qbittorrent/config/qBittorrent/categories.json
deleted file mode 100644
index 539f23f..0000000
--- a/qbittorrent/config/qBittorrent/categories.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "books": {
- "save_path": ""
- },
- "games": {
- "save_path": ""
- },
- "movies": {
- "save_path": ""
- },
- "music": {
- "save_path": ""
- },
- "radarr": {
- "save_path": ""
- },
- "tv": {
- "save_path": ""
- },
- "xxx": {
- "save_path": ""
- }
-}
diff --git a/qbittorrent/config/qBittorrent/qBittorrent-data.conf b/qbittorrent/config/qBittorrent/qBittorrent-data.conf
deleted file mode 100644
index 3701ffe..0000000
--- a/qbittorrent/config/qBittorrent/qBittorrent-data.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-[Stats]
-AllStats=@Variant(\0\0\0\x1c\0\0\0\x2\0\0\0\x12\0\x41\0l\0l\0t\0i\0m\0\x65\0\x44\0L\0\0\0\x4\0\0\tU\f\x82\x11\xd7\0\0\0\x12\0\x41\0l\0l\0t\0i\0m\0\x65\0U\0L\0\0\0\x4\0\0RV\x93qk\xdc)
diff --git a/qbittorrent/config/qBittorrent/qBittorrent.conf b/qbittorrent/config/qBittorrent/qBittorrent.conf
deleted file mode 100644
index e8ac349..0000000
--- a/qbittorrent/config/qBittorrent/qBittorrent.conf
+++ /dev/null
@@ -1,113 +0,0 @@
-[Application]
-FileLogger\Age=1
-FileLogger\AgeType=1
-FileLogger\Backup=true
-FileLogger\DeleteOld=true
-FileLogger\Enabled=true
-FileLogger\MaxSizeBytes=66560
-FileLogger\Path=/config/qBittorrent/logs
-
-[AutoRun]
-OnTorrentAdded\Enabled=false
-OnTorrentAdded\Program=
-enabled=false
-program=
-
-[BitTorrent]
-Session\AddTorrentStopped=false
-Session\AlternativeGlobalDLSpeedLimit=10000
-Session\AlternativeGlobalUPSpeedLimit=0
-Session\BandwidthSchedulerEnabled=true
-Session\DHTEnabled=true
-Session\DefaultSavePath=/data/torrents
-Session\DisableAutoTMMByDefault=false
-Session\ExcludedFileNames=
-Session\GlobalDLSpeedLimit=0
-Session\IgnoreSlowTorrentsForQueueing=true
-Session\LSDEnabled=true
-Session\MaxActiveCheckingTorrents=2
-Session\MaxActiveDownloads=10
-Session\MaxActiveTorrents=100
-Session\MaxActiveUploads=20
-Session\MaxConnections=-1
-Session\MaxUploads=-1
-Session\MaxUploadsPerTorrent=-1
-Session\PeXEnabled=true
-Session\Port=40848
-Session\QueueingSystemEnabled=true
-Session\SSL\Port=55040
-Session\ShareLimitAction=Stop
-Session\Tags=LastFiles, SceneTime
-Session\TempPath=/downloads/incomplete/
-Session\UseAlternativeGlobalSpeedLimit=true
-
-[Core]
-AutoDeleteAddedTorrentFile=Never
-
-[LegalNotice]
-Accepted=true
-
-[Meta]
-MigrationVersion=8
-
-[Network]
-PortForwardingEnabled=false
-Proxy\HostnameLookupEnabled=false
-Proxy\Profiles\BitTorrent=true
-Proxy\Profiles\Misc=true
-Proxy\Profiles\RSS=true
-
-[Preferences]
-Advanced\RecheckOnCompletion=false
-Advanced\trackerPort=9000
-Advanced\trackerPortForwarding=false
-Connection\PortRangeMin=6881
-Connection\ResolvePeerCountries=true
-Connection\UPnP=false
-Downloads\SavePath=/downloads/
-Downloads\TempPath=/downloads/incomplete/
-DynDNS\DomainName=aaaaaa.dyndns.org
-DynDNS\Enabled=false
-DynDNS\Password=
-DynDNS\Service=DynDNS
-DynDNS\Username=
-General\Locale=en
-MailNotification\email=
-MailNotification\enabled=false
-MailNotification\password=
-MailNotification\req_auth=true
-MailNotification\req_ssl=false
-MailNotification\sender=qBittorrent_notification@example.com
-MailNotification\smtp_server=smtp.changeme.com
-MailNotification\username=
-Scheduler\end_time=@Variant(\0\0\0\xf\x5%q\xa0)
-Scheduler\start_time=@Variant(\0\0\0\xf\x1\xee\x62\x80)
-WebUI\Address=*
-WebUI\AlternativeUIEnabled=false
-WebUI\AuthSubnetWhitelist=192.168.1.0/24
-WebUI\AuthSubnetWhitelistEnabled=true
-WebUI\BanDuration=3600
-WebUI\CSRFProtection=true
-WebUI\ClickjackingProtection=true
-WebUI\CustomHTTPHeaders=
-WebUI\CustomHTTPHeadersEnabled=false
-WebUI\HTTPS\CertificatePath=
-WebUI\HTTPS\Enabled=false
-WebUI\HTTPS\KeyPath=
-WebUI\HostHeaderValidation=true
-WebUI\LocalHostAuth=true
-WebUI\MaxAuthenticationFailCount=10
-WebUI\Password_PBKDF2="@ByteArray(OUcpojCntLZHBzdhkSxH9A==:6SbAK9PPyaEU4We0xKGyOEG9ktK0c1zdZszzsvi5jMtrJR0WuwZut60cV8IEDukvNXofPjWrGCv12kQCCKS/JA==)"
-WebUI\Port=8181
-WebUI\ReverseProxySupportEnabled=true
-WebUI\RootFolder=
-WebUI\SecureCookie=true
-WebUI\ServerDomains=*
-WebUI\SessionTimeout=3600
-WebUI\TrustedReverseProxiesList=qbittorrent.ccalifice.com
-WebUI\UseUPnP=false
-WebUI\Username=admin
-
-[RSS]
-AutoDownloader\DownloadRepacks=true
-AutoDownloader\SmartEpisodeFilter=s(\\d+)e(\\d+), (\\d+)x(\\d+), "(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", "(\\d{1,2}[.\\-]\\d{1,2}[.\\-]\\d{4})"
diff --git a/qbittorrent/config/qBittorrent/watched_folders.json b/qbittorrent/config/qBittorrent/watched_folders.json
deleted file mode 100644
index 2c63c08..0000000
--- a/qbittorrent/config/qBittorrent/watched_folders.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
diff --git a/qbittorrent/docker-compose.yml b/qbittorrent/docker-compose.yml
index 57e5a13..45d7e10 100644
--- a/qbittorrent/docker-compose.yml
+++ b/qbittorrent/docker-compose.yml
@@ -8,38 +8,10 @@ services:
- TZ=America/Sao_Paulo
- WEBUI_PORT=8181
volumes:
- - ./config:/config
+ - /home/ccalifice/docker-data/qbittorrent/config:/config
- /mnt/servarr/data/torrents:/data/torrents
ports:
- "8181:8181"
- "40848:40848"
- "40848:40848/udp"
restart: unless-stopped
-
-# qbit_manage:
-# container_name: qbit_manage
-# image: ghcr.io/stuffanthings/qbit_manage:latest
-# volumes:
-# - ./config:/config:rw
-# - /mnt/user/data/torrents/:/data/torrents:rw
-# - /mnt/hdd_1tb/data/torrents:/qbittorrent/:ro
-# environment:
-# - QBT_RUN=false
-# - QBT_SCHEDULE=1440
-# - QBT_CONFIG=config.yml
-# - QBT_LOGFILE=activity.log
-# - QBT_CROSS_SEED=false
-# - QBT_RECHECK=false
-# - QBT_CAT_UPDATE=false
-# - QBT_TAG_UPDATE=false
-# - QBT_REM_UNREGISTERED=false
-# - QBT_REM_ORPHANED=false
-# - QBT_TAG_TRACKER_ERROR=false
-# - QBT_TAG_NOHARDLINKS=false
-# - QBT_SHARE_LIMITS=false
-# - QBT_SKIP_CLEANUP=false
-# - QBT_DRY_RUN=false
-# - QBT_LOG_LEVEL=INFO
-# - QBT_DIVIDER==
-# - QBT_WIDTH=100
-# restart: on-failure:2
diff --git a/servarr/docker-compose.yml b/servarr/docker-compose.yml
index 62db9e5..cc4099c 100644
--- a/servarr/docker-compose.yml
+++ b/servarr/docker-compose.yml
@@ -1,4 +1,3 @@
----
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
@@ -8,7 +7,7 @@ services:
- PGID=1002
- TZ=America/Sao_Paulo
volumes:
- - ./sonarr:/config
+ - /home/ccalifice/docker-data/servarr/sonarr:/config
- /mnt/servarr/data:/data
ports:
- "8989:8989"
@@ -22,7 +21,7 @@ services:
- PGID=1002
- TZ=America/Sao_Paulo
volumes:
- - ./radarr:/config
+ - /home/ccalifice/docker-data/servarr/radarr:/config
- /mnt/servarr/data:/data
ports:
- "7878:7878"
@@ -40,25 +39,95 @@ services:
- SLSKD_SLSK_USERNAME=ticonouvido
- SLSKD_SLSK_PASSWORD=asdf1234
volumes:
- - ./slskd:/app
+ - /home/ccalifice/docker-data/servarr/slskd:/app
- /mnt/servarr/data/torrents/music:/music
- /mnt/servarr/data/media/music:/music-organized
restart: unless-stopped
- lidarr:
- image: ghcr.io/hotio/lidarr:pr-plugins
- container_name: lidarr
+ prowlarr:
+ image: lscr.io/linuxserver/prowlarr:develop
+ container_name: prowlarr
environment:
- PUID=1000
- PGID=1002
- TZ=America/Sao_Paulo
volumes:
- - ./lidarr:/config
- - /mnt/servarr/data/:/data #optional
+ - /home/ccalifice/docker-data/servarr/prowlarr:/config
ports:
- - 8686:8686
+ - "9696:9696"
restart: unless-stopped
+ bazarr:
+ image: lscr.io/linuxserver/bazarr:latest
+ container_name: bazarr
+ environment:
+ - PUID=1000
+ - PGID=1002
+ - TZ=America/Sao_Paulo
+ volumes:
+ - /home/ccalifice/docker-data/servarr/bazarr:/config
+ - /mnt/servarr/data/media/movies:/movies
+ - /mnt/servarr/data/media/tv:/tv
+ ports:
+ - "6767:6767"
+ restart: unless-stopped
+
+ jellyseerr:
+ image: fallenbagel/jellyseerr:latest
+ container_name: jellyseerr
+ environment:
+ - LOG_LEVEL=info
+ - TZ=America/Sao_Paulo
+ ports:
+ - 5055:5055
+ volumes:
+ - /home/ccalifice/docker-data/servarr/jellyseer:/app/config
+ restart: unless-stopped
+ networks:
+ - jellyseer
+
+ flaresolverr:
+ image: ghcr.io/flaresolverr/flaresolverr:latest
+ container_name: flaresolverr
+ environment:
+ - LOG_LEVEL=${LOG_LEVEL:-info}
+ - LOG_HTML=${LOG_HTML:-false}
+ - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
+ - TZ=America/Sao_Paulo
+ ports:
+ - "${PORT:-8191}:8191"
+ restart: unless-stopped
+
+ # jackett:
+ # image: lscr.io/linuxserver/jackett:latest
+ # container_name: jackett
+ # environment:
+ # - PUID=1000
+ # - PGID=1002
+ # - TZ=America/Sao_Paulo
+ # - AUTO_UPDATE=true #optional
+ # - RUN_OPTS= #optional
+ # volumes:
+ # - ./jackett:/config
+ # - ./blackhole:/downloads
+ # ports:
+ # - 9117:9117
+ # restart: unless-stopped
+
+ # lidarr:
+ # image: ghcr.io/hotio/lidarr:pr-plugins
+ # container_name: lidarr
+ # environment:
+ # - PUID=1000
+ # - PGID=1002
+ # - TZ=America/Sao_Paulo
+ # volumes:
+ # - /home/ccalifice/docker-data/servarr/lidarr:/config
+ # - /mnt/servarr/data/:/data #optional
+ # ports:
+ # - 8686:8686
+ # restart: unless-stopped
+
# soularr:
# restart: unless-stopped
@@ -79,78 +148,5 @@ services:
# - ./soularr:/data
# image: mrusse08/soularr:latest
- prowlarr:
- image: lscr.io/linuxserver/prowlarr:develop
- container_name: prowlarr
- environment:
- - PUID=1000
- - PGID=1002
- - TZ=America/Sao_Paulo
- volumes:
- - ./prowlarr:/config
- ports:
- - "9696:9696"
- restart: unless-stopped
-
- bazarr:
- image: lscr.io/linuxserver/bazarr:latest
- container_name: bazarr
- environment:
- - PUID=1000
- - PGID=1002
- - TZ=America/Sao_Paulo
- volumes:
- - ./bazarr:/config
- - /mnt/servarr/data/media/movies:/movies
- - /mnt/servarr/data/media/tv:/tv
- ports:
- - "6767:6767"
- restart: unless-stopped
-
- jellyseerr:
- image: fallenbagel/jellyseerr:latest
- container_name: jellyseerr
- environment:
- - LOG_LEVEL=info
- - TZ=America/Sao_Paulo
- ports:
- - 5055:5055
- volumes:
- - ./jellyseer:/app/config
- restart: unless-stopped
- networks:
- - pangolin
-
- flaresolverr:
- # DockerHub mirror flaresolverr/flaresolverr:latest
- image: ghcr.io/flaresolverr/flaresolverr:latest
- container_name: flaresolverr
- environment:
- - LOG_LEVEL=${LOG_LEVEL:-info}
- - LOG_HTML=${LOG_HTML:-false}
- - CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
- - TZ=America/Sao_Paulo
- ports:
- - "${PORT:-8191}:8191"
- restart: unless-stopped
-
- jackett:
- image: lscr.io/linuxserver/jackett:latest
- container_name: jackett
- environment:
- - PUID=1000
- - PGID=1002
- - TZ=America/Sao_Paulo
- - AUTO_UPDATE=true #optional
- - RUN_OPTS= #optional
- volumes:
- - ./jackett:/config
- - ./blackhole:/downloads
- ports:
- - 9117:9117
- restart: unless-stopped
-
networks:
- pangolin:
- external: true
- name: pangolin
+ jellyseer:
diff --git a/servarr/slskd/slskd.yml b/servarr/slskd/slskd.yml
deleted file mode 100644
index d5c0a6a..0000000
--- a/servarr/slskd/slskd.yml
+++ /dev/null
@@ -1,265 +0,0 @@
-# debug: false
-# headless: false
-# remote_configuration: false
-# remote_file_management: false
-# instance_name: default
-# flags:
-# no_logo: false
-# no_start: false
-# no_config_watch: false
-# no_connect: false
-# no_share_scan: false
-# force_share_scan: false
-# no_version_check: false
-# log_sql: false
-# experimental: false
-# volatile: false
-# case_sensitive_reg_ex: false
-# legacy_windows_tcp_keepalive: false
-# optimistic_relay_file_info: false
-# relay:
-# enabled: false
-# mode: controller # controller (default), agent, or debug (for local development)
-# # controller config is required when running in 'agent' mode
-# # this specifies the relay controller that will be controlling this agent
-# controller:
-# address: https://some.site.com:5000
-# ignore_certificate_errors: false
-# api_key:
-# secret:
-# downloads: false
-# # agent config is optional when running in 'controller' mode
-# # this specifies all of the agents capable of connecting
-# agents:
-# my_agent:
-# instance_name: my_agent # make sure the top-level instance_name of the agent matches!
-# secret:
-# cidr: 0.0.0.0/0,::/0
-# permissions:
-# file:
-# mode: ~ # not for Windows, chmod syntax, e.g. 644, 777. can't escalate beyond umask
- directories:
-# incomplete: ~
- downloads: /music
- shares:
- directories:
- - /music-organized
-# filters:
-# - \.ini$
-# - Thumbs.db$
-# - \.DS_Store$
-# cache:
-# storage_mode: memory
-# workers: 16
-# retention: ~ # retain indefinitely (do not automatically re-scan)
-# rooms:
-# - ~
-# global:
-# upload:
-# slots: 20
-# speed_limit: 1000 # in kibibytes
-# limits:
-# queued:
-# files: 500
-# megabytes: 5000
-# daily:
-# files: 1000
-# megabytes: 10000
-# failures: 200
-# weekly:
-# files: 5000
-# megabytes: 50000
-# failures: 1000
-# download:
-# slots: 500
-# speed_limit: 1000
-# groups:
-# default:
-# upload:
-# priority: 500
-# strategy: roundrobin
-# slots: 10
-# limits:
-# queued:
-# files: 150
-# megabytes: 1500
-# daily: ~ # no daily limits (weekly still apply)
-# weekly:
-# files: 1500
-# megabytes: 15000
-# failures: 150
-# leechers:
-# thresholds:
-# files: 1
-# directories: 1
-# upload:
-# priority: 999
-# strategy: roundrobin
-# slots: 1
-# speed_limit: 100
-# limits:
-# queued:
-# files: 15
-# megabytes: 150
-# daily:
-# files: 30
-# megabytes: 300
-# failures: 10
-# weekly:
-# files: 150
-# megabytes: 1500
-# failures: 30
-# blacklisted:
-# members:
-# -
-# cidrs:
-# -
-# user_defined:
-# my_buddies:
-# upload:
-# priority: 250
-# strategy: firstinfirstout
-# slots: 10
-# limits:
-# queued:
-# files: 1000 # override global default
-# members:
-# - alice
-# - bob
-# blacklist:
-# enabled: true
-# file:
-# filters:
-# search:
-# request:
-# - ^.{1,2}$
- web:
-# port: 5030
-# https:
-# disabled: false
-# port: 5031
-# force: false
-# certificate:
-# pfx: ~
-# password: ~
-# url_base: /
-# content_path: wwwroot
-# logging: false
- authentication:
-# disabled: false
-# username: slskd
-# password: slskd
-# jwt:
-# key: ~
-# ttl: 604800000
- api_keys:
- my_api_key:
- key: qwertyuiopasdfghjklzxcvbnm
-# role: readonly # readonly, readwrite, administrator
-# cidr: 0.0.0.0/0,::/0
-# retention:
-# searches: 10080 # 7 days, in minutes
-# transfers:
-# upload:
-# succeeded: 1440 # 1 day, in minutes
-# errored: 30
-# cancelled: 5
-# download:
-# succeeded: 1440 # 1 day, in minutes
-# errored: 20160 # 2 weeks, in minutes
-# cancelled: 5
-# files:
-# complete: 20160 # 2 weeks, in minutes
-# incomplete: 43200 # 30 days, in minutes
-# logs: 180 # days
-# logger:
-# disk: false
-# no_color: false
-# loki: ~
-# metrics:
-# enabled: false
-# url: /metrics
-# authentication:
-# disabled: false
-# username: slskd
-# password: slskd
-# feature:
-# swagger: false
-# soulseek:
-# address: vps.slsknet.org
-# port: 2271
-# username: ~
-# password: ~
-# description: |
-# A slskd user. https://github.com/slskd/slskd
-# listen_ip_address: 0.0.0.0
-# listen_port: 50300
-# diagnostic_level: Info
-# distributed_network:
-# disabled: false
-# disable_children: false
-# child_limit: 25
-# logging: false
-# connection:
-# timeout:
-# connect: 10000
-# inactivity: 15000
-# buffer:
-# read: 16384
-# write: 16384
-# transfer: 262144
-# write_queue: 250
-# proxy:
-# enabled: false
-# address: ~
-# port: ~
-# username: ~
-# password: ~
-# integration:
-# webhooks:
-# my_webhook:
-# on:
-# - DownloadFileComplete
-# call:
-# url: https://192.168.1.42:8080/slskd_webhook
-# headers:
-# - name: X-API-Key
-# value: foobar1234
-# - name: Authorization
-# value: Bearer eyJ...ssw5c
-# - name: User-Agent
-# value: slskd/0.0
-# ignore_certificate_errors: false
-# timeout: 5000 # in milliseconds
-# retry:
-# attempts: 3
-# scripts:
-# my_post_download_script:
-# on:
-# - DownloadFileComplete
-# - DownloadDirectoryComplete
-# run: data/my_script.sh --json-to-process $EVENT
-# my_logging_script:
-# on:
-# - All
-# run: data/log_slskd_events.sh $DATA
-# ftp:
-# enabled: false
-# address: ~
-# port: ~
-# username: ~
-# password: ~
-# remote_path: /
-# encryption_mode: auto
-# ignore_certificate_errors: false
-# overwrite_existing: true
-# connection_timeout: 5000
-# retry_attempts: 3
-# pushbullet:
-# enabled: false
-# access_token: ~
-# notification_prefix: "From slskd:"
-# notify_on_private_message: true
-# notify_on_room_mention: true
-# retry_attempts: 3
-# cooldown_time: 900000
diff --git a/stash/config/config.yml b/stash/config/config.yml
deleted file mode 100644
index 94f33f2..0000000
--- a/stash/config/config.yml
+++ /dev/null
@@ -1,245 +0,0 @@
-api_key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJjY2FsaWZpY2UiLCJzdWIiOiJBUElLZXkiLCJpYXQiOjE3NDE5NTY3OTN9.UWTnuuWhw0mFNQVWcXCer-NA8IyipxoGx0VieHW7XRQ
-blobs_path: /root/.stash/blobs
-blobs_storage: FILESYSTEM
-calculate_md5: false
-create_galleries_from_folders: true
-create_image_clip_from_videos: false
-dangerous_allow_public_without_auth: "false"
-database: /root/.stash/stash-go.sqlite
-defaults:
- auto_tag_task:
- paths: []
- performers:
- - '*'
- studios:
- - '*'
- tags:
- - '*'
- generate_task:
- clippreviews: false
- covers: true
- forcetranscodes: false
- imagepreviews: false
- imagethumbnails: true
- interactiveheatmapsspeeds: false
- markerids: []
- markerimagepreviews: false
- markers: false
- markerscreenshots: false
- overwrite: false
- phashes: true
- previewoptions:
- previewexcludeend: "0"
- previewexcludestart: "0"
- previewpreset: slow
- previewsegmentduration: 0.75
- previewsegments: 12
- previews: false
- sceneids: []
- sprites: false
- transcodes: false
- identify_task:
- options:
- fieldoptions:
- - createmissing: false
- field: title
- strategy: IGNORE
- - createmissing: true
- field: studio
- strategy: MERGE
- - createmissing: true
- field: performers
- strategy: MERGE
- - createmissing: true
- field: tags
- strategy: MERGE
- includemaleperformers: true
- setcoverimage: false
- setorganized: false
- skipmultiplematches: true
- skipmultiplematchtag: null
- skipsinglenameperformers: true
- skipsinglenameperformertag: null
- paths: []
- sceneids: []
- sources:
- - options:
- fieldoptions: []
- includemaleperformers: null
- setcoverimage: null
- setorganized: false
- skipmultiplematches: true
- skipmultiplematchtag: null
- skipsinglenameperformers: true
- skipsinglenameperformertag: null
- source:
- scraperid: builtin_autotag
- stashboxendpoint: null
- stashboxindex: null
- - options: null
- source:
- scraperid: null
- stashboxendpoint: https://stashdb.org/graphql
- stashboxindex: null
- scan_task:
- scangenerateclippreviews: true
- scangeneratecovers: true
- scangenerateimagepreviews: false
- scangeneratephashes: false
- scangeneratepreviews: true
- scangeneratesprites: false
- scangeneratethumbnails: true
-dlna:
- default_whitelist:
- - 192.168.1.80
-exclude: []
-gallery_cover_regex: (poster|cover|folder|board)\.[^\.]+$
-generated: ""
-host: 0.0.0.0
-image_exclude:
- - .*/f95/.*
-jwt_secret_key: 465db67dd7d0cbf0848283511375283970db6300c7c25ff57d5fcdc901116322
-menu_items:
- - scenes
- - images
- - groups
- - markers
- - galleries
- - performers
- - studios
- - tags
-no_proxy: localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12
-nobrowser: false
-notifications_enabled: false
-parallel_tasks: 0
-password: $2a$04$EcHNpS7.7GTpKNshc.QNQu53mbnVGomC5q9.YCkVPhVWBPquLIdoa
-plugins:
- package_sources:
- - localpath: community
- name: Community (stable)
- url: https://stashapp.github.io/CommunityScripts/stable/index.yml
- settings:
- DupFileManager:
- matchDupDistance: 1
- zzObsoleteSettingsCheckVer2: true
-plugins_path: /root/.stash/plugins
-port: 9999
-preview_audio: true
-preview_exclude_end: "0"
-preview_exclude_start: "0"
-preview_preset: medium
-preview_segment_duration: 0.75
-preview_segments: 12
-scrapers:
- package_sources:
- - localpath: community
- name: Community (stable)
- url: https://stashapp.github.io/CommunityScrapers/stable/index.yml
-scrapers_path: /root/.stash/scrapers
-security_tripwire_accessed_from_public_internet: ""
-sequential_scanning: false
-session_store_key: 5f2ae4ea161237c076aeb33c1f9a0e3f13a4d8092b3f76a669fe2e91e02f1d36
-show_one_time_moved_notification: false
-sound_on_preview: false
-stash:
- - excludeimage: false
- excludevideo: false
- path: /data/
-stash_boxes:
- - apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJhMmU5NTc3Ny0zOGEwLTQwMWYtYWMxNC01MjBkZmFjOWJhOTMiLCJzdWIiOiJBUElLZXkiLCJpYXQiOjE3NTk3NjAxMjV9.gOzykd_FyhgUNiircH9gE7S3nvLQrNMQ_26yzupVMkY
- endpoint: https://stashdb.org/graphql
- name: stash-box
-theme_color: '#202b33'
-ui:
- advancedMode: true
- frontPageContent:
- - __typename: CustomFilter
- direction: DESC
- message:
- id: recently_released_objects
- values:
- objects: Scenes
- mode: SCENES
- sortBy: date
- - __typename: CustomFilter
- direction: DESC
- message:
- id: recently_added_objects
- values:
- objects: Studios
- mode: STUDIOS
- sortBy: created_at
- - __typename: CustomFilter
- direction: DESC
- message:
- id: recently_released_objects
- values:
- objects: Movies
- mode: MOVIES
- sortBy: date
- - __typename: CustomFilter
- direction: DESC
- message:
- id: recently_added_objects
- values:
- objects: Performers
- mode: PERFORMERS
- sortBy: created_at
- - __typename: CustomFilter
- direction: DESC
- message:
- id: recently_released_objects
- values:
- objects: Galleries
- mode: GALLERIES
- sortBy: date
- lastNoteSeen: "20240826"
- pinnedFilters:
- scenes:
- - performers
- - tags
- taskDefaults:
- autoTag:
- performers:
- - '*'
- studios: []
- tags:
- - '*'
- cleanGenerated:
- blobFiles: true
- dryRun: false
- imageThumbnails: true
- markers: true
- screenshots: true
- sprites: true
- transcodes: true
- generate:
- clipPreviews: false
- covers: true
- imagePreviews: false
- imageThumbnails: true
- interactiveHeatmapsSpeeds: false
- markerImagePreviews: false
- markerScreenshots: false
- markers: false
- phashes: true
- previewOptions:
- previewExcludeEnd: "0"
- previewExcludeStart: "0"
- previewPreset: medium
- previewSegmentDuration: 0.75
- previewSegments: 12
- previews: false
- sprites: false
- transcodes: false
- scan:
- scanGenerateClipPreviews: true
- scanGenerateCovers: true
- scanGenerateImagePreviews: false
- scanGeneratePhashes: true
- scanGeneratePreviews: true
- scanGenerateSprites: false
- scanGenerateThumbnails: true
-username: ccalifice
-video_file_naming_algorithm: OSHASH
-write_image_thumbnails: true
diff --git a/stash/config/plugins/community/CommunityScriptsUILibrary/CommunityScriptsUILibrary.yml b/stash/config/plugins/community/CommunityScriptsUILibrary/CommunityScriptsUILibrary.yml
deleted file mode 100644
index 55074e8..0000000
--- a/stash/config/plugins/community/CommunityScriptsUILibrary/CommunityScriptsUILibrary.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: CommunityScriptsUILibrary
-description: CommunityScripts UI helper library
-version: 1.0.0
-ui:
- javascript:
- - cs-ui-lib.js
diff --git a/stash/config/plugins/community/CommunityScriptsUILibrary/README.md b/stash/config/plugins/community/CommunityScriptsUILibrary/README.md
deleted file mode 100644
index e641f69..0000000
--- a/stash/config/plugins/community/CommunityScriptsUILibrary/README.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# CommunityScripts UI Plugin library
-
-This is a set of slimmed down functions from StashUserscriptLibrary, targeting CommunityScripts originally introduced in [#319](https://github.com/stashapp/CommunityScripts/pull/319)
-
-
-## functions
-All the following functions are exposed under `window.csLib` and `csLib`
-
-## callGQL
-```js
-/**
- * This is a wrapped GraphQL (GQL) query caller
- * @param {...Object} reqData
- * @param {Object} reqData.query - GraphQL query
- * @param {Object}= reqData.variables - GraphQL variables
- * @returns {Object} - GQL response data with the `data` wrapper removed
- *
- * @example
- * // fetch the count of organized scenes
- * const filter = { organized: true };
- * const query = `query findScenes($filter: SceneFilter) { findScenes(filter: $filter) { count } }`;
- * const variables = { filter };
- * const response = await callGQL({ query, variables });
- * // response = { findScenes: { count: 3 } }
- */
-```
-
-## getConfiguration
-```js
-/**
- * Get configuration of a plugin from the server via GraphQL
- * @param {string} pluginId - The ID of the plugin as it is registered in the server
- * @param {*}= fallback - Fallback value if the configuration is not found. Defaults to an empty object
- * @returns {Object} - The configuration object of the plugin as it is stored in the server
- *
- * @example
- * // set default config
- * const defaultConfig = { enabled: true, theme: 'light' };
- * // fetch config from the server
- * const config = await getConfiguration('CommunityScriptsUIPlugin', defaultConfig);
- * // config = { theme: 'dark' }
- * // merge fetched with default config
- * const pluginConfig = {
- * ...defaultConfig
- * ...config
- * };
- * // pluginConfig = { enabled: true, theme: 'dark' }
- * }
- */
-```
-
-## setConfiguration
-```js
-/**
- * Set configuration of a plugin in the server via GraphQL
- * @param {string} pluginId - The ID of the plugin as it is registered in the server
- * @param {*} values - The configuration object with the values you want to save in the server
- * @returns {Object} - The configuration object of the plugin as it is stored in the server after update
- *
- * @example
- * // fetch config from the server
- * const config = await getConfiguration('CommunityScriptsUIPlugin', defaultConfig);
- * // config = { theme: 'dark' }
- * // update the config based on user input
- * // config = { theme: 'light' }
- * // save config in the server
- * await setConfiguration('CommunityScriptsUIPlugin', config);
- * }
- */
-```
-
-## waitForElement
-```js
- /**
- * Waits for an element to be available in the DOM and runs the callback function once it is
- * @param {string} selector - The CSS selector of the element to wait for
- * @param {function} callback - The function to be called once the element is available (with the element as an argument)
- * @returns
- *
- * @example
- * // wait for the element with the class 'my-element' to be available
- * // and change its color to red
- * function myCallback(el) {
- * el.style.color = 'red';
- * };
- * waitForElement('.my-element', myCallback);
- */
-```
-
-## pathElementListener
\ No newline at end of file
diff --git a/stash/config/plugins/community/CommunityScriptsUILibrary/cs-ui-lib.js b/stash/config/plugins/community/CommunityScriptsUILibrary/cs-ui-lib.js
deleted file mode 100644
index ba346c9..0000000
--- a/stash/config/plugins/community/CommunityScriptsUILibrary/cs-ui-lib.js
+++ /dev/null
@@ -1,141 +0,0 @@
-// CommunityScripts UI Library
-// cs-ui-lib.js
-(function () {
- // get base URL for graphQL queries
- const baseURL = document.querySelector("base")?.getAttribute("href") ?? "/";
-
- /**
- * This is a wrapped GraphQL (GQL) query caller
- * @param {...Object} reqData
- * @param {Object} reqData.query - GraphQL query
- * @param {Object}= reqData.variables - GraphQL variables
- * @returns {Object} - GQL response data with the `data` wrapper removed
- *
- * @example
- * // fetch the count of organized scenes
- * const filter = { organized: true };
- * const query = `query findScenes($filter: SceneFilter) { findScenes(filter: $filter) { count } }`;
- * const variables = { filter };
- * const response = await callGQL({ query, variables });
- * // response = { findScenes: { count: 3 } }
- */
- const callGQL = (reqData) =>
- fetch(`${baseURL}graphql`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(reqData),
- })
- .then((res) => res.json())
- .then((res) => res.data);
-
- /**
- * Get configuration of a plugin from the server via GraphQL
- * @param {string} pluginId - The ID of the plugin as it is registered in the server
- * @param {*}= fallback - Fallback value if the configuration is not found. Defaults to an empty object
- * @returns {Object} - The configuration object of the plugin as it is stored in the server
- *
- * @example
- * // set default config
- * const defaultConfig = { enabled: true, theme: 'light' };
- * // fetch config from the server
- * const config = await getConfiguration('CommunityScriptsUIPlugin', defaultConfig);
- * // config = { theme: 'dark' }
- * // merge fetched with default config
- * const pluginConfig = {
- * ...defaultConfig
- * ...config
- * };
- * // pluginConfig = { enabled: true, theme: 'dark' }
- * }
- */
- const getConfiguration = async (pluginId, fallback = {}) => {
- const query = `query Configuration { configuration { plugins }}`;
- const response = await callGQL({ query });
- return response.configuration.plugins?.[pluginId] ?? fallback;
- };
-
- /**
- * Set configuration of a plugin in the server via GraphQL
- * @param {string} pluginId - The ID of the plugin as it is registered in the server
- * @param {*} values - The configuration object with the values you want to save in the server
- * @returns {Object} - The configuration object of the plugin as it is stored in the server after update
- *
- * @example
- * // fetch config from the server
- * const config = await getConfiguration('CommunityScriptsUIPlugin', defaultConfig);
- * // config = { theme: 'dark' }
- * // update the config based on user input
- * // config = { theme: 'light' }
- * // save config in the server
- * await setConfiguration('CommunityScriptsUIPlugin', config);
- * }
- */
- const setConfiguration = async (pluginId, values) => {
- const query = `mutation ConfigurePlugin($pluginId: ID!, $input: Map!) { configurePlugin(plugin_id: $pluginId, input: $input) }`;
- const queryBody = {
- query: query,
- variables: {
- pluginId: pluginId,
- input: values,
- },
- };
- const response = await csLib.callGQL({ ...queryBody });
- return response.configurePlugin;
- };
-
- /**
- * Waits for an element to be available in the DOM and runs the callback function once it is
- * @param {string} selector - The CSS selector of the element to wait for
- * @param {function} callback - The function to be called once the element is available (with the element as an argument)
- * @returns
- *
- * @example
- * // wait for the element with the class 'my-element' to be available
- * // and change its color to red
- * function myCallback(el) {
- * el.style.color = 'red';
- * };
- * waitForElement('.my-element', myCallback);
- */
- function waitForElement(selector, callback) {
- var el = document.querySelector(selector);
- if (el) return callback(el);
- setTimeout(waitForElement, 100, selector, callback);
- }
-
- /**
- * Wait for a specific element to be available on a specific path
- * This combines the `waitForElement` and `PluginApi.Event.addEventListener` functions to only trigger on certain pages
- *
- * @param {string} path - The path to listen for
- * @param {string} element - The CSS selector of the element to wait for
- * @param {function} callback - The function to be called once the element is available (with the element as an argument)
- *
- * @example
- * // wait for the element with the class 'my-element' to be available, but only on the `/scene/#` path
- * function myCallback(el) {
- * el.style.color = 'red';
- * };
- * PathElementListener('/scene/', '.my-element', myCallback);
- * // this will only trigger the callback function when the user is on the `/scene/` path AND the element is available
- */
- const PathElementListener = (path, element, callback) => {
- // startup location
- if (window.location.pathname.startsWith(path))
- waitForElement(element, callback);
- PluginApi.Event.addEventListener("stash:location", (e) => {
- if (e.detail.data.location.pathname.startsWith(path))
- waitForElement(element, callback);
- });
- };
-
- // export to window
- window.csLib = {
- baseURL,
- callGQL,
- getConfiguration,
- setConfiguration,
- waitForElement,
- PathElementListener,
- };
-})();
diff --git a/stash/config/plugins/community/CommunityScriptsUILibrary/manifest b/stash/config/plugins/community/CommunityScriptsUILibrary/manifest
deleted file mode 100755
index 4e01abf..0000000
--- a/stash/config/plugins/community/CommunityScriptsUILibrary/manifest
+++ /dev/null
@@ -1,12 +0,0 @@
-id: CommunityScriptsUILibrary
-name: CommunityScriptsUILibrary
-metadata:
- description: CommunityScripts UI helper library
-version: 1.0.0-512fbb8
-date: "2024-11-27 21:50:31"
-requires: []
-source_repository: https://stashapp.github.io/CommunityScripts/stable/index.yml
-files:
-- cs-ui-lib.js
-- README.md
-- CommunityScriptsUILibrary.yml
diff --git a/stash/config/plugins/community/DupFileManager/DupFileManager.log b/stash/config/plugins/community/DupFileManager/DupFileManager.log
deleted file mode 100644
index 7cef01e..0000000
--- a/stash/config/plugins/community/DupFileManager/DupFileManager.log
+++ /dev/null
@@ -1,12 +0,0 @@
-[251006 10:33:16] [LN:95] INF: '******************* Starting *******************'
-[251006 10:33:17] [LN:1573] INF: "Sending json value {'LocalDupReportExist' : false, 'Path': '/root/.stash/plugins/DupFileManager/report/DuplicateTagScenes.html'}"
-[251006 10:33:17] [LN:1984] INF: '\n*********************************\nEXITING ***********************\n*********************************'
-[251006 10:33:17] [LN:95] INF: '******************* Starting *******************'
-[251006 10:33:17] [LN:1573] INF: "Sending json value {'LocalDupReportExist' : false, 'Path': '/root/.stash/plugins/DupFileManager/report/DuplicateTagScenes.html'}"
-[251006 10:33:17] [LN:1984] INF: '\n*********************************\nEXITING ***********************\n*********************************'
-[251006 10:35:25] [LN:95] INF: '******************* Starting *******************'
-[251006 10:35:25] [LN:1573] INF: "Sending json value {'LocalDupReportExist' : false, 'Path': '/root/.stash/plugins/DupFileManager/report/DuplicateTagScenes.html'}"
-[251006 10:35:25] [LN:1984] INF: '\n*********************************\nEXITING ***********************\n*********************************'
-[251006 10:35:26] [LN:95] INF: '******************* Starting *******************'
-[251006 10:35:26] [LN:1573] INF: "Sending json value {'LocalDupReportExist' : false, 'Path': '/root/.stash/plugins/DupFileManager/report/DuplicateTagScenes.html'}"
-[251006 10:35:26] [LN:1984] INF: '\n*********************************\nEXITING ***********************\n*********************************'
diff --git a/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_config.cpython-312.pyc b/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_config.cpython-312.pyc
deleted file mode 100644
index d8cf7c2..0000000
Binary files a/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_config.cpython-312.pyc and /dev/null differ
diff --git a/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_report_config.cpython-312.pyc b/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_report_config.cpython-312.pyc
deleted file mode 100644
index 61a654b..0000000
Binary files a/stash/config/plugins/community/DupFileManager/__pycache__/DupFileManager_report_config.cpython-312.pyc and /dev/null differ
diff --git a/stash/config/plugins/community/DupFileManager/__pycache__/ModulesValidate.cpython-312.pyc b/stash/config/plugins/community/DupFileManager/__pycache__/ModulesValidate.cpython-312.pyc
deleted file mode 100644
index f7c70e4..0000000
Binary files a/stash/config/plugins/community/DupFileManager/__pycache__/ModulesValidate.cpython-312.pyc and /dev/null differ
diff --git a/stash/config/plugins/community/DupFileManager/__pycache__/StashPluginHelper.cpython-312.pyc b/stash/config/plugins/community/DupFileManager/__pycache__/StashPluginHelper.cpython-312.pyc
deleted file mode 100644
index b15adee..0000000
Binary files a/stash/config/plugins/community/DupFileManager/__pycache__/StashPluginHelper.cpython-312.pyc and /dev/null differ
diff --git a/stash/config/plugins/community/LocalVisage/.gitignore b/stash/config/plugins/community/LocalVisage/.gitignore
deleted file mode 100644
index 1e3e14d..0000000
--- a/stash/config/plugins/community/LocalVisage/.gitignore
+++ /dev/null
@@ -1,179 +0,0 @@
-# Created by https://www.toptal.com/developers/gitignore/api/python
-# Edit at https://www.toptal.com/developers/gitignore?templates=python
-
-.deepface
-voy_db
-
-### Python ###
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# poetry
-# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-
-# pdm
-# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
-#pdm.lock
-# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
-# in version control.
-# https://pdm.fming.dev/#use-with-ide
-.pdm.toml
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# PyCharm
-# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
-# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-# and can be added to the global gitignore or merged into this file. For a more nuclear
-# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
-
-### Python Patch ###
-# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
-poetry.toml
-
-# ruff
-.ruff_cache/
-
-# LSP config files
-pyrightconfig.json
-
-# End of https://www.toptal.com/developers/gitignore/api/python
\ No newline at end of file
diff --git a/stash/config/plugins/community/LocalVisage/LocalVisage.py b/stash/config/plugins/community/LocalVisage/LocalVisage.py
deleted file mode 100644
index 1432ec9..0000000
--- a/stash/config/plugins/community/LocalVisage/LocalVisage.py
+++ /dev/null
@@ -1,371 +0,0 @@
-import os
-import sys
-import zipfile
-import tempfile
-from PythonDepManager import ensure_import
-# --- VENV AUTO-CREATION WITH REQUIREMENTS AND AUTO-RESTART ---
-venv_dir = os.path.join(os.path.dirname(__file__), "venv")
-requirements_path = os.path.join(os.path.dirname(__file__), "requirements.txt")
-# --- PYTHON VERSION CHECK ---
-
-if not os.path.isdir(venv_dir) and not (sys.version_info.major == 3 and sys.version_info.minor == 10):
- ensure_import("stashapi:stashapp-tools>=0.2.58")
- import stashapi.log as log
- log.error("Error: Python version must be >= 3.10.X (recommanded 3.10.11) for the first installation of the plugin. Once installed you can change back your python version in stash as this plugin will run within its own venv")
- log.error(f"Current version: {sys.version}")
- log.error("Go to https://www.python.org/downloads/release/python-31011/")
- sys.exit(1)
-# --- END PYTHON VERSION CHECK ---
-
-
-def in_venv():
- # Checks if running inside the venv we expect
- return (
- hasattr(sys, 'real_prefix') or
- (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)
- ) and os.path.abspath(sys.prefix) == os.path.abspath(venv_dir)
-def install_dependencies():
- """
- Install dependencies from requirements.txt if not already installed.
- """
- if not os.path.isfile(requirements_path):
- print("No requirements.txt found, skipping dependency installation.")
- return
-
- import subprocess
- pip_exe = os.path.join(venv_dir, "Scripts", "pip.exe") if os.name == "nt" else os.path.join(venv_dir, "bin", "pip")
- py_exe = os.path.join(venv_dir, "Scripts", "python.exe") if os.name == "nt" else os.path.join(venv_dir, "bin", "python")
- subprocess.check_call([py_exe,"-m","pip", "install", "--upgrade", "pip"])
- subprocess.check_call([pip_exe, "install", "-r", requirements_path])
-
-if not os.path.isdir(venv_dir):
-
- ensure_import("stashapi:stashapp-tools>=0.2.58")
- import stashapi.log as log
- import subprocess
- log.info("No venv found. Creating virtual environment...")
-
- subprocess.check_call([sys.executable, "-m", "venv", venv_dir])
- log.progress(0.25)
- log.info("Virtual environment created at "+ venv_dir)
- if os.path.isfile(requirements_path):
- log.info("Installing dependencies... This might take a while")
- install_dependencies()
- else:
- log.info("No requirements.txt found, skipping dependency installation.")
-
-# If not running in the venv, restart the script using the venv's Python
-if not in_venv():
- py_exe = os.path.join(venv_dir, "Scripts", "python.exe") if os.name == "nt" else os.path.join(venv_dir, "bin", "python")
- print(f"Restarting script in venv: {py_exe}")
- os.execv(py_exe, [py_exe] + sys.argv)
-# --- END VENV AUTO-CREATION WITH REQUIREMENTS AND AUTO-RESTART ---
-
-import json
-import subprocess
-import platform
-
-# Set environment variables
-os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0"
-os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # Suppress TF logs
-# Ensure dependencies
-
-try:
- from deepface import DeepFace
- import numpy as np
- import psutil
- import stashapi.log as log
- from stashapi.stashapp import StashInterface
-except:
- install_dependencies()
-
-from deepface import DeepFace
-import numpy as np
-import psutil
-import stashapi.log as log
-from stashapi.stashapp import StashInterface
-
-VOY_DB_PATH = os.path.join(os.path.dirname(__file__), "voy_db")
-os.makedirs(os.path.join(VOY_DB_PATH, "facenet"), exist_ok=True)
-os.makedirs(os.path.join(VOY_DB_PATH, "arc"), exist_ok=True)
-
-
-def main():
- """
- Main entry point for the plugin.
- """
- global stash
-
- json_input = json.loads(sys.stdin.read())
- stash = StashInterface(json_input["server_connection"])
- mode_arg = json_input["args"].get("mode")
- config = stash.get_configuration()["plugins"]
- settings = {"voyCount": 15, "sceneCount": 0, "imgCount": 0}
- if "LocalVisage" in config:
- settings.update(config["LocalVisage"])
-
- if mode_arg == "spawn_server":
- spawn_server(json_input["server_connection"])
- elif mode_arg == "stop_server":
- kill_stashface_server()
- elif mode_arg == "rebuild_model":
- rebuild_model(update_only=False, settings=settings)
- elif mode_arg == "update_model":
- rebuild_model(update_only=True, settings=settings)
-
-def can_read_image(image_path):
- """
- Check if an image path can be read, handling both regular files and files inside ZIP archives.
-
- Args:
- image_path (str): Path to the image file
-
- Returns:
- tuple: (can_read, actual_path) where can_read is bool and actual_path is the path to use
- """
- if os.path.exists(image_path):
- return True, image_path
-
- # Check if it's inside a ZIP file
- if ".zip" in image_path.lower():
- try:
- parts = image_path.split(".zip")
- if len(parts) >= 2:
- zip_path = parts[0] + ".zip"
- internal_path = parts[1].lstrip(os.sep + "/") # Remove leading separators
-
- if os.path.exists(zip_path):
- with zipfile.ZipFile(zip_path, 'r') as zip_file:
- # Check if the internal path exists in the ZIP
- if internal_path in zip_file.namelist():
- # Extract to temporary file and return temp path
- with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(internal_path)[1]) as tmp_file:
- tmp_file.write(zip_file.read(internal_path))
- return True, tmp_file.name
- except Exception as e:
- log.warning(f"Error reading from ZIP file {image_path}: {e}")
-
- return False, image_path
-
-def cleanup_temp_file(file_path):
- """
- Clean up temporary files created for ZIP extraction.
-
- Args:
- file_path (str): Path to the temporary file
- """
- try:
- if file_path.startswith(tempfile.gettempdir()):
- os.unlink(file_path)
- except Exception as e:
- log.warning(f"Error cleaning up temporary file {file_path}: {e}")
-
-
-def find_performers(settings):
- """
- Find performers with images for model building.
- """
- query={}
- # query performers based on sceneCount and imgCount settings
- scene_count_min = settings.get("sceneCount", 0)
- img_count_min = settings.get("imgCount", 0)
- if scene_count_min>0 or img_count_min>0:
- query={
- "scene_count": {"modifier": "GREATER_THAN", "value": scene_count_min-1},
- "image_count": {"modifier": "GREATER_THAN", "value": img_count_min-1},
- }
- performers_all = stash.find_performers(f=query, fragment="id name image_path custom_fields")
- performers_without_image = stash.find_performers(f={"is_missing": "image"}, fragment="id")
- performers_without_image_ids = {p["id"] for p in performers_without_image}
- performers_to_process = [p for p in performers_all if p["id"] not in performers_without_image_ids]
-
-
-
- performers_to_process = [
- p for p in performers_to_process
- if (p.get("scene_count", 0) >= scene_count_min and
- p.get("image_count", 0) >= img_count_min)
- ]
- return enrich_performers(performers_to_process, settings)
-
-def enrich_performers(performers, settings):
- """
- Add extra images to each performer for embedding calculation.
- """
- for progress, performer in enumerate(performers):
- performer["images"] = []
- if performer.get("image_path"):
- performer["images"].append(performer["image_path"])
- extra_images = stash.find_images(
- filter={
- "direction": "ASC",
- "page": 1,
- "per_page": settings.get("voyCount", 15) - 1,
- "q": "",
- "sort": "random_11365347"
- },
- f={
- "performer_count": {"modifier": "EQUALS", "value": 1},
- "performers": {"modifier": "INCLUDES_ALL", "value": [performer["id"]]},
- "path": {
- "modifier": "NOT_MATCHES_REGEX",
- "value": r".*\.(mp4|webm|avi|mov|mkv|flv|wmv|gif)$|.*[^\x00-\x7F].*"
- }
- }
- )
- for image in extra_images:
- if image.get("visual_files") and len(image["visual_files"]) > 0:
- image_path = image["visual_files"][0]["path"]
- can_read, actual_path = can_read_image(image_path)
- if can_read:
- performer["images"].append(actual_path)
- else:
- log.warning(f"Image path does not exist and cannot be read: {image_path}")
- else:
- log.warning(f"No visual files found for image ID: {image['id']}")
- log.progress((progress + 1) / len(performers))
- return performers
-
-def rebuild_model(update_only, settings):
- """
- Build or update the face embedding model for all performers.
- """
- log.info("Updating model..." if update_only else "Rebuilding model...")
- performers = find_performers(settings)
- if not performers:
- log.info("No performers found for model building.")
- return
-
- log.info("Database scraped, starting to rebuild model...")
- for progress, performer in enumerate(performers):
- embeddings_facenet = []
- embeddings_arc = []
- custom_fields = performer.get("custom_fields", {})
- images_used = custom_fields.get("number_of_images_used_for_voy", 0)
- if update_only and images_used >= settings["voyCount"]:
- continue
- if update_only and len(performer["images"]) <= images_used:
- continue
-
- for uri in performer["images"]:
- try:
- result_facenet = DeepFace.represent(
- img_path=uri,
- model_name="Facenet512",
- detector_backend='yolov8',
- normalization='Facenet2018',
- align=True,
- enforce_detection=False
- )
- embeddings_facenet.append(result_facenet[0]['embedding'])
- result_arc = DeepFace.represent(
- img_path=uri,
- model_name="ArcFace",
- detector_backend='yolov8',
- enforce_detection=False,
- align=True
- )
- embeddings_arc.append(result_arc[0]['embedding'])
- except Exception as e:
- log.warning(f"[WARN] Skipping {uri}: {e}")
- finally:
- # Clean up temporary files created for ZIP extraction
- cleanup_temp_file(uri)
-
- if embeddings_facenet and embeddings_arc:
- avg_embedding_facenet = np.mean(embeddings_facenet, axis=0).astype(np.float32)
- facenet_path = os.path.join(VOY_DB_PATH, "facenet", f"{performer['id']}-{performer['name']}.voy")
- np.save(facenet_path, avg_embedding_facenet)
- avg_embedding_arc = np.mean(embeddings_arc, axis=0).astype(np.float32)
- arc_path = os.path.join(VOY_DB_PATH, "arc", f"{performer['id']}-{performer['name']}.voy")
- np.save(arc_path, avg_embedding_arc)
- embeddings_count = max(len(embeddings_facenet), len(embeddings_arc))
- stash.update_performer({
- "id": performer["id"],
- "custom_fields": {
- "partial": {
- "number_of_images_used_for_voy": embeddings_count,
- }
- }
- })
- log.info(f"[INFO] Saved VOY for {performer['name']} with {embeddings_count} images.")
- else:
- log.warning(f"[WARN] No valid embeddings for {performer['name']}.")
- log.progress((progress + 1) / len(performers))
- log.info("Rebuilding model finished.")
- if server_running():
- kill_stashface_server()
- # Optionally, reload server with new connection info if needed
-
-def server_running():
- """
- Check if the stashface server is running.
- """
- try:
- for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
- name = proc.info.get('name', '').lower()
- cmdline_raw = proc.info.get('cmdline')
- if not cmdline_raw:
- continue
- cmdline = [str(arg).lower() for arg in cmdline_raw]
- if 'python' in name and any('stashface' in arg and 'app.py' in arg for arg in cmdline):
- log.debug("Stashface server is already running.")
- return True
- except psutil.NoSuchProcess:
- return False
- return False
-
-def kill_stashface_server():
- """
- Kill any running stashface server processes.
- """
- killed = False
- for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
- try:
- cmdline = proc.info['cmdline']
- if cmdline and any('stashface' in arg and 'app.py' in arg for arg in cmdline):
- log.debug(f"Killing process {proc.pid}: {' '.join(cmdline)}")
- proc.kill()
- killed = True
- except (psutil.NoSuchProcess, psutil.AccessDenied):
- continue
- if killed:
- log.info("Stashface server killed.")
-
-def spawn_server(server_connection=None):
- """
- Spawn the stashface server as a subprocess.
- """
- if server_running():
- log.info("Stashface server is already running.")
- return
- plugin_dir = os.path.dirname(__file__)
- py_exe = os.path.join(venv_dir, "Scripts", "python.exe") if os.name == "nt" else os.path.join(venv_dir, "bin", "python")
- cmd = [
- py_exe,
- os.path.abspath(os.path.join(plugin_dir, "stashface", "app.py")),
- ]
- log.info("Spawning server")
- env = os.environ.copy()
- if server_connection is not None:
- env["SERVER_CONNECTION"] = json.dumps(server_connection)
- if platform.system() == "Windows":
- subprocess.Popen(
- cmd,
- creationflags=subprocess.CREATE_NEW_CONSOLE,
- close_fds=True,
- cwd=plugin_dir,
- env=env
- )
- else:
- subprocess.Popen(
- cmd,
- start_new_session=True,
- close_fds=True,
- cwd=plugin_dir,
- env=env
- )
- log.info("Server spawned successfully, you can now use the plugin.")
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/stash/config/plugins/community/LocalVisage/LocalVisage.yml b/stash/config/plugins/community/LocalVisage/LocalVisage.yml
deleted file mode 100644
index fa0224d..0000000
--- a/stash/config/plugins/community/LocalVisage/LocalVisage.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-name: Local Visage
-description: Local Performer Recognition plugin using DeepFace
-# requires: PythonDepManager
-# requires: stashUserscriptLibrary7dJx1qP
-version: 1.0.1
-exec:
- - python
- - "{pluginDir}/LocalVisage.py"
-interface: raw
-ui:
- requires:
- - stashUserscriptLibrary7dJx1qP
- javascript:
- - frontend.js
- - https://cdn.jsdelivr.net/npm/@gradio/client@1.15.3/dist/index.js
- css:
- - frontend.css
- csp:
- connect-src:
- - http://localhost:7860
- - http://192.168.1.198:7860
- - http://your-server-ip-address:7860
- script-src:
- - https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js
- - https://cdn.jsdelivr.net/npm/@gradio/client@1.15.3/dist/index.js
-
-tasks:
- - name: Rebuild Face Recognition Model
- description: Rebuild the face recognition model entirely
- defaultArgs:
- mode: rebuild_model
- - name: Update Face Recognition Model
- description: Update the face performers model with new images if there model was built on less than "Target image count per voy" images
- defaultArgs:
- mode: update_model
- - name: Start server
- description: Start the face recognition server (if not started) to allow the plugin to work
- defaultArgs:
- mode: spawn_server
- - name: Stop server
- description: Stop the face recognition server
- defaultArgs:
- mode: stop_server
-
-settings:
- voyCount:
- displayName: Target image count per voy (default is 15)
- description: Number of images to to use to create the face recognition model (per performer)
- type: NUMBER
- imgCount:
- displayName: Minimum number of images for performer to be added to model
- description: Minimum number of images a performer must have to be included in recognition (EXCLUDING THE PERFORMER THUMBNAIL). Set to 0 for best result.
- type: NUMBER
- sceneCount:
- displayName: Minimum number of scenes for performer to be added to model
- description: Minimum number of scenes a performer must have to be included in recognition
- type: NUMBER
diff --git a/stash/config/plugins/community/LocalVisage/frontend.css b/stash/config/plugins/community/LocalVisage/frontend.css
deleted file mode 100644
index a423d17..0000000
--- a/stash/config/plugins/community/LocalVisage/frontend.css
+++ /dev/null
@@ -1,136 +0,0 @@
-button.svelte-localhjf {
- background-color: var(--nav-color);
- border: 0px;
-}
-.scanner.svelte-localhjf {
- animation: svelte-localhjf-pulse 2s infinite;
-}
-@keyframes svelte-localhjf-pulse {
- 0% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 var(--light);
- }
- 70% {
- transform: scale(1.1);
- box-shadow: 0 0 0 10px var(--info);
- }
- 100% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 var(--primary);
- }
-}
-svg.svelte-localhjf {
- fill: #ffffff;
-}
-button.svelte-localhjf {
- background-color: var(--nav-color);
- border: 0px;
-}
-.scanner.svelte-localhjf {
- animation: svelte-localhjf-pulse 2s infinite;
-}
-@keyframes svelte-localhjf-pulse {
- 0% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 var(--light);
- }
- 70% {
- transform: scale(1.1);
- box-shadow: 0 0 0 10px var(--info);
- }
- 100% {
- transform: scale(0.95);
- box-shadow: 0 0 0 0 var(--primary);
- }
-}
-svg.svelte-localhjf {
- fill: #ffffff;
-}
-.carousel.svelte-localhja {
- display: flex;
- overflow-x: auto;
- overflow-y: auto;
- white-space: nowrap;
- overscroll-behavior-x: contain;
- overscroll-behavior-y: contain;
- scroll-snap-type: x mandatory;
- gap: 1rem;
-}
-.modal-header.svelte-localhja {
- font-size: 2.4rem;
- border-bottom: 0px;
- padding: 10px 10px 0px 10px;
-}
-.modal-footer.svelte-localhja {
- border-top: 0px;
-}
-.svelte-localhja::-webkit-scrollbar {
- width: 30px;
-}
-.svelte-localhja::-webkit-scrollbar-thumb {
- background: var(--orange);
- border-radius: 20px;
-}
-.card.svelte-localhja {
- max-width: 78%;
-}
-.performer-card.svelte-localhja {
- cursor: pointer;
-}
-.performer-card-image .svelte-localhja {
- min-width: none !important;
- aspect-ratio: 4/5;
-}
-.assigned.svelte-localhja {
- border: 5px solid var(--green);
- animation: border 1s ease-in-out;
-}
-.face-tab.svelte-localhja {
- width: 50px;
- height: 50px;
- object-fit: cover;
-}
-.selected.svelte-localhjb {
- border: 2px solid #007bff;
-}
-.face-tabs.svelte-localhjb {
- position: absolute;
- flex: 0 0 450px;
- max-width: 450px;
- min-width: 450px;
- height: 100%;
- overflow: auto;
- order: -1;
- background-color: var(--body-color);
-}
-.face-item.svelte-localhjb {
- width: 160px;
- height: 90px;
- border-radius: 5px 5px 0px 0px;
- position: relative;
- cursor: pointer;
-}
-.svelte-tabs__tab.svelte-localhjc {
- border: none;
- border-bottom: 2px solid transparent;
- color: #000000;
- cursor: pointer;
- list-style: none;
- display: inline-block;
- padding: 0.5em 0.75em;
-}
-.svelte-tabs__tab.svelte-localhjc:focus {
- outline: thin dotted;
-}
-.svelte-tabs__selected.svelte-localhjc {
- border-bottom: 2px solid #4f81e5;
- color: #4f81e5;
-}
-.svelte-tabs__tab-panel.svelte-lcocalhjd {
- margin-top: 0.5em;
-}
-.svelte-tabs__tab-list.svelte-localhje {
- border-bottom: 1px solid #cccccc;
- margin: 0;
- padding: 0;
-}
diff --git a/stash/config/plugins/community/LocalVisage/frontend.js b/stash/config/plugins/community/LocalVisage/frontend.js
deleted file mode 100644
index e275575..0000000
--- a/stash/config/plugins/community/LocalVisage/frontend.js
+++ /dev/null
@@ -1,14579 +0,0 @@
-(function () {
- "use strict";
- // Using CDN version of @gradio/client to avoid bundling issues
- // import { Client, handle_file } from "@gradio/client";
-
- let VISAGE_API_URL =
- window.location.protocol + "//" + window.location.hostname + ":7860"; // URL of the Visage API
- let THRESHOLD = 0; // remove matches with a distance higher than this
- let MAX_RESULTS = 5; // number of results to show, don't change this for now
-
- function waitForElm(selector) {
- return new Promise((resolve) => {
- if (document.querySelector(selector)) {
- return resolve(document.querySelector(selector));
- }
-
- const observer = new MutationObserver((mutations) => {
- if (document.querySelector(selector)) {
- resolve(document.querySelector(selector));
- observer.disconnect();
- }
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- });
- });
- }
-
- function getScenarioAndID() {
- var result = document.URL.match(/(scenes|images)\/(\d+)/);
- var scenario = result[1];
- var scenario_id = result[2];
- return [scenario, scenario_id];
- }
-
- async function startServer() {
- const reqData = {
- query: `mutation{
- runPluginTask(plugin_id:"LocalVisage",task_name:"Start server")
-}`,
- };
- await stash7dJx1qP.stash.callGQL(reqData);
- }
-
- async function getPerformersForScene(scene_id) {
- const reqData = {
- query: `{
- findScene(id: "${scene_id}") {
- performers {
- id
- }
- }
- }`,
- };
- var result = await stash7dJx1qP.stash.callGQL(reqData);
- return result.data.findScene.performers.map((p) => p.id);
- }
-
- async function getPerformersForImage(image_id) {
- const reqData = {
- query: `{
- findImage(id: "${image_id}") {
- performers {
- id
- }
- }
- }`,
- };
- var result = await stash7dJx1qP.stash.callGQL(reqData);
- return result.data.findImage.performers.map((p) => p.id);
- }
-
- async function updateScene(scene_id, performer_ids) {
- const reqData = {
- variables: { input: { id: scene_id, performer_ids: performer_ids } },
- query: `mutation sceneUpdate($input: SceneUpdateInput!){
- sceneUpdate(input: $input) {
- id
- }
- }`,
- };
- return stash7dJx1qP.stash.callGQL(reqData);
- }
-
- async function updateImage(image_id, performer_ids) {
- const reqData = {
- variables: { input: { id: image_id, performer_ids: performer_ids } },
- query: `mutation imageUpdate($input: ImageUpdateInput!){
- imageUpdate(input: $input) {
- id
- }
- }`,
- };
- return stash7dJx1qP.stash.callGQL(reqData);
- }
-
- async function getStashboxEndpoint() {
- const reqData = {
- query: `{
- configuration {
- general {
- stashBoxes {
- endpoint
- }
- }
- }
- }`,
- };
- var result = await stash7dJx1qP.stash.callGQL(reqData);
- return result.data.configuration.general.stashBoxes[0].endpoint;
- }
-
- async function getPerformerDataFromStashID(stash_id) {
- const reqData = {
- variables: {
- source: {
- stash_box_index: 0,
- },
- input: {
- query: stash_id,
- },
- },
- query: `query ScrapeSinglePerformer($source: ScraperSourceInput!, $input: ScrapeSinglePerformerInput!) {
- scrapeSinglePerformer(source: $source, input: $input) {
- name
- disambiguation
- gender
- url
- twitter
- instagram
- birthdate
- ethnicity
- country
- eye_color
- height
- measurements
- fake_tits
- career_length
- tattoos
- piercings
- aliases
- images
- details
- death_date
- hair_color
- weight
- remote_site_id
- }
- }`,
- };
- var result = await stash7dJx1qP.stash.callGQL(reqData);
- return result.data.scrapeSinglePerformer.filter(
- (p) => p.remote_site_id === stash_id
- )[0];
- }
-
- async function createPerformer(performer) {
- const reqData = {
- variables: { input: performer },
- query: `mutation performerCreate($input: PerformerCreateInput!) {
- performerCreate(input: $input){
- id
- }
- }`,
- };
- return stash7dJx1qP.stash.callGQL(reqData);
- }
-
- let gradioClientModule = null;
- let gradioClient = null;
-
- /**
- * Loads the Gradio client from CDN
- * @returns {Promise