├── .shellcheckrc ├── LICENSE ├── README.md ├── build ├── core ├── debug ├── docs ├── arm.sh ├── cf.sh ├── ibm.sh ├── index.md ├── iptv.sh ├── nx.sh ├── or.sh ├── pve.sh ├── v2.sh └── x.sh ├── env ├── i18n ├── .gitignore ├── README.md ├── iptv.sh.pot ├── make-pot.sh └── po │ └── iptv.sh-en.mo ├── iptv.sh ├── make ├── scripts ├── 460.32.04.patch ├── Add-SVT-HEVC-FLV-support-on-FFmpeg-git.patch ├── Add-SVT-HEVC-FLV-support-on-FFmpeg-release.patch ├── Add-SVT-HEVC-support-for-RTMP-and-HLS-on-Nginx-HTTP-FLV.patch ├── Dockerfile ├── NVIDIA-Linux-x86_64-460.32.04-vgpu-kvm.run ├── README.md ├── browser.js ├── build.sh ├── docker │ ├── .env │ ├── certs │ │ └── openssl.cnf │ ├── data │ │ ├── authelia │ │ │ ├── config │ │ │ │ ├── configuration.yml │ │ │ │ └── users_database.yml │ │ │ └── secrets │ │ │ │ ├── JWT_SECRET │ │ │ │ ├── SESSION_REDIS_PASSWORD │ │ │ │ ├── SESSION_SECRET │ │ │ │ ├── STORAGE_ENCRYPTION_KEY │ │ │ │ └── STORAGE_POSTGRES_PASSWORD │ │ ├── mi-gpt │ │ │ ├── .env │ │ │ └── .migpt.js │ │ └── traefik │ │ │ └── config │ │ │ ├── dynamic.yml │ │ │ └── traefik.yml │ └── docker-compose.yml ├── fengshows_proxy.js ├── fix_ngx_lua_resp_get_headers_key_whitespace.patch ├── md5sum.c ├── node-libcurl-impersonate.patch ├── node_xtream_codes_proxy.js ├── stream_proxy.js └── xtream_codes_proxy.js ├── src ├── _4gtv │ ├── add_link │ ├── cron │ ├── del_acc │ ├── disable_cron │ ├── edit_acc │ ├── enable_cron │ ├── get_acc_token │ ├── list_acc │ ├── list_accs │ ├── login_acc │ ├── reg_acc │ ├── set_acc │ ├── start_link │ └── use_proxy ├── ali ├── alist │ ├── acc │ ├── act │ ├── api │ └── app ├── arm ├── calibre │ ├── install │ ├── kcc │ └── web ├── cf ├── cloudflare │ ├── add_host │ ├── add_subdomain │ ├── add_token │ ├── add_user │ ├── add_worker │ ├── add_zone │ ├── config_route │ ├── delete_host │ ├── delete_user │ ├── delete_worker │ ├── delete_zone │ ├── deploy_worker │ ├── edit_user │ ├── edit_worker │ ├── edit_zone │ ├── get_hosts │ ├── get_user │ ├── get_users │ ├── get_workers │ ├── get_zone │ ├── list_host │ ├── list_hosts │ ├── list_routes │ ├── list_subdomain │ ├── list_token │ ├── list_user │ ├── list_users │ ├── list_worker │ ├── list_workers │ ├── list_zone │ ├── list_zones │ ├── menu_partner │ ├── menu_workers │ ├── monitor_workers │ ├── move_zone │ ├── regen_host │ ├── set_host │ ├── set_user │ ├── set_worker │ ├── set_zone │ └── update_token ├── cw ├── cx ├── docker │ ├── crt │ ├── install │ └── service ├── dr ├── ha ├── haproxy │ └── install ├── hk ├── ibm ├── ibm_cf │ ├── add_app │ ├── add_route │ ├── add_user │ ├── delete_app │ ├── delete_route │ ├── delete_user │ ├── deploy_v2ray │ ├── disable_cron │ ├── download_v2ray │ ├── edit_user │ ├── enable_cron │ ├── exec_cron │ ├── get_api │ ├── get_apps │ ├── get_users │ ├── list_app │ ├── list_apps │ ├── list_users │ ├── login_user │ ├── menu_cf │ ├── menu_v2ray │ ├── set_app │ ├── set_cron │ ├── set_user │ ├── update_app │ ├── update_v2ray │ └── update_v2ray_config ├── iptv │ ├── add_channel │ ├── add_schedule │ ├── check_xtream │ ├── cmd_a │ ├── cmd_add │ ├── cmd_astro │ ├── cmd_backup │ ├── cmd_c │ ├── cmd_curl │ ├── cmd_debug │ ├── cmd_default │ ├── cmd_e │ ├── cmd_ed │ ├── cmd_ee │ ├── cmd_list │ ├── cmd_monitor │ ├── cmd_singtel │ ├── delete_channel │ ├── delete_schedule │ ├── edit_channel │ ├── edit_default │ ├── edit_schedule │ ├── filter_string │ ├── flv_creator │ ├── get_channel │ ├── get_channels │ ├── get_default │ ├── hls_creator │ ├── input_channels │ ├── list_channel │ ├── list_channels │ ├── list_monitor │ ├── list_schedule │ ├── menu_4gtv │ ├── menu_color │ ├── menu_edit_channel │ ├── menu_edit_default │ ├── menu_iptv │ ├── menu_listings │ ├── menu_monitor │ ├── menu_schedule │ ├── menu_ts │ ├── menu_vip │ ├── menu_vip_user │ ├── parse_hls │ ├── parse_schedule │ ├── parse_stream │ ├── parse_youtube │ ├── rand_name │ ├── restart_channel │ ├── search_soccer │ ├── set_stream │ ├── sort_schedule │ ├── start_channel │ ├── start_monitor │ ├── stop_channel │ ├── sync_file │ ├── toggle_channel │ ├── update_self │ ├── usage │ ├── view_channel │ └── view_log ├── lhh ├── nginx │ ├── add_cors │ ├── add_directive │ ├── add_domain │ ├── add_enabled │ ├── add_flv │ ├── add_http │ ├── add_localhost │ ├── add_nodejs │ ├── add_rtmp │ ├── add_samesite_none │ ├── add_ssl │ ├── add_stream │ ├── add_upstream_nodejs │ ├── add_user │ ├── append_conf │ ├── build_conf │ ├── check_acme │ ├── check_localhost │ ├── config_block │ ├── config_directive │ ├── config_dnscrypt │ ├── config_domain │ ├── config_localhost │ ├── config_mmproxy │ ├── config_nodejs │ ├── config_postfix │ ├── config_server │ ├── delete_domain │ ├── disable_domain │ ├── enable_domain │ ├── get_config │ ├── get_stream │ ├── input_args │ ├── install │ ├── is_block_directive │ ├── list_domain │ ├── list_domains │ ├── list_localhost │ ├── list_stream │ ├── menu_mongodb │ ├── menu_nodejs │ ├── parse_config │ ├── restart │ ├── rotate_log │ ├── select_domain │ ├── select_domain_server │ ├── select_localhost_server │ ├── sites_crt │ ├── toggle │ ├── toggle_domain │ ├── update_cert │ ├── update_ip │ └── view_status ├── nx ├── openresty │ └── install ├── or ├── proxmox │ ├── get_vms │ ├── list_vms │ └── select_vm ├── pve ├── rc ├── rclone │ ├── config │ ├── mount │ ├── remote │ ├── serve │ └── sync ├── service │ ├── base64 │ ├── browse │ ├── control │ ├── get │ └── obscure ├── smb ├── test │ ├── core │ └── inquirer ├── tr ├── tt ├── tv ├── v2 ├── v2ray │ ├── add_inbound │ ├── add_inbound_account │ ├── add_nginx_proxy │ ├── add_outbound │ ├── add_outbound_account │ ├── config_domain │ ├── delete_inbound │ ├── delete_inbound_account │ ├── delete_outbound │ ├── delete_outbound_account │ ├── get_dns │ ├── get_free_tag │ ├── get_inbounds │ ├── get_outbounds │ ├── get_policy │ ├── get_reverse │ ├── get_routing │ ├── get_stats │ ├── get_traffic │ ├── inbound │ ├── install │ ├── list_dns │ ├── list_inbound_accounts │ ├── list_inbound_domains │ ├── list_inbound_share │ ├── list_inbounds │ ├── list_nginx_domain │ ├── list_nginx_domains │ ├── list_outbound_accounts │ ├── list_outbounds │ ├── list_policy │ ├── list_reverse │ ├── list_routing │ ├── list_stats │ ├── log │ ├── menu │ ├── proxy │ ├── reset_stats │ ├── select_account │ ├── select_inbound │ ├── select_nginx_domain │ ├── select_nginx_proxy │ ├── select_nginx_server │ ├── select_outbound │ ├── set_bound │ ├── set_dns │ ├── set_policy │ ├── set_proxy │ ├── set_reverse │ ├── set_routing │ ├── set_stream │ ├── stream │ ├── update_cert │ ├── update_nginx_cert │ └── view_staus ├── vip │ ├── add_channel │ ├── add_host │ ├── add_user │ ├── delete_channel │ ├── delete_host │ ├── delete_user │ ├── deploy_channel │ ├── disable │ ├── edit_channel │ ├── edit_host │ ├── edit_user │ ├── enable │ ├── get_config │ ├── get_hosts │ ├── get_schedules │ ├── get_stream │ ├── get_users │ ├── list_channel │ ├── list_channels │ ├── list_hosts │ ├── list_user │ ├── list_user_channel │ ├── list_users │ ├── monitor │ ├── process_lists │ ├── set_channel │ ├── set_host │ ├── set_public │ ├── set_user │ └── verify_license ├── x ├── xray │ └── install └── xtream_codes │ ├── add_account │ ├── add_mac │ ├── domain_filter │ ├── get_channels │ ├── get_domains │ ├── list │ ├── list_account │ ├── list_channels │ ├── list_mac │ ├── search_channels │ ├── test_account │ └── verify_mac ├── utils ├── apt ├── build ├── cookie ├── crossplane ├── curl ├── dnscrypt ├── ffmpeg ├── git ├── go ├── htpasswd ├── i18n ├── imagemagick ├── imgcat ├── inquirer ├── iperf ├── jq ├── log ├── lua ├── mirror ├── mongodb ├── node ├── nodejs ├── openssl ├── pdf2html ├── postfix ├── postgresql ├── progress ├── python ├── quictls ├── shfile ├── spinner ├── swap ├── system ├── tesseract ├── vim ├── wrangler ├── youtube ├── yq └── zlib └── ver /.shellcheckrc: -------------------------------------------------------------------------------- 1 | external-sources=true 2 | check-sourced=true 3 | disable=SC2292,SC2250 4 | -------------------------------------------------------------------------------- /debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | DEV_PATH=$(dirname "$0") 6 | 7 | Error() 8 | { 9 | echo 10 | exit 1 11 | } 12 | 13 | Include() 14 | { 15 | INCLUDE_PATH="$DEV_PATH" 16 | include_name="$1" 17 | 18 | while [[ $include_name =~ \/ ]] 19 | do 20 | INCLUDE_PATH="$INCLUDE_PATH/${include_name%%/*}" 21 | include_name="${include_name#*/}" 22 | done 23 | 24 | shift 25 | 26 | # shellcheck disable=SC1090 27 | . "$INCLUDE_PATH/$include_name" "$@" || Error include $? 28 | } 29 | 30 | Include ver 31 | 32 | Include env 33 | 34 | Include core "$@" 35 | 36 | Include utils/i18n "$@" 37 | 38 | if [ -z "$*" ] 39 | then 40 | echo 41 | debug_options=( '编译' '测试' ) 42 | inquirer list_input_index "选择操作" debug_options debug_options_index 43 | 44 | if [ "$debug_options_index" -eq 0 ] 45 | then 46 | echo 47 | inquirer list_input_index "是否部署" ny_options ny_index 48 | 49 | if [ "$ny_index" -eq 0 ] 50 | then 51 | "$DEV_PATH"/make 52 | else 53 | "$DEV_PATH"/make install 54 | fi 55 | 56 | exit 0 57 | elif [ "$debug_options_index" -eq 1 ] 58 | then 59 | self_options=() 60 | for file in "$DEV_PATH"/src/* 61 | do 62 | if [ ! -d "$file" ] 63 | then 64 | self_options+=("${file##*/}") 65 | fi 66 | done 67 | 68 | echo 69 | inquirer list_input "选择测试的程序" self_options self 70 | 71 | echo 72 | inquirer text_input "输入参数" args_input "$i18n_blank" 73 | 74 | if [ -z "$args_input" ] 75 | then 76 | args=() 77 | else 78 | IFS= read -ra args <<< "$args_input" 79 | fi 80 | fi 81 | else 82 | self=$1 83 | shift 84 | args=("${@:-}") 85 | fi 86 | 87 | Include src/"$self" "${args[@]:-}" 88 | -------------------------------------------------------------------------------- /docs/arm.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/cf.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/ibm.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/docs/index.md -------------------------------------------------------------------------------- /docs/nx.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/or.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/pve.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/v2.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /docs/x.sh: -------------------------------------------------------------------------------- 1 | iptv.sh -------------------------------------------------------------------------------- /i18n/.gitignore: -------------------------------------------------------------------------------- 1 | iptv.sh-*.pot 2 | iptv.sh-en.po 3 | -------------------------------------------------------------------------------- /i18n/README.md: -------------------------------------------------------------------------------- 1 | # Translating 2 | 3 | ``` bash 4 | git clone https://github.com/woniuzfb/iptv.git 5 | cd iptv/i18n 6 | 7 | - DO NOT edit/remove iptv.sh.pot # if you are a native English speaker and want to help (see bottom) 8 | 9 | ./make-pot.sh [pot-language (en, zh_CN)] [po-language (ru, de ...)] 10 | 11 | e.g. 12 | ./make-pot.sh en ru # translating English to ru 13 | ./make-pot.sh zh_CN ru # if you prefer translating Chinese to ru 14 | 15 | using the editor of your choice: 16 | poedit po/iptv.sh-ru.po # iptv.sh-ru.po under directory po 17 | 18 | ./make-pot.sh zh_CN ru # run this when you are done 19 | 20 | PR po/iptv.sh-ru.po 21 | ``` 22 | 23 | ## PR iptv.sh.pot 24 | 25 | - if you are a native English speaker 26 | 27 | ``` bash 28 | using the editor of your choice: 29 | poedit iptv.sh.pot # EDIT msgid only 30 | 31 | PR iptv.sh.pot 32 | ``` 33 | -------------------------------------------------------------------------------- /i18n/po/iptv.sh-en.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/i18n/po/iptv.sh-en.mo -------------------------------------------------------------------------------- /make: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Docker / HAProxy / Rclone / Alist / Calibre / FFmpeg / 3 | # Nginx / Openresty / V2ray / Xray / Armbian / PVE / 4 | # Samba / LianHuanHua / ... 5 | # Copyright (C) 2019-2025 6 | # Released under GPL Version 3 License 7 | 8 | set -euo pipefail 9 | 10 | DEV_PATH=$(dirname "$0") 11 | 12 | Error() 13 | { 14 | echo 15 | exit 1 16 | } 17 | 18 | Include() 19 | { 20 | INCLUDE_PATH="$DEV_PATH" 21 | include_name="$1" 22 | 23 | while [[ $include_name =~ \/ ]] 24 | do 25 | INCLUDE_PATH="$INCLUDE_PATH/${include_name%%/*}" 26 | include_name="${include_name#*/}" 27 | done 28 | 29 | shift 30 | 31 | # shellcheck disable=SC1090 32 | . "$INCLUDE_PATH/$include_name" "$@" || Error include $? 33 | } 34 | 35 | Include ver 36 | 37 | Include env 38 | 39 | Include core "$@" 40 | 41 | Include utils/i18n "$@" 42 | 43 | Include build "$@" 44 | 45 | if [[ -x $(command -v readlink) ]] && [ -L "$0" ] && alternative=$(readlink "$0") && [ -L "$alternative" ] 46 | then 47 | self=${alternative##*/} 48 | else 49 | self=${0##*/} 50 | fi 51 | 52 | self=${self%.*} 53 | 54 | Include src/$self "$@" 55 | -------------------------------------------------------------------------------- /scripts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | # remove sed command ? 4 | RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \ 5 | && apt-get update && apt-get install -y \ 6 | autoconf \ 7 | automake \ 8 | build-essential \ 9 | cmake \ 10 | zlib1g-dev \ 11 | libtool \ 12 | pkg-config \ 13 | texinfo \ 14 | frei0r-plugins-dev \ 15 | libopencore-amrnb-dev \ 16 | libopencore-amrwb-dev \ 17 | libtheora-dev \ 18 | libvo-amrwbenc-dev \ 19 | libxvidcore-dev \ 20 | libssl-dev \ 21 | libva-dev \ 22 | libvdpau-dev \ 23 | libxcb1-dev \ 24 | libxcb-shm0-dev \ 25 | libxcb-xfixes0-dev \ 26 | flex \ 27 | bison \ 28 | libharfbuzz-dev \ 29 | libfontconfig-dev \ 30 | libfreetype6-dev \ 31 | python3 \ 32 | python3-pip \ 33 | python3-setuptools \ 34 | python3-wheel \ 35 | ninja-build \ 36 | doxygen \ 37 | git \ 38 | libxext-dev \ 39 | libsndfile1-dev \ 40 | libasound2-dev \ 41 | curl \ 42 | graphviz && rm -rf /var/lib/apt/lists/* 43 | 44 | COPY build.sh /root/ffmpeg_sources/ 45 | 46 | VOLUME /root/ffmpeg_sources/ 47 | WORKDIR /root/ffmpeg_sources/ 48 | CMD /bin/bash 49 | -------------------------------------------------------------------------------- /scripts/NVIDIA-Linux-x86_64-460.32.04-vgpu-kvm.run: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/NVIDIA-Linux-x86_64-460.32.04-vgpu-kvm.run -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # stream_proxy.js 2 | 3 | ``` bash 4 | 5 | ``` 6 | 7 | ## xtream_codes_proxy.js 8 | 9 | ``` bash 10 | 11 | ``` 12 | -------------------------------------------------------------------------------- /scripts/docker/.env: -------------------------------------------------------------------------------- 1 | TRAEFIK_DOMAIN=example.com 2 | AUTHELIA_DOMAIN=example.com 3 | -------------------------------------------------------------------------------- /scripts/docker/data/authelia/config/users_database.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ############################################################### 3 | # Users Database # 4 | ############################################################### 5 | 6 | # This file can be used if you do not have an LDAP set up. 7 | 8 | # List of users 9 | users: 10 | : 11 | disabled: false 12 | displayname: "" 13 | password: "" 14 | # # Password is authelia 15 | # password: "$6$rounds=50000$BpLnfgDsc2WD8F2q$Zis.ixdg9s/UOJYrs56b5QEZFiZECu0qZVNsIYxBaNJ7ucIL.nlxVCT5tqh8KHG8X4tlwCFm5r6NTOZZ5qRFN/" 16 | email: '@{{ env "DOMAIN" }}' 17 | groups: 18 | - admins 19 | - dev 20 | ... 21 | -------------------------------------------------------------------------------- /scripts/docker/data/authelia/secrets/JWT_SECRET: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/docker/data/authelia/secrets/JWT_SECRET -------------------------------------------------------------------------------- /scripts/docker/data/authelia/secrets/SESSION_REDIS_PASSWORD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/docker/data/authelia/secrets/SESSION_REDIS_PASSWORD -------------------------------------------------------------------------------- /scripts/docker/data/authelia/secrets/SESSION_SECRET: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/docker/data/authelia/secrets/SESSION_SECRET -------------------------------------------------------------------------------- /scripts/docker/data/authelia/secrets/STORAGE_ENCRYPTION_KEY: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/docker/data/authelia/secrets/STORAGE_ENCRYPTION_KEY -------------------------------------------------------------------------------- /scripts/docker/data/authelia/secrets/STORAGE_POSTGRES_PASSWORD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/scripts/docker/data/authelia/secrets/STORAGE_POSTGRES_PASSWORD -------------------------------------------------------------------------------- /scripts/docker/data/mi-gpt/.env: -------------------------------------------------------------------------------- 1 | OPENAI_MODEL = Blackbox 2 | OPENAI_API_KEY= 3 | OPENAI_BASE_URL= 4 | -------------------------------------------------------------------------------- /scripts/docker/data/traefik/config/traefik.yml: -------------------------------------------------------------------------------- 1 | providers: 2 | docker: {} 3 | file: 4 | directory: /etc/traefik 5 | filename: dynamic.yml 6 | watch: true 7 | 8 | log: 9 | level: ERROR 10 | filePath: /var/log/traefik/traefik.log 11 | 12 | entryPoints: 13 | web: 14 | address: :80 15 | proxyProtocol: 16 | insecure: false 17 | trustedIPs: 18 | - 10.0.0.0/8 19 | - 172.16.0.0/12 20 | - 192.168.0.0/16 21 | - fc00::/7 22 | forwardedHeaders: 23 | insecure: false 24 | trustedIPs: 25 | - 10.0.0.0/8 26 | - 172.16.0.0/12 27 | - 192.168.0.0/16 28 | - fc00::/7 29 | 30 | websecure: 31 | address: :443 32 | proxyProtocol: 33 | insecure: false 34 | trustedIPs: 35 | - 10.0.0.0/8 36 | - 172.16.0.0/12 37 | - 192.168.0.0/16 38 | - fc00::/7 39 | forwardedHeaders: 40 | insecure: false 41 | trustedIPs: 42 | - 10.0.0.0/8 43 | - 172.16.0.0/12 44 | - 192.168.0.0/16 45 | - fc00::/7 46 | 47 | api: 48 | dashboard: true 49 | insecure: true 50 | 51 | testData: 52 | code: 200 53 | method: OPTIONS 54 | 55 | experimental: 56 | localPlugins: 57 | CorsPreflight: 58 | modulename: github.com/Medzoner/traefik-plugin-cors-preflight 59 | -------------------------------------------------------------------------------- /scripts/fix_ngx_lua_resp_get_headers_key_whitespace.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/ngx_http_lua_headers.c b/src/ngx_http_lua_headers.c 2 | index 85836a1..ea526af 100644 3 | --- a/src/ngx_http_lua_headers.c 4 | +++ b/src/ngx_http_lua_headers.c 5 | @@ -429,7 +429,7 @@ ngx_http_lua_ngx_resp_get_headers(lua_State *L) 6 | ngx_http_lua_ctx_t *ctx; 7 | u_char *lowcase_key = NULL; 8 | size_t lowcase_key_sz = 0; 9 | - ngx_uint_t i; 10 | + ngx_uint_t i, j, k; 11 | int n; 12 | int max; 13 | int raw = 0; 14 | @@ -568,6 +568,29 @@ ngx_http_lua_ngx_resp_get_headers(lua_State *L) 15 | continue; 16 | } 17 | 18 | + for (j = k = 0; j < header[i].key.len; j++, k++) 19 | + { 20 | + if (k == header[i].key.len) 21 | + { 22 | + header[i].key.data[j] = '\0'; 23 | + header[i].key.len = j; 24 | + break; 25 | + } 26 | + 27 | + if (header[i].key.data[j] == ' ') 28 | + { 29 | + if (k == header[i].key.len - 1) 30 | + { 31 | + header[i].key.data[j] = '\0'; 32 | + header[i].key.len = j; 33 | + break; 34 | + } 35 | + k++; 36 | + } 37 | + 38 | + header[i].key.data[j] = header[i].key.data[k]; 39 | + } 40 | + 41 | if (raw) { 42 | lua_pushlstring(L, (char *) header[i].key.data, header[i].key.len); 43 | 44 | -------------------------------------------------------------------------------- /src/_4gtv/del_acc: -------------------------------------------------------------------------------- 1 | Del4gtvAcc() 2 | { 3 | List4gtvAccs 4 | 5 | if [ "$_4gtv_accs_count" -eq 0 ] 6 | then 7 | exit 1 8 | fi 9 | 10 | echo -e "选择账号" 11 | while read -p "$i18n_default_cancel" _4gtv_accs_num 12 | do 13 | case "$_4gtv_accs_num" in 14 | "") 15 | Println "$i18n_canceled...\n" && exit 1 16 | ;; 17 | *[!0-9]*) 18 | Println "$error $i18n_input_correct_no\n" 19 | ;; 20 | *) 21 | if [ "$_4gtv_accs_num" -gt 0 ] && [ "$_4gtv_accs_num" -le "$_4gtv_accs_count" ] 22 | then 23 | _4gtv_accs_index=$((_4gtv_accs_num-1)) 24 | jq_path='["4gtv","accounts",'"$_4gtv_accs_index"']' 25 | JQ delete "$SERVICES_CONFIG" 26 | Println "$info 账号删除成功\n" 27 | break 28 | else 29 | Println "$error $i18n_input_correct_no\n" 30 | fi 31 | ;; 32 | esac 33 | done 34 | } 35 | -------------------------------------------------------------------------------- /src/_4gtv/disable_cron: -------------------------------------------------------------------------------- 1 | Disable4gtvCron() 2 | { 3 | if crontab -l | grep -q "$AIOS_PREFIX/bin/tv 4g -" 2> /dev/null 4 | then 5 | crontab -l > "$IPTV_ROOT/cron_tmp" 2> /dev/null || true 6 | sed -i "/\/usr\/local\/bin\/tv 4g -/d" "$IPTV_ROOT/cron_tmp" 7 | crontab "$IPTV_ROOT/cron_tmp" > /dev/null 8 | rm -f "$IPTV_ROOT/cron_tmp" 9 | Println "$info 定时任务 (每5天注册账号) 关闭成功\n" 10 | else 11 | Println "$error 定时任务 (每5天注册账号) 未开启 !\n" 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /src/_4gtv/edit_acc: -------------------------------------------------------------------------------- 1 | Edit4gtvAcc() 2 | { 3 | List4gtvAccs 4 | 5 | if [ "$_4gtv_accs_count" -eq 0 ] 6 | then 7 | exit 1 8 | fi 9 | 10 | echo -e "选择账号" 11 | while read -p "$i18n_default_cancel" _4gtv_accs_num 12 | do 13 | case "$_4gtv_accs_num" in 14 | "") 15 | Println "$i18n_canceled...\n" && exit 1 16 | ;; 17 | *[!0-9]*) 18 | Println "$error $i18n_input_correct_no\n" 19 | ;; 20 | *) 21 | if [ "$_4gtv_accs_num" -gt 0 ] && [ "$_4gtv_accs_num" -le "$_4gtv_accs_count" ] 22 | then 23 | _4gtv_accs_index=$((_4gtv_accs_num-1)) 24 | Set4gtvAccEmail 25 | Set4gtvAccPass 26 | new_acc=$( 27 | $JQ_FILE -n --arg email "$_4gtv_acc_email" --arg password "$_4gtv_acc_pass" \ 28 | '{ 29 | email: $email, 30 | password: $password 31 | }' 32 | ) 33 | json=true 34 | jq_path='["4gtv","accounts",'"$_4gtv_accs_index"']' 35 | JQ update "$SERVICES_CONFIG" "$new_acc" 36 | Println "$info 账号修改成功\n" 37 | break 38 | else 39 | Println "$error $i18n_input_correct_no\n" 40 | fi 41 | ;; 42 | esac 43 | done 44 | } 45 | -------------------------------------------------------------------------------- /src/_4gtv/enable_cron: -------------------------------------------------------------------------------- 1 | Enable4gtvCron() 2 | { 3 | if crontab -l | grep -q "$AIOS_PREFIX/bin/tv 4g -" 2> /dev/null 4 | then 5 | Println "$error 定时任务 (每5天注册账号) 已开启 !\n" 6 | else 7 | crontab -l > "$IPTV_ROOT/cron_tmp" 2> /dev/null || true 8 | printf '%s\n' "0 0 */5 * * $AIOS_PREFIX/bin/tv 4g -" >> "$IPTV_ROOT/cron_tmp" 9 | crontab "$IPTV_ROOT/cron_tmp" > /dev/null 10 | rm -f "$IPTV_ROOT/cron_tmp" 11 | Println "$info 定时任务 (每5天注册账号) 开启成功\n" 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /src/_4gtv/get_acc_token: -------------------------------------------------------------------------------- 1 | Get4gtvAccToken() 2 | { 3 | List4gtvAccs 4 | 5 | if [ "$_4gtv_accs_count" -eq 0 ] 6 | then 7 | exit 1 8 | fi 9 | 10 | echo -e "选择账号" 11 | while read -p "$i18n_default_cancel" _4gtv_accs_num 12 | do 13 | case "$_4gtv_accs_num" in 14 | "") 15 | Println "$i18n_canceled...\n" && exit 1 16 | ;; 17 | *[!0-9]*) 18 | Println "$error $i18n_input_correct_no\n" 19 | ;; 20 | *) 21 | if [ "$_4gtv_accs_num" -gt 0 ] && [ "$_4gtv_accs_num" -le "$_4gtv_accs_count" ] 22 | then 23 | _4gtv_accs_index=$((_4gtv_accs_num-1)) 24 | fsVALUE=${_4gtv_accs_token[_4gtv_accs_index]:-} 25 | if [ -z "$fsVALUE" ] 26 | then 27 | Println "$error 请先登录此账号\n" 28 | exit 1 29 | fi 30 | break 31 | else 32 | Println "$error $i18n_input_correct_no\n" 33 | fi 34 | ;; 35 | esac 36 | done 37 | } 38 | -------------------------------------------------------------------------------- /src/_4gtv/list_accs: -------------------------------------------------------------------------------- 1 | List4gtvAccs() 2 | { 3 | ServiceGet 4gtv 4 | _4gtv_accs_list="" 5 | 6 | for((i=0;i<_4gtv_accs_count;i++)); 7 | do 8 | if [ -n "${_4gtv_accs_token[i]:-}" ] 9 | then 10 | is_login="${green} [ 已登录 ] ${normal}" 11 | else 12 | is_login="" 13 | fi 14 | _4gtv_accs_list="$_4gtv_accs_list ${green}$((i+1)).${normal}${indent_6}邮箱: ${green}${_4gtv_accs_email[i]}${normal}$is_login\n${indent_6}密码: ${green}${_4gtv_accs_pass[i]}${normal}\n\n" 15 | done 16 | 17 | if [ -n "$_4gtv_accs_list" ] 18 | then 19 | Println "$_4gtv_accs_list" 20 | else 21 | Println "$error 没有账号\n" 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /src/_4gtv/reg_acc: -------------------------------------------------------------------------------- 1 | Reg4gtvAcc() 2 | { 3 | Set4gtvAccEmail 4 | Set4gtvAccPass 5 | IFS=" " read -r result msg < <(CurlFake -s -Lm 10 \ 6 | -H 'Origin: https://www.4gtv.tv' \ 7 | -H 'Referer: https://www.4gtv.tv/signup.html' \ 8 | -d "fnREGISTER_TYPE=1&fsLOGIN_TYPE=&fsLINK_ID=&fsUSER=$_4gtv_acc_email&fsLOGIN_TYPE=&fsLINK_ID=&fsPASSWORD=$_4gtv_acc_pass&fsPASSWORD1=$_4gtv_acc_pass&fnBIRTH_YEAR=$((RANDOM%20+1980))&fsSEX=male" \ 9 | https://api2.4gtv.tv/Account/Register \ 10 | | $JQ_FILE -r '[.Success,.ErrMessage]|join(" ")' 11 | ) || true 12 | 13 | if [ "$result" = true ] || [ "$msg" = "String was not recognized as a valid DateTime." ] 14 | then 15 | if [ ! -s "$SERVICES_CONFIG" ] 16 | then 17 | printf '{"%s":{"%s":[]}}' "4gtv" "accounts" > "$SERVICES_CONFIG" 18 | fi 19 | new_acc=$( 20 | $JQ_FILE -n --arg email "$_4gtv_acc_email" --arg password "$_4gtv_acc_pass" \ 21 | '{ 22 | email: $email, 23 | password: $password 24 | }' 25 | ) 26 | jq_path='["4gtv","accounts"]' 27 | JQ add "$SERVICES_CONFIG" "[$new_acc]" 28 | Println "$info 账号注册成功\n" 29 | else 30 | Println "$error 账号注册失败, 请重试\n\n$msg\n" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /src/_4gtv/set_acc: -------------------------------------------------------------------------------- 1 | Set4gtvAccEmail() 2 | { 3 | Println "输入新账号邮箱" 4 | while read -p "(默认: 随机): " _4gtv_acc_email 5 | do 6 | [ -z "$_4gtv_acc_email" ] && _4gtv_acc_email="$(RandStr)_$(printf '%(%s)T' -1)@gmail.com" 7 | if [[ $_4gtv_acc_email =~ ^[A-Za-z0-9]([a-zA-Z0-9_\.\-]*)@([A-Za-z0-9]+)([a-zA-Z0-9\.\-]*)\.([A-Za-z]{2,})$ ]] 8 | then 9 | break 10 | else 11 | Println "$error 邮箱格式错误, 请重新输入\n" 12 | fi 13 | done 14 | Println " 4gtv 账号邮箱: ${green} $_4gtv_acc_email ${normal}\n" 15 | } 16 | 17 | Set4gtvAccPass() 18 | { 19 | Println "输入新账号密码(字母或数字 8-12 位)" 20 | while read -p "(默认: 随机): " _4gtv_acc_pass 21 | do 22 | [ -z "$_4gtv_acc_pass" ] && _4gtv_acc_pass=$(RandStr) 23 | if [[ $_4gtv_acc_pass =~ ^[A-Za-z0-9]{8,12}$ ]] 24 | then 25 | break 26 | else 27 | Println "$error 账号密码格式错误, 请重新输入\n" 28 | fi 29 | done 30 | Println " 4gtv 账号密码: ${green} $_4gtv_acc_pass ${normal}\n" 31 | } 32 | -------------------------------------------------------------------------------- /src/_4gtv/use_proxy: -------------------------------------------------------------------------------- 1 | Use4gtvProxy() 2 | { 3 | GetDefault 4 | d_4gtv_proxy=${d_4gtv_proxy:-$d_proxy} 5 | Println "$tip 可以使用脚本自带的 v2ray 管理面板添加代理, 可以输入 omit 省略此选项" 6 | inquirer text_input "请输入 4gtv 代理, 比如 http://username:passsword@127.0.0.1:5555 : " _4gtv_proxy "${d_4gtv_proxy:-$i18n_blank}" 7 | if [ -z "$_4gtv_proxy" ] || [ "$_4gtv_proxy" == "omit" ] 8 | then 9 | _4gtv_proxy="" 10 | _4gtv_proxy_command=() 11 | else 12 | _4gtv_proxy_command=( -x "$_4gtv_proxy" ) 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /src/ali: -------------------------------------------------------------------------------- 1 | Include src/service/get 2 | Include src/service/control 3 | Include src/alist/app 4 | Include src/alist/acc 5 | Include src/alist/api 6 | Include src/alist/act 7 | 8 | echo 9 | alist_options=( '文件管理' 'Alist管理' '账号管理' '退出' ) 10 | inquirer list_input_index "请选择" alist_options alist_options_index 11 | 12 | case $alist_options_index in 13 | 0) 14 | AlistAct 15 | ;; 16 | 1) 17 | echo 18 | alist_app_options=( '查看' '添加' '修改' '删除' '本地安装/升级' ) 19 | inquirer list_input_index "选择操作" alist_app_options alist_app_options_index 20 | 21 | case $alist_app_options_index in 22 | 0) AlistAppView 23 | ;; 24 | 1) AlistAppAdd 25 | ;; 26 | 2) AlistAppEdit 27 | ;; 28 | 3) AlistAppDel 29 | ;; 30 | 4) AlistAppInstall 31 | ;; 32 | esac 33 | ;; 34 | 2) 35 | echo 36 | alist_acc_options=( '查看' '添加' '登陆' '修改' '删除' ) 37 | inquirer list_input_index "选择操作" alist_acc_options alist_acc_options_index 38 | 39 | case $alist_acc_options_index in 40 | 0) AlistAccView 41 | ;; 42 | 1) AlistAccAdd 43 | ;; 44 | 2) AlistAccLogin 45 | ;; 46 | 3) AlistAccEdit 47 | ;; 48 | 4) AlistAccDel 49 | ;; 50 | esac 51 | ;; 52 | *) 53 | ;; 54 | esac 55 | 56 | exit 0 57 | -------------------------------------------------------------------------------- /src/calibre/install: -------------------------------------------------------------------------------- 1 | CalibreInstall() 2 | { 3 | if [ "$dist" == "mac" ] 4 | then 5 | brew install --cask calibre 6 | else 7 | DepInstall wget 8 | DepInstall xdg-utils 9 | if [ "$dist" == "rpm" ] 10 | then 11 | DepInstall xz 12 | DepInstall libglvnd-opengl 13 | DepInstall libxcb-cursor-devel 14 | DepInstall nss 15 | DepInstall libxkbcommon 16 | DepInstall mesa-libEGL 17 | else 18 | DepInstall xz-utils 19 | DepInstall libopengl0 20 | DepInstall libxcb-cursor0 21 | DepInstall libnss3 22 | DepInstall libxkbcommon-x11-0 23 | DepInstall libglx0 24 | DepInstall libegl1 25 | fi 26 | ImageMagickInstall 27 | PythonInstall 28 | pip3 install pyqt6 29 | mkdir -p "$CALIBRE_ROOT"/bin 30 | sudo -v && wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sudo sh /dev/stdin install_dir="$CALIBRE_ROOT"/bin version=7.16.0 31 | fi 32 | 33 | Println "$info calibre 安装/更新成功\n" 34 | } 35 | 36 | CalibrePluginInstall() 37 | { 38 | if { [ "$dist" == "mac" ] && [ ! -d /Applications/calibre.app ]; } || { [ "$dist" != "mac" ] && [ ! -d "$CALIBRE_ROOT"/bin ]; } 39 | then 40 | CalibreInstall 41 | fi 42 | 43 | if [ "$dist" == "mac" ] 44 | then 45 | CALIBRE_BIN_ROOT="/Applications/calibre.app/Contents/MacOS" 46 | else 47 | CALIBRE_BIN_ROOT="$CALIBRE_ROOT"/bin/calibre 48 | fi 49 | 50 | local plugin_name="$1" plugin_grep=$("$CALIBRE_BIN_ROOT"/calibre-customize -l) 51 | if ! grep -q "$plugin_name" <<< "$plugin_grep" 52 | then 53 | DepInstall curl 54 | mkdir -p "$CALIBRE_ROOT"/plugins 55 | if ! curl -L -o "${CALIBRE_ROOT}/plugins/${plugin_name}.zip" "$FFMPEG_MIRROR_LINK/calibre/plugins/${plugin_name}.zip" 56 | then 57 | Println "$error $plugin_name 插件不存在\n" 58 | return 1 59 | fi 60 | "$CALIBRE_BIN_ROOT"/calibre-customize -a "${CALIBRE_ROOT}/plugins/${plugin_name}.zip" 61 | Println "$info $plugin_name 插件安装成功\n" 62 | fi 63 | } 64 | -------------------------------------------------------------------------------- /src/cloudflare/add_host: -------------------------------------------------------------------------------- 1 | CloudflareAddHost() 2 | { 3 | CloudflareSetHostKey 4 | CloudflareSetHostName 5 | 6 | if [ ! -s "$CF_CONFIG" ] 7 | then 8 | printf '{"%s":[],"%s":[]}' "users" "hosts" > "$CF_CONFIG" 9 | fi 10 | 11 | new_host=$( 12 | $JQ_FILE -n --arg name "$cf_host_name" --arg key "$cf_host_key" \ 13 | '{ 14 | name: $name, 15 | key: $key, 16 | free: 0, 17 | zones: [] 18 | }' 19 | ) 20 | 21 | jq_path='["hosts"]' 22 | JQ add "$CF_CONFIG" "[$new_host]" 23 | Println "$info CFP 添加成功\n" 24 | } 25 | -------------------------------------------------------------------------------- /src/cloudflare/add_token: -------------------------------------------------------------------------------- 1 | CloudflareAddToken() 2 | { 3 | CloudflareListToken 4 | 5 | echo -e "选择 Token" 6 | 7 | while read -p "$i18n_default_cancel" tokens_num 8 | do 9 | case $tokens_num in 10 | "") 11 | Println "$i18n_canceled...\n" 12 | exit 1 13 | ;; 14 | *[!0-9]*) 15 | Println "$error $i18n_input_correct_no\n" 16 | ;; 17 | *) 18 | if [ "$tokens_num" -gt 0 ] && [ "$tokens_num" -le "$tokens_count" ] 19 | then 20 | tokens_index=$((tokens_num-1)) 21 | token_id=${tokens_id[tokens_index]} 22 | break 23 | else 24 | Println "$error $i18n_input_correct_no\n" 25 | fi 26 | ;; 27 | esac 28 | done 29 | 30 | echo 31 | ExitOnList n "`gettext \"需要更新此 Token 后才能添加到脚本, 是否继续\"`" 32 | 33 | Println "$info 更新 Token" 34 | cf_user_token_new=$(curl -s -X PUT https://api.cloudflare.com/client/v4/user/tokens/$token_id/value \ 35 | -H "X-Auth-Email:$cf_user_email" \ 36 | -H "X-Auth-Key:$cf_user_api_key" \ 37 | -H "Content-Type: application/json" \ 38 | --data '{}' | $JQ_FILE -r '.result' 39 | ) || true 40 | 41 | if [ -z "$cf_user_token_new" ] || [ "$cf_user_token_new" == null ] 42 | then 43 | Println "$error 更新 Token 失败\n" 44 | else 45 | Println "$info Token 更新成功: $cf_user_token_new" 46 | jq_path='["users",'"$cf_users_index"',"token"]' 47 | JQ update "$CF_CONFIG" "$cf_user_token_new" 48 | Println "$info 用户 Token 添加成功\n" 49 | fi 50 | } 51 | -------------------------------------------------------------------------------- /src/cloudflare/add_user: -------------------------------------------------------------------------------- 1 | CloudflareAddUser() 2 | { 3 | CloudflareSetUserEmail 4 | CloudflareSetUserPass 5 | CloudflareSetUserToken 6 | CloudflareSetUserKey 7 | 8 | if [ ! -s "$CF_CONFIG" ] 9 | then 10 | printf '{"%s":[],"%s":[]}' "users" "hosts" > "$CF_CONFIG" 11 | fi 12 | 13 | new_user=$( 14 | $JQ_FILE -n --arg email "$cf_user_email" --arg pass "$cf_user_pass" \ 15 | --arg token "$cf_user_token" --arg key "$cf_user_api_key" \ 16 | '{ 17 | email: $email, 18 | pass: $pass, 19 | token: $token, 20 | key: $key 21 | }' 22 | ) 23 | 24 | jq_path='["users"]' 25 | JQ add "$CF_CONFIG" "[$new_user]" 26 | Println "$info 用户添加成功\n" 27 | } 28 | -------------------------------------------------------------------------------- /src/cloudflare/delete_user: -------------------------------------------------------------------------------- 1 | CloudflareDelUser() 2 | { 3 | CloudflareListUsers 4 | 5 | if [ "$cf_users_count" -eq 0 ] 6 | then 7 | Println "$error 请先添加用户\n" 8 | exit 1 9 | fi 10 | 11 | echo -e "选择用户" 12 | while read -p "$i18n_default_cancel" cf_users_num 13 | do 14 | case "$cf_users_num" in 15 | "") 16 | Println "$i18n_canceled...\n" && exit 1 17 | ;; 18 | *[!0-9]*) 19 | Println "$error $i18n_input_correct_no\n" 20 | ;; 21 | *) 22 | if [ "$cf_users_num" -gt 0 ] && [ "$cf_users_num" -le "$cf_users_count" ] 23 | then 24 | cf_users_index=$((cf_users_num-1)) 25 | break 26 | else 27 | Println "$error $i18n_input_correct_no\n" 28 | fi 29 | ;; 30 | esac 31 | done 32 | 33 | jq_path='["users",'"$cf_users_index"']' 34 | JQ delete "$CF_CONFIG" 35 | Println "$info 用户删除成功\n" 36 | } 37 | -------------------------------------------------------------------------------- /src/cloudflare/delete_worker: -------------------------------------------------------------------------------- 1 | CloudflareDelWorker() 2 | { 3 | CloudflareListWorkers 4 | 5 | if [ "$cf_workers_count" -eq 0 ] 6 | then 7 | Println "$error 请先添加 worker\n" 8 | exit 1 9 | fi 10 | 11 | echo "选择 worker" 12 | while read -p "$i18n_default_cancel" cf_workers_num 13 | do 14 | case "$cf_workers_num" in 15 | "") 16 | Println "$i18n_canceled...\n" && exit 1 17 | ;; 18 | *[!0-9]*) 19 | Println "$error $i18n_input_correct_no\n" 20 | ;; 21 | *) 22 | if [ "$cf_workers_num" -gt 0 ] && [ "$cf_workers_num" -le "$cf_workers_count" ] 23 | then 24 | cf_workers_index=$((cf_workers_num-1)) 25 | cf_worker_name=${cf_workers_name[cf_workers_index]} 26 | cf_worker_path=${cf_workers_path[cf_workers_index]} 27 | break 28 | else 29 | Println "$error $i18n_input_correct_no\n" 30 | fi 31 | ;; 32 | esac 33 | done 34 | 35 | if [ -d "$CF_WORKERS_ROOT/$cf_worker_path" ] 36 | then 37 | echo 38 | inquirer list_input "是否删除 worker 目录 $CF_WORKERS_ROOT/$cf_worker_path" ny_options del_cf_worker_path 39 | 40 | if [[ $del_cf_worker_path == "$i18n_yes" ]] 41 | then 42 | rm -rf "$CF_WORKERS_ROOT/${cf_worker_path:-notfound}" 43 | fi 44 | fi 45 | 46 | jq_path='["workers",'"$cf_workers_index"']' 47 | JQ delete "$CF_CONFIG" 48 | Println "$info worker: $cf_worker_name 删除成功\n" 49 | } 50 | -------------------------------------------------------------------------------- /src/cloudflare/get_hosts: -------------------------------------------------------------------------------- 1 | CloudflareGetHosts() 2 | { 3 | cf_hosts_list="" 4 | cf_hosts_count=0 5 | cf_hosts_name=() 6 | cf_hosts_key=() 7 | cf_hosts_zones_count=() 8 | cf_hosts_zone_name=() 9 | cf_hosts_zone_resolve_to=() 10 | cf_hosts_zone_user_email=() 11 | cf_hosts_zone_user_unique_id=() 12 | cf_hosts_zone_always_use_https=() 13 | cf_hosts_zone_ssl=() 14 | cf_hosts_zone_subdomains=() 15 | while IFS="^" read -r name key zones_count zone_name zone_resolve_to zone_user_email zone_user_unique_id zone_always_use_https zone_ssl zone_subdomains 16 | do 17 | cf_hosts_count=$((cf_hosts_count+1)) 18 | name=${name#\"} 19 | cf_hosts_name+=("$name") 20 | cf_hosts_key+=("$key") 21 | cf_hosts_zones_count+=("$zones_count") 22 | cf_hosts_zone_name+=("$zone_name") 23 | cf_hosts_zone_resolve_to+=("$zone_resolve_to") 24 | cf_hosts_zone_user_email+=("$zone_user_email") 25 | zone_user_unique_id=${zone_user_unique_id%\"} 26 | cf_hosts_zone_user_unique_id+=("$zone_user_unique_id") 27 | cf_hosts_zone_always_use_https+=("$zone_always_use_https") 28 | cf_hosts_zone_ssl+=("$zone_ssl") 29 | zone_subdomains=${zone_subdomains%\"} 30 | cf_hosts_zone_subdomains+=("$zone_subdomains") 31 | 32 | cf_hosts_list="$cf_hosts_list ${green}$cf_hosts_count.${normal}${indent_6}CFP: ${green}$name${normal} host key: ${green}$key${normal} 域名数: ${green}$zones_count${normal}\n\n" 33 | done < <($JQ_FILE '.hosts[]|[.name,.key,(.zones|length),([.zones[].name]|join("|")),([.zones[].resolve_to]|join("|")),([.zones[].user_email]|join("|")),([.zones[].user_unique_id]|join("|")),([.zones[].always_use_https]|join("|")),([.zones[].ssl]|join("|")),([.zones[].subdomains]|join("|"))]|join("^")' "$CF_CONFIG") 34 | return 0 35 | } 36 | -------------------------------------------------------------------------------- /src/cloudflare/get_users: -------------------------------------------------------------------------------- 1 | CloudflareGetUsers() 2 | { 3 | cf_users_list="" 4 | cf_users_count=0 5 | cf_users_email=() 6 | cf_users_pass=() 7 | cf_users_token=() 8 | cf_users_api_key=() 9 | while IFS="^" read -r email pass token key 10 | do 11 | cf_users_count=$((cf_users_count+1)) 12 | email=${email#\"} 13 | cf_users_email+=("$email") 14 | cf_users_pass+=("$pass") 15 | cf_users_token+=("$token") 16 | key=${key%\"} 17 | cf_users_api_key+=("$key") 18 | 19 | cf_users_list="$cf_users_list ${green}$cf_users_count.${normal}${indent_6}邮箱: ${green}$email${normal} 密码: ${green}$pass${normal}\n${indent_6}Token: ${green}${token:-无}${normal}\n${indent_6}Key: ${green}${key:-无}${normal}\n\n" 20 | done < <($JQ_FILE '.users[]|[.email,.pass,.token,.key]|join("^")' "$CF_CONFIG") 21 | return 0 22 | } 23 | -------------------------------------------------------------------------------- /src/cloudflare/get_workers: -------------------------------------------------------------------------------- 1 | CloudflareGetWorkers() 2 | { 3 | SetDelimiters 4 | IFS=$'\002\t' read -r name path project_name upstream < <(JQs flat "$CF_CONFIG" '.[0].workers' ' 5 | (. // {}| if . == "" then {} else . end) as $workers | 6 | reduce ({name,path,project_name,upstream}|keys_unsorted[]) as $key ([]; 7 | $workers[$key] as $val | if $val then 8 | . + [$val + "'"${delimiters[0]}"'\u0002"] 9 | else 10 | . + ["\u0002"] 11 | end 12 | )|@tsv' "${delimiters[@]}") 13 | 14 | if [ -z "$name" ] 15 | then 16 | cf_workers_count=0 17 | return 0 18 | fi 19 | 20 | IFS="${delimiters[0]}" read -r -a cf_workers_name <<< "$name" 21 | IFS="${delimiters[0]}" read -r -a cf_workers_path <<< "$path" 22 | IFS="${delimiters[0]}" read -r -a cf_workers_project_name <<< "$project_name" 23 | 24 | if [ -z "$upstream" ] 25 | then 26 | cf_workers_upstream=("${cf_workers_name[@]//*/}") 27 | else 28 | IFS="${delimiters[0]}" read -r -a cf_workers_upstream <<< "$upstream" 29 | fi 30 | 31 | cf_workers_count=${#cf_workers_name[@]} 32 | } 33 | -------------------------------------------------------------------------------- /src/cloudflare/get_zone: -------------------------------------------------------------------------------- 1 | CloudflareGetZone() 2 | { 3 | IFS="^" read -r result cf_zone_hosted_cnames cf_zone_forward_tos cf_zone_ssl_status cf_zone_ssl_meta_tag msg < <(curl -s -Lm 50 https://api.cloudflare.com/host-gw.html \ 4 | -d 'act=zone_lookup' \ 5 | -d "host_key=$cf_host_key" \ 6 | -d "user_key=$cf_user_key" \ 7 | -d "zone_name=$cf_zone_name" \ 8 | | $JQ_FILE '[.result,([(.response.hosted_cnames| if .== null then {} else . end)|to_entries[] 9 | |([.key,.value]|join("="))] 10 | |join("|")),([(.response.forward_tos| if .== null then {} else . end)|to_entries[] 11 | |([.key,.value]|join("="))] 12 | |join("|")),.response.ssl_status,.response.ssl_meta_tag,.msg]|join("^")' 13 | ) || true 14 | 15 | result=${result#\"} 16 | msg=${msg%\"} 17 | 18 | if [ -z "$result" ] || [ "$result" == "error" ] 19 | then 20 | Println "$error ${msg:-超时, 请重试}\n" && exit 1 21 | fi 22 | 23 | IFS="|" read -r -a cf_zone_hosted_cnames_arr <<< "$cf_zone_hosted_cnames" 24 | IFS="|" read -r -a cf_zone_forward_tos_arr <<< "$cf_zone_forward_tos" 25 | 26 | cf_hosted_cnames=() 27 | cf_resolve_tos=() 28 | cf_forward_tos=() 29 | 30 | for cf_zone_hosted_cname in "${cf_zone_hosted_cnames_arr[@]}" 31 | do 32 | cf_hosted_cname=${cf_zone_hosted_cname%%=*} 33 | cf_resolve_to=${cf_zone_hosted_cname#*=} 34 | cf_hosted_cnames+=("$cf_hosted_cname") 35 | cf_resolve_tos+=("$cf_resolve_to") 36 | for cf_zone_forward_to in "${cf_zone_forward_tos_arr[@]}" 37 | do 38 | if [ "${cf_zone_forward_to%%=*}" == "$cf_hosted_cname" ] 39 | then 40 | cf_forward_tos+=("${cf_zone_forward_to#*=}") 41 | break 42 | fi 43 | done 44 | done 45 | } 46 | -------------------------------------------------------------------------------- /src/cloudflare/list_host: -------------------------------------------------------------------------------- 1 | CloudflareListHost() 2 | { 3 | CloudflareListHosts 4 | } 5 | -------------------------------------------------------------------------------- /src/cloudflare/list_hosts: -------------------------------------------------------------------------------- 1 | CloudflareListHosts() 2 | { 3 | if [ ! -s "$CF_CONFIG" ] 4 | then 5 | Println "$error 请先添加 CFP\n" && exit 1 6 | fi 7 | 8 | CloudflareGetHosts 9 | 10 | if [ "$cf_hosts_count" -gt 0 ] 11 | then 12 | Println "$cf_hosts_list" 13 | else 14 | Println "$error 请先添加 CFP\n" && exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /src/cloudflare/list_users: -------------------------------------------------------------------------------- 1 | CloudflareListUsers() 2 | { 3 | if [ ! -s "$CF_CONFIG" ] 4 | then 5 | Println "$error 请先添加用户\n" && exit 1 6 | fi 7 | 8 | CloudflareGetUsers 9 | 10 | if [ "$cf_users_count" -gt 0 ] 11 | then 12 | Println "$cf_users_list" 13 | else 14 | Println "$error 没有用户\n" 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /src/cloudflare/list_worker: -------------------------------------------------------------------------------- 1 | CloudflareListWorker() 2 | { 3 | CloudflareListWorkers 4 | } 5 | -------------------------------------------------------------------------------- /src/cloudflare/list_workers: -------------------------------------------------------------------------------- 1 | CloudflareListWorkers() 2 | { 3 | if [ ! -s "$CF_CONFIG" ] 4 | then 5 | Println "$error 请先添加 worker\n" && exit 1 6 | fi 7 | 8 | CloudflareGetWorkers 9 | 10 | if [ "$cf_workers_count" -gt 0 ] 11 | then 12 | cf_workers_list="" 13 | 14 | for((i=0;i "$IBM_CONFIG" 6 | fi 7 | 8 | IbmSetUserEmail 9 | IbmSetUserPass 10 | 11 | IbmGetApi 12 | 13 | ibmcloud api "$ibm_api" 14 | 15 | IbmSetUserRegion 16 | 17 | Println "$info 登录账号: $ibm_user_email [ $ibm_user_region ]" 18 | ibmcloud login -u "$ibm_user_email" -p "$ibm_user_pass" -r "$ibm_user_region" 19 | 20 | IbmSetUserResourceGroup 21 | 22 | ibmcloud target -g "$ibm_user_resource_group" 23 | 24 | IbmSetUserOrg 25 | 26 | ibmcloud target -o "$ibm_user_org" 27 | 28 | IbmSetUserSpace 29 | 30 | ibmcloud target -s "$ibm_user_space" 31 | 32 | new_user=$( 33 | $JQ_FILE -n --arg email "$ibm_user_email" --arg pass "$ibm_user_pass" \ 34 | --arg region "$ibm_user_region" --arg resource_group "$ibm_user_resource_group" \ 35 | --arg org "$ibm_user_org" --arg space "$ibm_user_space" \ 36 | '{ 37 | email: $email, 38 | pass: $pass, 39 | region: $region, 40 | resource_group: $resource_group, 41 | org: $org, 42 | space: $space 43 | }' 44 | ) 45 | 46 | jq_path='["users"]' 47 | JQ add "$IBM_CONFIG" "[$new_user]" 48 | Println "$info 用户添加成功\n" 49 | } 50 | -------------------------------------------------------------------------------- /src/ibm_cf/delete_app: -------------------------------------------------------------------------------- 1 | IbmDelApp() 2 | { 3 | IbmListCfApp 4 | 5 | Println "$info 登录账号: $ibm_user_email [ $ibm_user_region ]" 6 | ibmcloud login -u "$ibm_user_email" -p "$ibm_user_pass" -r "$ibm_user_region" -g "$ibm_user_resource_group" 7 | ibmcloud target -o "$ibm_user_org" -s "$ibm_user_space" 8 | 9 | echo 10 | inquirer list_input "是否删除 APP 绑定的路由" yn_options delete_app_routes_yn 11 | 12 | if [ "$delete_app_routes_yn" == "$i18n_yes" ] 13 | then 14 | ibmcloud cf delete "$ibm_cf_app_name" -r 15 | else 16 | ibmcloud cf delete "$ibm_cf_app_name" 17 | fi 18 | 19 | jq_path='["cf","apps",'"$ibm_cf_apps_index"']' 20 | JQ delete "$IBM_CONFIG" 21 | 22 | Println "$info APP $ibm_cf_app_name 删除成功" 23 | } 24 | -------------------------------------------------------------------------------- /src/ibm_cf/delete_user: -------------------------------------------------------------------------------- 1 | IbmDelUser() 2 | { 3 | IbmListUsers 4 | 5 | echo -e "选择用户" 6 | while read -p "$i18n_default_cancel" ibm_users_num 7 | do 8 | case "$ibm_users_num" in 9 | "") 10 | Println "$i18n_canceled...\n" && exit 1 11 | ;; 12 | *[!0-9]*) 13 | Println "$error $i18n_input_correct_no\n" 14 | ;; 15 | *) 16 | if [ "$ibm_users_num" -gt 0 ] && [ "$ibm_users_num" -le "$ibm_users_count" ] 17 | then 18 | ibm_users_index=$((ibm_users_num-1)) 19 | ibm_user_email=${ibm_users_email[ibm_users_index]} 20 | break 21 | else 22 | Println "$error $i18n_input_correct_no\n" 23 | fi 24 | ;; 25 | esac 26 | done 27 | 28 | jq_path='["users",'"$ibm_users_index"']' 29 | JQ delete "$IBM_CONFIG" 30 | 31 | Println "$info 用户 $ibm_user_email 删除成功" 32 | } 33 | -------------------------------------------------------------------------------- /src/ibm_cf/disable_cron: -------------------------------------------------------------------------------- 1 | IbmDisableCfAppCron() 2 | { 3 | if crontab -l | grep -q "$AIOS_PREFIX/bin/ibm cron" 2> /dev/null 4 | then 5 | crontab -l > "$IBM_APPS_ROOT/cron_tmp" 2> /dev/null || true 6 | sed -i "/\/usr\/local\/bin\/ibm cron/d" "$IBM_APPS_ROOT/cron_tmp" 7 | crontab "$IBM_APPS_ROOT/cron_tmp" > /dev/null 8 | rm -f "$IBM_APPS_ROOT/cron_tmp" 9 | Println "$info 定时重启任务关闭成功\n" 10 | else 11 | Println "$error 定时重启任务未开启 !\n" 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /src/ibm_cf/download_v2ray: -------------------------------------------------------------------------------- 1 | IbmDownloadV2ray() 2 | { 3 | if [ -d "$IBM_APPS_ROOT/ibm_$v2ray_name" ] 4 | then 5 | Println "$error ibm $v2ray_name 已存在\n" 6 | else 7 | DepsCheck 8 | JQInstall 9 | 10 | Println "$info 下载 ibm $v2ray_name ..." 11 | cd ~ 12 | rm -rf $v2ray_package_name-linux-64 13 | if v2ray_version=$(curl -s -L "$FFMPEG_MIRROR_LINK/$v2ray_name.json" | $JQ_FILE -r '.tag_name') && curl -L "$FFMPEG_MIRROR_LINK/$v2ray_name/$v2ray_version/$v2ray_package_name-linux-64.zip" -o "$v2ray_package_name-linux-64.zip" && unzip "$v2ray_package_name-linux-64.zip" -d "$v2ray_package_name-linux-64" > /dev/null 14 | then 15 | mkdir -p "$IBM_APPS_ROOT/ibm_$v2ray_name" 16 | mv ${v2ray_package_name}-linux-64/$v2ray_name "$IBM_APPS_ROOT/ibm_$v2ray_name/" 17 | if [ "$v2ray_name" == "xray" ] 18 | then 19 | if ! curl -L "$FFMPEG_MIRROR_LINK/xray/v2ctl" -o "$IBM_APPS_ROOT/ibm_$v2ray_name/v2ctl" 20 | then 21 | Println "$error 无法连接服务器, 请稍后再试\n" 22 | exit 1 23 | fi 24 | else 25 | mv $v2ray_package_name-linux-64/v2ctl "$IBM_APPS_ROOT/ibm_$v2ray_name/" 26 | fi 27 | chmod 700 "$IBM_APPS_ROOT/ibm_$v2ray_name/$v2ray_name" 28 | chmod 700 "$IBM_APPS_ROOT/ibm_$v2ray_name/v2ctl" 29 | Println "$info ibm $v2ray_name 下载完成\n" 30 | else 31 | Println "$error 无法连接服务器, 请稍后再试\n" 32 | fi 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /src/ibm_cf/edit_user: -------------------------------------------------------------------------------- 1 | IbmEditUser() 2 | { 3 | IbmListUsers 4 | 5 | if [ "$ibm_users_count" -eq 0 ] 6 | then 7 | Println "$error 请先添加用户\n" 8 | exit 1 9 | fi 10 | 11 | echo "选择用户" 12 | while read -p "$i18n_default_cancel" ibm_users_num 13 | do 14 | case "$ibm_users_num" in 15 | "") 16 | Println "$i18n_canceled...\n" && exit 1 17 | ;; 18 | *[!0-9]*) 19 | Println "$error $i18n_input_correct_no\n" 20 | ;; 21 | *) 22 | if [ "$ibm_users_num" -gt 0 ] && [ "$ibm_users_num" -le "$ibm_users_count" ] 23 | then 24 | ibm_users_index=$((ibm_users_num-1)) 25 | break 26 | else 27 | Println "$error $i18n_input_correct_no\n" 28 | fi 29 | ;; 30 | esac 31 | done 32 | 33 | IbmSetUserEmail 34 | IbmSetUserPass 35 | 36 | IbmGetApi 37 | 38 | ibmcloud api "$ibm_api" 39 | 40 | IbmSetUserRegion 41 | 42 | Println "$info 登录账号: $ibm_user_email [ $ibm_user_region ]" 43 | ibmcloud login -u "$ibm_user_email" -p "$ibm_user_pass" -r "$ibm_user_region" 44 | 45 | IbmSetUserResourceGroup 46 | 47 | ibmcloud target -g "$ibm_user_resource_group" 48 | 49 | IbmSetUserOrg 50 | 51 | ibmcloud target -o "$ibm_user_org" 52 | 53 | IbmSetUserSpace 54 | 55 | ibmcloud target -s "$ibm_user_space" 56 | 57 | new_user=$( 58 | $JQ_FILE -n --arg email "$ibm_user_email" --arg pass "$ibm_user_pass" \ 59 | --arg region "$ibm_user_region" --arg resource_group "$ibm_user_resource_group" \ 60 | --arg org "$ibm_user_org" --arg space "$ibm_user_space" \ 61 | '{ 62 | email: $email, 63 | pass: $pass, 64 | region: $region, 65 | resource_group: $resource_group, 66 | org: $org, 67 | space: $space 68 | }' 69 | ) 70 | 71 | json=true 72 | jq_path='["users",'"$ibm_users_index"']' 73 | JQ update "$IBM_CONFIG" "$new_user" 74 | Println "$info 用户修改成功\n" 75 | } 76 | -------------------------------------------------------------------------------- /src/ibm_cf/enable_cron: -------------------------------------------------------------------------------- 1 | IbmEnableCfAppCron() 2 | { 3 | if crontab -l | grep -q "$AIOS_PREFIX/bin/ibm cron" 2> /dev/null 4 | then 5 | Println "$error 定时重启任务已开启 !\n" 6 | else 7 | IFS=" " read -r cron_days cron_hour cron_min < <($JQ_FILE -r '.cf.cron|[.days,.hour,.min]|join(" ")' "$IBM_CONFIG") 8 | [ -z "$cron_days" ] && Println "$error 请先设置定时重启任务\n" && exit 1 9 | crontab -l > "$IBM_APPS_ROOT/cron_tmp" 2> /dev/null || true 10 | printf '%s\n' "$cron_min $cron_hour */$cron_days * * $AIOS_PREFIX/bin/ibm cron" >> "$IBM_APPS_ROOT/cron_tmp" 11 | if ! grep -q 'PATH=' < "$IBM_APPS_ROOT/cron_tmp" 12 | then 13 | cron=$(< "$IBM_APPS_ROOT/cron_tmp") 14 | echo -e "PATH=$PATH\n$cron" > "$IBM_APPS_ROOT/cron_tmp" 15 | fi 16 | crontab "$IBM_APPS_ROOT/cron_tmp" > /dev/null 17 | rm -f "$IBM_APPS_ROOT/cron_tmp" 18 | Println "$info 定时重启任务开启成功\n" 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /src/ibm_cf/get_api: -------------------------------------------------------------------------------- 1 | IbmGetApi() 2 | { 3 | while IFS= read -r line 4 | do 5 | if [[ $line == *"endpoint:"* ]] 6 | then 7 | ibm_api=${line##* } 8 | break 9 | fi 10 | done < <(ibmcloud api) 11 | 12 | if [ -z "${ibm_api:-}" ] 13 | then 14 | Println "$error 无法获取 ibmcloud api ?\n" 15 | exit 1 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /src/ibm_cf/get_apps: -------------------------------------------------------------------------------- 1 | IbmGetCfApps() 2 | { 3 | ibm_cf_apps_list="" 4 | ibm_cf_apps_count=0 5 | ibm_cf_apps_name=() 6 | ibm_cf_apps_user_email=() 7 | ibm_cf_apps_routes_count=() 8 | ibm_cf_apps_route_hostname=() 9 | ibm_cf_apps_route_port=() 10 | ibm_cf_apps_route_domain=() 11 | ibm_cf_apps_route_path=() 12 | while IFS="^" read -r name user_email routes_count route_hostname route_port route_domain route_path 13 | do 14 | ibm_cf_apps_count=$((ibm_cf_apps_count+1)) 15 | name=${name#\"} 16 | ibm_cf_apps_name+=("$name") 17 | ibm_cf_apps_user_email+=("$user_email") 18 | ibm_cf_apps_routes_count+=("$routes_count") 19 | ibm_cf_apps_route_hostname+=("$route_hostname") 20 | ibm_cf_apps_route_port+=("$route_port") 21 | ibm_cf_apps_route_domain+=("$route_domain") 22 | route_path=${route_path%\"} 23 | ibm_cf_apps_route_path+=("$route_path") 24 | 25 | ibm_cf_apps_list="$ibm_cf_apps_list ${green}$ibm_cf_apps_count.${normal}${indent_6}APP: ${green}$name${normal} 用户: ${green}$user_email${normal} 路由数: ${green}$routes_count${normal}\n\n" 26 | done < <($JQ_FILE '.cf.apps[]|[.name,.user_email,(.routes|length),([.routes[].hostname]|join("|")),([.routes[].port]|join("|")),([.routes[].domain]|join("|")),([.routes[].path]|join("|"))]|join("^")' "$IBM_CONFIG") 27 | return 0 28 | } 29 | -------------------------------------------------------------------------------- /src/ibm_cf/get_users: -------------------------------------------------------------------------------- 1 | IbmGetUsers() 2 | { 3 | ibm_users_list="" 4 | ibm_users_count=0 5 | ibm_users_email=() 6 | ibm_users_pass=() 7 | ibm_users_region=() 8 | ibm_users_resource_group=() 9 | ibm_users_org=() 10 | ibm_users_space=() 11 | while IFS=" " read -r email pass region resource_group org space 12 | do 13 | ibm_users_count=$((ibm_users_count+1)) 14 | email=${email#\"} 15 | ibm_users_email+=("$email") 16 | ibm_users_pass+=("$pass") 17 | ibm_users_region+=("$region") 18 | ibm_users_resource_group+=("$resource_group") 19 | ibm_users_org+=("$org") 20 | space=${space%\"} 21 | ibm_users_space+=("$space") 22 | 23 | ibm_users_list="$ibm_users_list ${green}$ibm_users_count.${normal}${indent_6}地区: ${green}$region${normal} 资源组: ${green}$resource_group${normal}\n${indent_6}邮箱: ${green}$email${normal} 密码: ${green}$pass${normal}\n${indent_6}组织: ${green}$org${normal} 空间: ${green}$space${normal}\n\n" 24 | done < <($JQ_FILE '.users[]|[.email,.pass,.region,.resource_group,.org,.space]|join(" ")' "$IBM_CONFIG") 25 | return 0 26 | } 27 | -------------------------------------------------------------------------------- /src/ibm_cf/list_apps: -------------------------------------------------------------------------------- 1 | IbmListCfApps() 2 | { 3 | if [ ! -s "$IBM_CONFIG" ] 4 | then 5 | Println "$error 请先添加 APP\n" && exit 1 6 | fi 7 | 8 | IbmGetCfApps 9 | 10 | if [ "$ibm_cf_apps_count" -gt 0 ] 11 | then 12 | Println "$ibm_cf_apps_list" 13 | else 14 | Println "$error 没有 APP\n" 15 | exit 1 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /src/ibm_cf/list_users: -------------------------------------------------------------------------------- 1 | IbmListUsers() 2 | { 3 | if [ ! -s "$IBM_CONFIG" ] 4 | then 5 | Println "$error 请先添加用户\n" && exit 1 6 | fi 7 | 8 | IbmGetUsers 9 | 10 | if [ "$ibm_users_count" -gt 0 ] 11 | then 12 | Println "$ibm_users_list" 13 | else 14 | Println "$error 没有用户\n" 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /src/ibm_cf/login_user: -------------------------------------------------------------------------------- 1 | IbmLoginUser() 2 | { 3 | IbmListUsers 4 | 5 | if [ "$ibm_users_count" -eq 0 ] 6 | then 7 | Println "$error 请先添加用户\n" 8 | exit 1 9 | fi 10 | 11 | echo -e "选择用户" 12 | while read -p "$i18n_default_cancel" ibm_users_num 13 | do 14 | case "$ibm_users_num" in 15 | "") 16 | Println "$i18n_canceled...\n" && exit 1 17 | ;; 18 | *[!0-9]*) 19 | Println "$error $i18n_input_correct_no\n" 20 | ;; 21 | *) 22 | if [ "$ibm_users_num" -gt 0 ] && [ "$ibm_users_num" -le "$ibm_users_count" ] 23 | then 24 | ibm_users_index=$((ibm_users_num-1)) 25 | ibm_user_email=${ibm_users_email[ibm_users_index]} 26 | ibm_user_pass=${ibm_users_pass[ibm_users_index]} 27 | ibm_user_region=${ibm_users_region[ibm_users_index]} 28 | ibm_user_resource_group=${ibm_users_resource_group[ibm_users_index]} 29 | ibm_user_org=${ibm_users_org[ibm_users_index]} 30 | ibm_user_space=${ibm_users_space[ibm_users_index]} 31 | break 32 | else 33 | Println "$error $i18n_input_correct_no\n" 34 | fi 35 | ;; 36 | esac 37 | done 38 | 39 | Println "$info 登录账号: $ibm_user_email [ $ibm_user_region ]" 40 | ibmcloud login -u "$ibm_user_email" -p "$ibm_user_pass" -r "$ibm_user_region" -g "$ibm_user_resource_group" 41 | ibmcloud target -o "$ibm_user_org" -s "$ibm_user_space" 42 | } 43 | -------------------------------------------------------------------------------- /src/ibm_cf/set_user: -------------------------------------------------------------------------------- 1 | IbmSetUserEmail() 2 | { 3 | Println "请输入用户邮箱" 4 | read -p "$i18n_default_cancel" ibm_user_email 5 | [ -z "$ibm_user_email" ] && Println "$i18n_canceled...\n" && exit 1 6 | if [[ -n $($JQ_FILE '.users[]|select(.email=="'"$ibm_user_email"'")' "$IBM_CONFIG") ]] 7 | then 8 | Println "$error 用户已经存在\n" 9 | exit 1 10 | fi 11 | Println " 用户邮箱: ${green} $ibm_user_email ${normal}\n" 12 | } 13 | 14 | IbmSetUserPass() 15 | { 16 | Println "请输入用户密码" 17 | read -p "$i18n_default_cancel" ibm_user_pass 18 | [ -z "$ibm_user_pass" ] && Println "$i18n_canceled...\n" && exit 1 19 | Println " 用户密码: ${green} $ibm_user_pass ${normal}\n" 20 | } 21 | 22 | IbmSetUserRegion() 23 | { 24 | ibmcloud regions 25 | Println "请输入账号所在区域名称" 26 | read -p "(默认: us-south): " ibm_user_region 27 | ibm_user_region=${ibm_user_region:-us-south} 28 | Println " 区域: ${green} $ibm_user_region ${normal}\n" 29 | } 30 | 31 | IbmSetUserResourceGroup() 32 | { 33 | ibmcloud resource groups 34 | Println "请输入资源组名称" 35 | read -p "(默认: Default): " ibm_user_resource_group 36 | ibm_user_resource_group=${ibm_user_resource_group:-Default} 37 | Println " 资源组: ${green} $ibm_user_resource_group ${normal}\n" 38 | } 39 | 40 | IbmSetUserOrg() 41 | { 42 | ibmcloud account orgs 43 | Println "请输入组织名称" 44 | read -p "(默认: $ibm_user_email): " ibm_user_org 45 | ibm_user_org=${ibm_user_org:-$ibm_user_email} 46 | Println " 组织: ${green} $ibm_user_org ${normal}\n" 47 | } 48 | 49 | IbmSetUserSpace() 50 | { 51 | ibmcloud account spaces 52 | Println "请输入空间名称" 53 | read -p "(默认: dev): " ibm_user_space 54 | ibm_user_space=${ibm_user_space:-dev} 55 | Println " 空间: ${green} $ibm_user_space ${normal}\n" 56 | } 57 | -------------------------------------------------------------------------------- /src/ibm_cf/update_v2ray: -------------------------------------------------------------------------------- 1 | IbmUpdateV2ray() 2 | { 3 | if [ ! -d "$IBM_APPS_ROOT/ibm_$v2ray_name" ] 4 | then 5 | Println "$error ibm $v2ray_name 未安装\n" 6 | else 7 | Println "$info 更新 ibm $v2ray_name ..." 8 | cd ~ 9 | rm -rf $v2ray_package_name-linux-64 10 | if v2ray_version=$(curl -s -L "$FFMPEG_MIRROR_LINK/$v2ray_name.json" | $JQ_FILE -r '.tag_name') && curl -L "$FFMPEG_MIRROR_LINK/$v2ray_name/$v2ray_version/$v2ray_package_name-linux-64.zip" -o "$v2ray_package_name-linux-64.zip" && unzip $v2ray_package_name-linux-64.zip -d $v2ray_package_name-linux-64 > /dev/null 11 | then 12 | mkdir -p "$IBM_APPS_ROOT/ibm_$v2ray_name" 13 | mv $v2ray_package_name-linux-64/$v2ray_name "$IBM_APPS_ROOT/ibm_$v2ray_name/" 14 | if [ "$v2ray_name" == "xray" ] 15 | then 16 | if ! curl -L "$FFMPEG_MIRROR_LINK/xray/v2ctl" -o "$IBM_APPS_ROOT/ibm_$v2ray_name/v2ctl" 17 | then 18 | Println "$error 无法连接服务器, 请稍后再试\n" 19 | exit 1 20 | fi 21 | else 22 | mv $v2ray_package_name-linux-64/v2ctl "$IBM_APPS_ROOT/ibm_$v2ray_name/" 23 | fi 24 | chmod 700 "$IBM_APPS_ROOT/ibm_$v2ray_name/$v2ray_name" 25 | chmod 700 "$IBM_APPS_ROOT/ibm_$v2ray_name/v2ctl" 26 | Println "$info ibm $v2ray_name 更新完成\n" 27 | else 28 | Println "$error 无法连接服务器, 请稍后再试\n" 29 | fi 30 | fi 31 | } 32 | -------------------------------------------------------------------------------- /src/ibm_cf/update_v2ray_config: -------------------------------------------------------------------------------- 1 | IbmUpdateV2rayConfig() 2 | { 3 | if [ ! -d "$IBM_APPS_ROOT/ibm_$v2ray_name" ] 4 | then 5 | Println "$error $v2ray_name 未安装...\n" 6 | exit 1 7 | elif [ ! -s "$V2_CONFIG" ] 8 | then 9 | cat > "$V2_CONFIG" < /dev/null 10 | then 11 | Println "$error 命令已经存在\n" 12 | exit 1 13 | fi 14 | echo 15 | alternative_options=( nginx openresty xray v2ray armbian "proxmox ve" 16 | "ibm cloud foundry" "cloudflare partner,workers" ffmpeg ) 17 | inquirer list_input_index "选择执行的脚本" alternative_options alternative_options_index 18 | commands=( NX_FILE OR_FILE X_FILE V2_FILE ARM_FILE PVE_FILE IBM_FILE CF_FILE SH_FILE ) 19 | ln -s ${!commands[alternative_options_index]} /usr/bin/$name 20 | Println "$info 自定义命令 $name 添加成功\n" 21 | exit 0 22 | -------------------------------------------------------------------------------- /src/iptv/cmd_astro: -------------------------------------------------------------------------------- 1 | Println "$info 检测 astro ..." 2 | 3 | SetDelimiters 4 | IFS=$'\002\t' read -r m_id m_title m_description m_is_hd m_language < <( 5 | JQs flat "$(curl -s -Lm 20 -H 'User-Agent: '"$USER_AGENT_BROWSER"'' https://contenthub-api.eco.astro.com.my/channel/all.json)" '.[0].response' ' 6 | . as $response | reduce ({id,title,description,isHd,language}|keys_unsorted[]) as $key ([]; 7 | $response[$key] as $val | if $val then 8 | . + [$val + "'"${delimiters[0]}"'\u0002"] 9 | else 10 | . + ["\u0002"] 11 | end 12 | )|@tsv' "${delimiters[@]}") 13 | 14 | IFS="${delimiters[0]}" read -ra chnls_id <<< "$m_id" 15 | IFS="${delimiters[0]}" read -ra chnls_title <<< "$m_title" 16 | IFS="${delimiters[0]}" read -ra chnls_description <<< "$m_description" 17 | IFS="${delimiters[0]}" read -ra chnls_is_hd <<< "$m_is_hd" 18 | IFS="${delimiters[0]}" read -ra chnls_language <<< "$m_language" 19 | 20 | chnls_list="" 21 | for((i=0;i<${#chnls_id[@]};i++)); 22 | do 23 | if [ "${chnls_is_hd[i]}" = true ] 24 | then 25 | is_hd="${green}是${normal}" 26 | else 27 | is_hd="${red}否${normal}" 28 | fi 29 | chnls_list="$chnls_list ${green}$((i+1)).${normal}${indent_6}频道ID: ${green}${chnls_id[i]}${normal} 频道名称: ${green}${chnls_title[i]}${normal}\n${indent_6}高清: ${green}$is_hd${normal} 语言: ${green}${chnls_language[i]}${normal}\n${indent_6}${chnls_description[i]}\n\n" 30 | done 31 | 32 | Println "$chnls_list" 33 | exit 0 34 | -------------------------------------------------------------------------------- /src/iptv/cmd_c: -------------------------------------------------------------------------------- 1 | to_locale=${2:-} 2 | new_locale="" 3 | 4 | if [ -n "$to_locale" ] 5 | then 6 | new_locale=${to_locale%.*} 7 | 8 | if [[ $new_locale =~ ^(.+)[-|_](.+)$ ]] 9 | then 10 | new_locale=$(tr '[:upper:]' '[:lower:]' <<< "${BASH_REMATCH[1]}")_$(tr '[:lower:]' '[:upper:]' <<< "${BASH_REMATCH[2]}") 11 | else 12 | new_locale=$(tr '[:upper:]' '[:lower:]' <<< "$new_locale") 13 | fi 14 | 15 | if [[ $to_locale =~ \. ]] 16 | then 17 | new_locale="$new_locale.${to_locale#*.}" 18 | fi 19 | fi 20 | 21 | i18nInstall "$new_locale" 22 | exit 0 23 | -------------------------------------------------------------------------------- /src/iptv/cmd_curl: -------------------------------------------------------------------------------- 1 | Include utils/go "$@" 2 | 3 | Include utils/nodejs "$@" 4 | 5 | echo 6 | curl_options=( '安装/设置 curl impersonate' '编译 curl impersonate' '编译 node-libcurl (使用 curl impersonate)' ) 7 | inquirer list_input_index "选择操作" curl_options curl_options_index 8 | 9 | if [ "$curl_options_index" -eq 0 ] 10 | then 11 | CurlImpersonateUpdate 12 | 13 | echo 14 | elif [ "$curl_options_index" -eq 1 ] 15 | then 16 | CurlImpersonateCompile 17 | else 18 | NodeLibcurlImpersonateCompile 19 | fi 20 | 21 | exit 0 22 | -------------------------------------------------------------------------------- /src/iptv/cmd_debug: -------------------------------------------------------------------------------- 1 | if [[ ${2:-1} == "1" ]] 2 | then 3 | flag=true 4 | else 5 | flag=false 6 | fi 7 | 8 | sed -i "0,/sh_debug=.*/s//sh_debug=$flag/" "$SH_FILE" 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /src/iptv/cmd_default: -------------------------------------------------------------------------------- 1 | [ ! -d "$IPTV_ROOT" ] && Println "$error 尚未安装, 请检查 !\n" && exit 1 2 | echo 3 | ExitOnList n "请确保已经升级到最新脚本, 是否继续" 4 | ShFallback 5 | channels="" 6 | while IFS= read -r line 7 | do 8 | if [[ $line == *\"pid\":* ]] 9 | then 10 | pid=${line#*:} 11 | pid=${pid%,*} 12 | rand_pid=$pid 13 | while [[ -n $($JQ_FILE '.channels[]|select(.pid=='"$rand_pid"')' "$CHANNELS_FILE") ]] 14 | do 15 | true & 16 | rand_pid=$! 17 | done 18 | line=${line//$pid/$rand_pid} 19 | fi 20 | channels="$channels$line" 21 | done < <(curl -s -Lm 20 "$SH_FALLBACK/$DEFAULT_DEMOS") 22 | [ -z "$channels" ] && Println "$error 暂时无法连接服务器, 请稍后再试 !\n" && exit 1 23 | SetDelimiters 24 | IFS="${delimiters[0]}"$'\t' read -r -a channels_name < <(JQs flat "$channels" '' '.channel_name' "${delimiters[@]}") 25 | echo 26 | channels_name+=("全部") 27 | inquirer list_input "选择添加的频道" channels_name channel_name 28 | if [ "$channel_name" != "全部" ] 29 | then 30 | channels=$($JQ_FILE '[.[]|select(.channel_name=="'"$channel_name"'")]' <<< "$channels") 31 | fi 32 | jq_path='["channels"]' 33 | JQ add "$CHANNELS_FILE" "$channels" 34 | Println "$info 频道添加成功 !\n" 35 | exit 0 36 | -------------------------------------------------------------------------------- /src/iptv/cmd_e: -------------------------------------------------------------------------------- 1 | [ ! -d "$IPTV_ROOT" ] && Println "$error 尚未安装, 请检查 !\n" && exit 1 2 | editor "$CHANNELS_FILE" && exit 0 3 | -------------------------------------------------------------------------------- /src/iptv/cmd_ed: -------------------------------------------------------------------------------- 1 | DepInstall vim 2 | if [ "$dist" == "rpm" ] 3 | then 4 | alternatives --config editor 5 | else 6 | update-alternatives --config editor 7 | fi 8 | exit 0 9 | -------------------------------------------------------------------------------- /src/iptv/cmd_ee: -------------------------------------------------------------------------------- 1 | [ ! -d "$IPTV_ROOT" ] && Println "$error 尚未安装, 请检查 !\n" && exit 1 2 | GetDefault 3 | [ -z "$d_sync_file" ] && Println "$error sync_file 未设置, 请检查 !\n" && exit 1 4 | echo 5 | edit_options=($d_sync_file) 6 | inquirer list_input "选择修改的文件" edit_options edit_option 7 | editor "$edit_option" 8 | exit 0 9 | -------------------------------------------------------------------------------- /src/iptv/cmd_monitor: -------------------------------------------------------------------------------- 1 | [ ! -d "$IPTV_ROOT" ] && Println "$error 尚未安装, 请先安装 !" && exit 1 2 | [ ! -d "${MONITOR_LOG%/*}" ] && MONITOR_LOG="$HOME/monitor.log" 3 | 4 | cmd=${2:-} 5 | 6 | case $cmd in 7 | "s"|"stop") 8 | MonitorStop 9 | ;; 10 | "l"|"log") 11 | MonitorList "${3:-}" 12 | ;; 13 | *) 14 | MonitorStart 15 | ;; 16 | esac 17 | exit 0 18 | -------------------------------------------------------------------------------- /src/iptv/cmd_singtel: -------------------------------------------------------------------------------- 1 | Println "$info 检测 singteltv ..." 2 | while IFS= read -r line 3 | do 4 | if [[ $line =~ epgEndPoint ]] 5 | then 6 | line=${line#*epgEndPoint":"} 7 | epg_end_point=${line%%"*} 8 | line=${line#*tvChannelLists":} 9 | tv_channel_lists=${line%%,"errorMessage*} 10 | tv_channel_lists=${tv_channel_lists//"/\"} 11 | $JQ_FILE -r '.[]|[ 12 | (.title // "空"), 13 | (.channelId // "空"), 14 | (.language // []|join(",")) 15 | ]|@tsv' <<< "$tv_channel_lists" 16 | break 17 | fi 18 | done < <(curl -s -L "https://www.singtel.com/personal/products-services/tv/tv-programme-guide" 2> /dev/null) 19 | exit 0 20 | -------------------------------------------------------------------------------- /src/iptv/delete_channel: -------------------------------------------------------------------------------- 1 | DelChannel() 2 | { 3 | ListChannels 4 | InputChannelsIndex 5 | 6 | for chnl_pid in "${chnls_pid_chosen[@]}" 7 | do 8 | GetChannel 9 | 10 | if [ "${kind:-}" == "flv" ] 11 | then 12 | if [ "$chnl_flv_status" == "on" ] 13 | then 14 | StopChannel 15 | fi 16 | elif [ "$chnl_status" == "on" ] 17 | then 18 | StopChannel 19 | fi 20 | 21 | jq_path='["channels"]' 22 | JQ delete "$CHANNELS_FILE" pid "$chnl_pid" 23 | 24 | rm -f "$FFMPEG_LOG_ROOT/$chnl_pid.log" 25 | rm -f "$FFMPEG_LOG_ROOT/$chnl_pid.err" 26 | rm -f "$FFMPEG_LOG_ROOT/$chnl_pid.pid" 27 | 28 | Println "$info 频道 [ $chnl_channel_name ] 删除成功 !\n" 29 | done 30 | } 31 | -------------------------------------------------------------------------------- /src/iptv/edit_default: -------------------------------------------------------------------------------- 1 | EditDefault() 2 | { 3 | jq_path='["default","'"$1"'"]' 4 | 5 | if [ -n "${2:-}" ] 6 | then 7 | JQ update "$CHANNELS_FILE" "$2" 8 | else 9 | JQ update "$CHANNELS_FILE" "${!1}" 10 | fi 11 | 12 | Println "$info $1 修改成功\n" 13 | } 14 | -------------------------------------------------------------------------------- /src/iptv/filter_string: -------------------------------------------------------------------------------- 1 | FilterString() 2 | { 3 | global_options=() 4 | global_flags=( 5 | cpuflags y n filter_threads stats stats_period progress debug_ts qphist benchmark 6 | benchmark_all timelimit dump hex filter_complex filter_complex_threads lavfi 7 | filter_complex_script sdp_file abort_on max_error_rate xerror auto_conversion_filters 8 | nostats nostdin hide_banner loglevel 9 | ) 10 | 11 | for var in "${@}" 12 | do 13 | #var_new=${!var//[\^\`]/-} 14 | var_new=${!var} 15 | 16 | var_parse="$var_new" 17 | if [ -n "$var_parse" ] 18 | then 19 | for global_flag in "${global_flags[@]}" 20 | do 21 | if [[ $var_parse =~ (.*)"-$global_flag"$ ]] 22 | then 23 | global_options+=("-$global_flag") 24 | var_parse="${BASH_REMATCH[1]}" 25 | elif [[ $var_parse =~ (.*)"-$global_flag "([^ -]*)(.*) ]] 26 | then 27 | global_options+=("-$global_flag") 28 | [ -n "${BASH_REMATCH[2]}" ] && global_options+=("${BASH_REMATCH[2]}") 29 | var_parse="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" 30 | fi 31 | done 32 | fi 33 | 34 | read -r ${var}_command <<< "$var_parse" 35 | 36 | read -r ${var?} <<< "$var_new" 37 | done 38 | } 39 | -------------------------------------------------------------------------------- /src/iptv/list_monitor: -------------------------------------------------------------------------------- 1 | MonitorList() 2 | { 3 | if [ -s "$MONITOR_LOG" ] 4 | then 5 | Println "$info 监控日志: " 6 | count=0 7 | log="" 8 | last_line="" 9 | printf -v this_hour '%(%H)T' -1 10 | while IFS= read -r line 11 | do 12 | if [ "$count" -lt "${1:-10}" ] 13 | then 14 | message=${line#* } 15 | message=${message#* } 16 | if [ -z "$last_line" ] 17 | then 18 | count=$((count+1)) 19 | log="$line" 20 | last_line="$message" 21 | elif [ "$message" != "$last_line" ] 22 | then 23 | count=$((count+1)) 24 | log="$line\n$log" 25 | last_line="$message" 26 | fi 27 | fi 28 | 29 | if [ "${line:2:1}" == "-" ] 30 | then 31 | hour=${line:6:2} 32 | elif [ "${line:2:1}" == ":" ] 33 | then 34 | hour=${line:0:2} 35 | fi 36 | 37 | if [ -n "${hour:-}" ] && [ "$hour" != "$this_hour" ] && [ "$count" -eq "${1:-10}" ] 38 | then 39 | break 40 | elif [ -n "${hour:-}" ] && [ "$hour" == "$this_hour" ] && [[ $line == *"计划重启时间"* ]] 41 | then 42 | [ -z "${found_line:-}" ] && found_line="$line" 43 | fi 44 | done < <(awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' "$MONITOR_LOG") 45 | Println "$log" 46 | [ -n "${found_line:-}" ] && Println "${green}${found_line#* }${normal}" 47 | echo 48 | fi 49 | if [ -s "$IP_LOG" ] 50 | then 51 | Println "$info AntiDDoS 日志: " 52 | tail -n 10 "$IP_LOG" 53 | fi 54 | if [ ! -s "$MONITOR_LOG" ] && [ ! -s "$IP_LOG" ] 55 | then 56 | Println "$error 无日志\n" 57 | fi 58 | } 59 | -------------------------------------------------------------------------------- /src/iptv/menu_color: -------------------------------------------------------------------------------- 1 | echo 2 | color_options=( '设置颜色' '恢复默认' ) 3 | inquirer list_input_index "选择操作" color_options color_options_index 4 | 5 | if [ "$color_options_index" -eq 1 ] 6 | then 7 | sed -E -i '/(bg_black|red|green|yellow|blue|cyan|white)=/d' "$i18n_FILE" 8 | Println "$info 颜色重置成功\n" 9 | exit 0 10 | fi 11 | 12 | echo 13 | color_options=( "${red}默认红色文字${normal}" "${green}默认绿色文字${normal}" "${yellow}默认黄色文字${normal}" 14 | "${blue}默认蓝色文字${normal}" "${cyan}默认青色文字${normal}" "${white}默认白色文字${normal}" '默认黑色背景' ) 15 | inquirer list_input_index "选择修改内容" color_options color_options_index 16 | 17 | echo 18 | inquirer color_pick "设置 ${color_options[color_options_index]}" color_pick 19 | 20 | if [ "$color_options_index" -eq 0 ] 21 | then 22 | sed -i '/red=/d' "$i18n_FILE" 23 | printf "red='%s'" "$color_pick" >> "$i18n_FILE" 24 | elif [ "$color_options_index" -eq 1 ] 25 | then 26 | sed -i '/green=/d' "$i18n_FILE" 27 | printf "green='%s'" "$color_pick" >> "$i18n_FILE" 28 | elif [ "$color_options_index" -eq 2 ] 29 | then 30 | sed -i '/yellow=/d' "$i18n_FILE" 31 | printf "yellow='%s'" "$color_pick" >> "$i18n_FILE" 32 | elif [ "$color_options_index" -eq 3 ] 33 | then 34 | sed -i '/blue=/d' "$i18n_FILE" 35 | printf "blue='%s'" "$color_pick" >> "$i18n_FILE" 36 | elif [ "$color_options_index" -eq 4 ] 37 | then 38 | sed -i '/cyan=/d' "$i18n_FILE" 39 | printf "cyan='%s'" "$color_pick" >> "$i18n_FILE" 40 | elif [ "$color_options_index" -eq 5 ] 41 | then 42 | sed -i '/white=/d' "$i18n_FILE" 43 | printf "white='%s'" "$color_pick" >> "$i18n_FILE" 44 | else 45 | sed -i '/bg_black=/d' "$i18n_FILE" 46 | printf "bg_black='%s'" "$color_pick" >> "$i18n_FILE" 47 | fi 48 | 49 | Println "$info 颜色设置成功\n" 50 | 51 | exit 0 52 | -------------------------------------------------------------------------------- /src/iptv/menu_iptv: -------------------------------------------------------------------------------- 1 | Menu() 2 | { 3 | color=${color:-${green}} 4 | 5 | if [ -z "${kind:-}" ] 6 | then 7 | title="HLS" 8 | msg=$(gettext "输入: f 切换到 FLV 面板, v 切换到 VIP 面板") 9 | elif [ "$kind" == "flv" ] 10 | then 11 | title="FLV" 12 | msg=$(gettext "输入: h 切换到 HLS 面板, v 切换到 VIP 面板") 13 | fi 14 | 15 | Println " ${dim_underlined}[AI]OS | @woniuzfb${normal} 16 | 17 | `gettext \"IPTV 一键管理脚本\"` ${red}[v$sh_ver]${normal} 18 | 19 | ${color}1.${normal} `gettext \"安装\"` 20 | ${color}2.${normal} `gettext \"卸载\"` 21 | ${color}3.${normal} `gettext \"升级\"` 22 | ———————————— 23 | ${color}4.${normal} `gettext \"查看频道\"` 24 | ${color}5.${normal} `gettext \"添加频道\"` 25 | ${color}6.${normal} `gettext \"修改频道\"` 26 | ${color}7.${normal} `gettext \"开关频道\"` 27 | ${color}8.${normal} `gettext \"重启频道\"` 28 | ${color}9.${normal} `gettext \"查看日志\"` 29 | ${color}10.${normal} `gettext \"删除频道\"` 30 | ${color}11.${normal} `gettext \"设置计划\"` 31 | ${color}12.${normal} `gettext \"设置监控\"` 32 | ${color}13.${normal} `gettext \"修改默认\"` 33 | 34 | `eval_gettext \"\\\$tip 当前: \\\${green}\\\$title\\\${normal} 面板\"` 35 | ${tip} $msg\n\n" 36 | read -p "`gettext \"输入序号\"` [1-13]: " menu_num 37 | case "$menu_num" in 38 | h) 39 | kind="" 40 | color=${green} 41 | Menu 42 | ;; 43 | f) 44 | kind="flv" 45 | color=${blue} 46 | Menu 47 | ;; 48 | v) 49 | VipMenu 50 | ;; 51 | 1) Install 52 | ;; 53 | 2) Uninstall 54 | ;; 55 | 3) Update 56 | ;; 57 | 4) ViewChannel 58 | ;; 59 | 5) AddChannel 60 | ;; 61 | 6) EditChannelMenu 62 | ;; 63 | 7) ToggleChannel 64 | ;; 65 | 8) RestartChannel 66 | ;; 67 | 9) ViewChannelLog 68 | ;; 69 | 10) DelChannel 70 | ;; 71 | 11) ScheduleMenu 72 | ;; 73 | 12) MonitorMenu 74 | ;; 75 | 13) EditDefaultMenu 76 | ;; 77 | *) Println "$error $i18n_input_correct_number [1-13]\n" 78 | ;; 79 | esac 80 | } 81 | -------------------------------------------------------------------------------- /src/iptv/menu_schedule: -------------------------------------------------------------------------------- 1 | ScheduleMenu() 2 | { 3 | echo 4 | chnls_schedule_options=( '查看' '添加' '修改' '排序' '删除' ) 5 | inquirer list_input_index "请选择" chnls_schedule_options chnls_schedule_options_index 6 | 7 | if [ "$chnls_schedule_options_index" -eq 0 ] 8 | then 9 | ListChannelsSchedule 10 | 11 | if [ -z "$chnls_schedule_list" ] 12 | then 13 | Println "$error 请先添加频道计划\n" 14 | exit 1 15 | fi 16 | elif [ "$chnls_schedule_options_index" -eq 1 ] 17 | then 18 | AddChannelsSchedule 19 | elif [ "$chnls_schedule_options_index" -eq 2 ] 20 | then 21 | EditChannelsSchedule 22 | elif [ "$chnls_schedule_options_index" -eq 3 ] 23 | then 24 | SortChannelsSchedule 25 | else 26 | DelChannelsSchedule 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /src/iptv/menu_vip: -------------------------------------------------------------------------------- 1 | VipMenu() 2 | { 3 | [ ! -d "$IPTV_ROOT" ] && Println "`eval_gettext \"\\\$error 请先输入 tv 安装 !\"`\n" && exit 1 4 | if [ ! -f "$IPTV_ROOT/VIP" ] 5 | then 6 | VipUserMenu 7 | return 0 8 | fi 9 | 10 | Println " `gettext \"VIP 面板\"` 11 | 12 | ${red}1.${normal} `gettext \"查看 VIP 用户\"` 13 | ${red}2.${normal} `gettext \"添加 VIP 用户\"` 14 | ${red}3.${normal} `gettext \"设置 VIP 用户\"` 15 | ${red}4.${normal} `gettext \"查看 VIP 频道\"` 16 | ${red}5.${normal} `gettext \"添加 VIP 频道\"` 17 | ${red}6.${normal} `gettext \"部署 VIP 频道\"` 18 | ${red}7.${normal} `gettext \"设置 VIP 频道\"` 19 | ${red}8.${normal} `gettext \"查看 VIP 服务器\"` 20 | ${red}9.${normal} `gettext \"添加 VIP 服务器\"` 21 | ${red}10.${normal} `gettext \"设置 VIP 服务器\"` 22 | ${red}11.${normal} `gettext \"删除 VIP 用户\"` 23 | ${red}12.${normal} `gettext \"删除 VIP 频道\"` 24 | ${red}13.${normal} `gettext \"删除 VIP 服务器\"` 25 | ${red}14.${normal} `gettext \"开启 VIP\"` 26 | ${red}15.${normal} `gettext \"关闭 VIP\"` 27 | 28 | `eval_gettext \"\\\$tip 输入: h 切换到 HLS 面板, f 切换到 FLV 面板\"`\n\n" 29 | 30 | read -p "`gettext \"输入序号\"` [1-15]: " vip_menu_num 31 | 32 | case "$vip_menu_num" in 33 | h) 34 | kind="" 35 | color=${green} 36 | Menu 37 | ;; 38 | f) 39 | kind="flv" 40 | color=${blue} 41 | Menu 42 | ;; 43 | 1) VipListUser 44 | ;; 45 | 2) VipAddUser 46 | ;; 47 | 3) VipEditUser 48 | ;; 49 | 4) VipListChannel 50 | ;; 51 | 5) VipAddChannel 52 | ;; 53 | 6) VipDeployChannel 54 | ;; 55 | 7) VipEditChannel 56 | ;; 57 | 8) VipListHosts 58 | ;; 59 | 9) VipAddHost 60 | ;; 61 | 10) VipEditHost 62 | ;; 63 | 11) VipDelUser 64 | ;; 65 | 12) VipDelChannel 66 | ;; 67 | 13) VipDelHost 68 | ;; 69 | 14) VipEnable 70 | ;; 71 | 15) VipDisable 72 | ;; 73 | *) Println "$error $i18n_input_correct_number [1-15]\n" 74 | ;; 75 | esac 76 | } 77 | -------------------------------------------------------------------------------- /src/iptv/menu_vip_user: -------------------------------------------------------------------------------- 1 | VipUserMenu() 2 | { 3 | Println " `gettext \"VIP 面板\"` 4 | 5 | ${red}1.${normal} `gettext \"查看 VIP 频道\"` 6 | ${red}2.${normal} `gettext \"输入 VIP 授权码\"` 7 | 8 | `eval_gettext \"\\\$tip 输入: h 切换到 HLS 面板, f 切换到 FLV 面板\"`\n\n" 9 | read -p "`gettext \"输入序号\"` [1-2]: " vip_menu_num 10 | case "$vip_menu_num" in 11 | h) 12 | kind="" 13 | color=${green} 14 | Menu 15 | ;; 16 | f) 17 | kind="flv" 18 | color=${blue} 19 | Menu 20 | ;; 21 | 1) VipListUserChannel 22 | ;; 23 | 2) VipVerifyLicense 24 | ;; 25 | *) Println "$error $i18n_input_correct_number [1-2]\n" 26 | ;; 27 | esac 28 | } 29 | -------------------------------------------------------------------------------- /src/iptv/parse_schedule: -------------------------------------------------------------------------------- 1 | ParseSchedule() 2 | { 3 | schedule=$($JQ_FILE 'unique_by(.start_time)' <<< "$schedule") 4 | 5 | SetDelimiters 6 | IFS=$'\003\t' read -r m_title m_time m_start_time m_end_time < <(JQs flat "$schedule" '' ' 7 | . as $schedles | reduce ({title,time,start_time,end_time}|keys_unsorted[]) as $key ([]; 8 | $schedles[$key] as $val | if $val then 9 | . + [$val + "\u0002\u0003"] 10 | else 11 | . + ["\u0003"] 12 | end 13 | )|@tsv' "${delimiters[@]}") 14 | 15 | IFS="${delimiters[1]}" read -ra ${chnl_id}_title <<< "$m_title" 16 | IFS="${delimiters[1]}" read -ra ${chnl_id}_time <<< "$m_time" 17 | IFS="${delimiters[1]}" read -ra ${chnl_id}_start_time <<< "$m_start_time" 18 | IFS="${delimiters[1]}" read -ra ${chnl_id}_end_time <<< "$m_end_time" 19 | 20 | schedule_title=("${chnl_id}_title"[@]) 21 | schedule_title=("${!schedule_title}") 22 | schedule_time=("${chnl_id}_time"[@]) 23 | schedule_time=("${!schedule_time}") 24 | schedule_start_time=("${chnl_id}_start_time"[@]) 25 | schedule_start_time=("${!schedule_start_time}") 26 | schedule_end_time=("${chnl_id}_end_time"[@]) 27 | schedule_end_time=("${!schedule_end_time}") 28 | schedule_indices=("${!schedule_title[@]}") 29 | 30 | for schedule_index in "${schedule_indices[@]}" 31 | do 32 | if [ "$((${schedule_end_time[schedule_index]}-${schedule_start_time[schedule_index]}))" -lt 5400 ] 33 | then 34 | continue 35 | fi 36 | schedules_chnl_id+=("$chnl_id") 37 | schedules_start_time+=("${schedule_start_time[schedule_index]}") 38 | schedules_end_time+=("${schedule_end_time[schedule_index]}") 39 | schedules_title+=("${schedule_title[schedule_index]}") 40 | schedules_list+=("${schedule_title[schedule_index]} ${green}[$chnl_id]${normal} ${blue}[${schedule_time[schedule_index]}]${normal}$mark") 41 | done 42 | } 43 | -------------------------------------------------------------------------------- /src/iptv/rand_name: -------------------------------------------------------------------------------- 1 | RandOutputDirName() 2 | { 3 | while :;do 4 | output_dir_name=$(RandStr) 5 | if [[ -z $($JQ_FILE '.channels[] | select(.output_dir_name=="'"$output_dir_name"'")' "$CHANNELS_FILE") ]] 6 | then 7 | echo "$output_dir_name" 8 | break 9 | fi 10 | done 11 | } 12 | 13 | RandPlaylistName() 14 | { 15 | while :;do 16 | playlist_name=$(RandStr) 17 | if [[ -z $($JQ_FILE '.channels[] | select(.playlist_name=="'"$playlist_name"'")' "$CHANNELS_FILE") ]] 18 | then 19 | echo "$playlist_name" 20 | break 21 | fi 22 | done 23 | } 24 | 25 | RandSegDirName() 26 | { 27 | while :;do 28 | seg_dir_name=$(RandStr) 29 | if [[ -z $($JQ_FILE '.channels[] | select(.seg_dir_name=="'"$seg_dir_name"'")' "$CHANNELS_FILE") ]] 30 | then 31 | echo "$seg_dir_name" 32 | break 33 | fi 34 | done 35 | } 36 | -------------------------------------------------------------------------------- /src/iptv/restart_channel: -------------------------------------------------------------------------------- 1 | RestartChannel() 2 | { 3 | ListChannels 4 | InputChannelsIndex 5 | 6 | for chnl_pid in "${chnls_pid_chosen[@]}" 7 | do 8 | GetChannel 9 | 10 | if [ "$chnl_status" == "on" ] || [ "$chnl_flv_status" == "on" ] || [ -d "$chnl_output_dir_root" ] 11 | then 12 | StopChannel 13 | fi 14 | 15 | CheckIfXtreamCodes 16 | 17 | if [ "$to_try" -eq 1 ] 18 | then 19 | continue 20 | fi 21 | 22 | StartChannel 23 | done 24 | } 25 | -------------------------------------------------------------------------------- /src/iptv/start_monitor: -------------------------------------------------------------------------------- 1 | MonitorStart() 2 | { 3 | if [ -s "$IPTV_ROOT/monitor.pid" ] || [ -s "$IPTV_ROOT/antiddos.pid" ] 4 | then 5 | Println "$error 监控已经在运行 !\n" && exit 1 6 | else 7 | if { [ -d "/usr/local/openresty" ] && [ ! -d "/usr/local/nginx" ]; } || { [ -s "/usr/local/openresty/nginx/logs/nginx.pid" ] && kill -0 "$(< "/usr/local/openresty/nginx/logs/nginx.pid")" 2> /dev/null ; } 8 | then 9 | nginx_prefix="/usr/local/openresty/nginx" 10 | nginx_name="openresty" 11 | nginx_ctl="or" 12 | elif { [ -d "/usr/local/nginx" ] && [ ! -d "/usr/local/openresty" ]; } || { [ -s "/usr/local/nginx/logs/nginx.pid" ] && kill -0 "$(< "/usr/local/nginx/logs/nginx.pid")" 2> /dev/null ; } 13 | then 14 | nginx_prefix="/usr/local/nginx" 15 | nginx_name="nginx" 16 | nginx_ctl="nx" 17 | else 18 | echo 19 | inquirer list_input_index "没有检测到运行的 nginx, 是否使用 openresty" yn_options yn_index 20 | 21 | if [ "$yn_index" -eq 0 ] 22 | then 23 | nginx_prefix="/usr/local/openresty/nginx" 24 | nginx_name="openresty" 25 | nginx_ctl="or" 26 | else 27 | nginx_prefix="/usr/local/nginx" 28 | nginx_name="nginx" 29 | nginx_ctl="nx" 30 | fi 31 | fi 32 | 33 | NGINX_FILE="$nginx_prefix/sbin/nginx" 34 | 35 | MonitorSet 36 | 37 | i18nGetMsg get_channel 38 | 39 | if [ "$sh_debug" = true ] 40 | then 41 | ( Monitor ) 42 | else 43 | ( Monitor ) > /dev/null 2> /dev/null < /dev/null & 44 | fi 45 | 46 | Println "$info 监控启动成功 !\n" 47 | AntiDDoSSet 48 | 49 | if [ "$sh_debug" = true ] 50 | then 51 | ( AntiDDoS ) 52 | else 53 | ( AntiDDoS ) > /dev/null 2> /dev/null < /dev/null & 54 | fi 55 | 56 | Println "$info AntiDDoS 启动成功 !\n" 57 | rm -f "$IPTV_ROOT/ip.pid" 58 | fi 59 | } 60 | -------------------------------------------------------------------------------- /src/iptv/toggle_channel: -------------------------------------------------------------------------------- 1 | ToggleChannel() 2 | { 3 | ListChannels 4 | InputChannelsIndex 5 | 6 | for chnl_pid in "${chnls_pid_chosen[@]}" 7 | do 8 | GetChannel 9 | 10 | if { [ -z "${kind:-}" ] && [ "$chnl_status" == "off" ]; } || { [ "${kind:-}" == "flv" ] && [ "$chnl_flv_status" == "off" ]; } 11 | then 12 | if [ -d "$chnl_output_dir_root" ] 13 | then 14 | StopChannel 15 | fi 16 | 17 | CheckIfXtreamCodes 18 | 19 | if [ "$to_try" -eq 1 ] 20 | then 21 | continue 22 | fi 23 | 24 | StartChannel 25 | else 26 | StopChannel 27 | fi 28 | done 29 | } 30 | -------------------------------------------------------------------------------- /src/iptv/view_channel: -------------------------------------------------------------------------------- 1 | ViewChannel() 2 | { 3 | ListChannels 4 | InputChannelsIndex 5 | 6 | for chnl_pid in "${chnls_pid_chosen[@]}" 7 | do 8 | ListChannel 9 | done 10 | } 11 | -------------------------------------------------------------------------------- /src/iptv/view_log: -------------------------------------------------------------------------------- 1 | ViewChannelLog() 2 | { 3 | ListChannels 4 | InputChannelsIndex 5 | 6 | for chnl_pid in "${chnls_pid_chosen[@]}" 7 | do 8 | ListChannel 9 | 10 | Println "${green}输出日志:${normal}\n" 11 | if [ -s "$FFMPEG_LOG_ROOT/$chnl_pid.log" ] 12 | then 13 | tail -n 10 "$FFMPEG_LOG_ROOT/$chnl_pid.log" 14 | else 15 | echo "无" 16 | fi 17 | 18 | Println "${red}错误日志:${normal}\n" 19 | if [ -s "$FFMPEG_LOG_ROOT/$chnl_pid.err" ] 20 | then 21 | cat "$FFMPEG_LOG_ROOT/$chnl_pid.err" 22 | else 23 | echo "无" 24 | fi 25 | echo 26 | done 27 | } 28 | -------------------------------------------------------------------------------- /src/nginx/add_enabled: -------------------------------------------------------------------------------- 1 | NginxAddEnabled() 2 | { 3 | directive_include='{"directive":"include","args":["sites_enabled/*.conf"]}' 4 | directives=( include ) 5 | directives_val=() 6 | check_directives=() 7 | check_args=( '["sites_enabled/*.conf"]' ) 8 | 9 | NginxAddDirective 2 10 | } 11 | -------------------------------------------------------------------------------- /src/nginx/add_flv: -------------------------------------------------------------------------------- 1 | NginxAddFlv() 2 | { 3 | directive_location='{"directive":"location","args":["/flv"],"block":[]}' 4 | 5 | directives=( location ) 6 | directives_val=() 7 | check_directives=() 8 | check_args=( '["/flv"]' ) 9 | 10 | NginxAddDirective 3 11 | 12 | directive_flv_live='{"directive":"flv_live","args":["on"]}' 13 | directive_chunked_transfer_encoding='{"directive":"chunked_transfer_encoding","args":["on"]}' 14 | 15 | directives=( flv_live chunked_transfer_encoding ) 16 | directives_val=() 17 | check_directives=() 18 | check_args=() 19 | 20 | NginxAddDirective 4 21 | } 22 | -------------------------------------------------------------------------------- /src/nginx/add_http: -------------------------------------------------------------------------------- 1 | NginxAddHttp() 2 | { 3 | directive_http=' 4 | {"directive":"http","args":[],"block":[ 5 | {"directive":"include","args":["mime.types"]}, 6 | {"directive":"default_type","args":["application/octet-stream"]}, 7 | {"directive":"sendfile","args":["on"]}, 8 | {"directive":"keepalive_timeout","args":["65"]}, 9 | {"directive":"server","args":[],"block":[ 10 | {"directive":"listen","args":["80"]}, 11 | {"directive":"server_name","args":["localhost"]}, 12 | {"directive":"access_log","args":["logs/localhost-access.log"]}, 13 | {"directive":"error_log","args":["logs/localhost-error.log"]}, 14 | {"directive":"location","args":["/"],"block":[ 15 | {"directive":"root","args":["html/localhost"]}, 16 | {"directive":"index","args":["index.html","index.htm"]} 17 | ]}, 18 | {"directive":"error_page","args":["500","502","503","504","/50x.html"]}, 19 | {"directive":"location","args":["/50x.html"],"block":[ 20 | {"directive":"root","args":["html/localhost"]} 21 | ]} 22 | ]} 23 | ]}' 24 | 25 | directives=( http ) 26 | directives_val=() 27 | check_directives=() 28 | check_args=() 29 | 30 | NginxAddDirective 1 31 | } 32 | -------------------------------------------------------------------------------- /src/nginx/add_localhost: -------------------------------------------------------------------------------- 1 | NginxAddLocalhost() 2 | { 3 | directive_server=' 4 | {"directive":"server","args":[],"block":[ 5 | {"directive":"listen","args":["80"]}, 6 | {"directive":"server_name","args":["localhost"]}, 7 | {"directive":"access_log","args":["logs/localhost-access.log"]}, 8 | {"directive":"error_log","args":["logs/localhost-error.log"]}, 9 | {"directive":"location","args":["/"],"block":[ 10 | {"directive":"root","args":["html/localhost"]}, 11 | {"directive":"index","args":["index.html","index.htm"]} 12 | ]}, 13 | {"directive":"error_page","args":["500","502","503","504","/50x.html"]}, 14 | {"directive":"location","args":["/50x.html"],"block":[ 15 | {"directive":"root","args":["html/localhost"]} 16 | ]} 17 | ]}' 18 | 19 | directives=( server ) 20 | directives_val=() 21 | check_directives=() 22 | check_args=() 23 | 24 | NginxAddDirective 2 25 | } 26 | -------------------------------------------------------------------------------- /src/nginx/add_rtmp: -------------------------------------------------------------------------------- 1 | NginxAddRtmp() 2 | { 3 | directive_rtmp_auto_push='{"directive":"rtmp_auto_push","args":["on"]}' 4 | directive_rtmp_auto_push_reconnect='{"directive":"rtmp_auto_push_reconnect","args":["1s"]}' 5 | directive_rtmp_socket_dir='{"directive":"rtmp_socket_dir","args":["/tmp"]}' 6 | directive_rtmp=' 7 | {"directive":"rtmp","args":[],"block":[ 8 | {"directive":"out_queue","args":["4096"]}, 9 | {"directive":"out_cork","args":["8"]}, 10 | {"directive":"max_streams","args":["128"]}, 11 | {"directive":"timeout","args":["15s"]}, 12 | {"directive":"drop_idle_publisher","args":["10s"]}, 13 | {"directive":"log_interval","args":["120s"]}, 14 | {"directive":"log_size","args":["1m"]}, 15 | {"directive":"server","args":[],"block":[ 16 | {"directive":"listen","args":["1935"]}, 17 | {"directive":"server_name","args":["localhost"]}, 18 | {"directive":"access_log","args":["logs/flv.log"]}, 19 | {"directive":"application","args":["flv"],"block":[ 20 | {"directive":"live","args":["on"]}, 21 | {"directive":"gop_cache","args":["on"]} 22 | ]} 23 | ]} 24 | ]}' 25 | 26 | directives=( rtmp_auto_push rtmp_auto_push_reconnect rtmp_socket_dir rtmp ) 27 | directives_val=() 28 | check_directives=() 29 | check_args=() 30 | 31 | NginxAddDirective 1 32 | } 33 | -------------------------------------------------------------------------------- /src/nginx/add_samesite_none: -------------------------------------------------------------------------------- 1 | NginxAddSameSiteNone() 2 | { 3 | directive_map='{"directive":"map","args":["$http_user_agent","$samesite_none"],"block":[]}' 4 | 5 | directives=( map ) 6 | directives_val=() 7 | check_directives=() 8 | check_args=( '["$http_user_agent","$samesite_none"]' ) 9 | 10 | NginxAddDirective 2 11 | 12 | directive_default='{"directive":"default","args":["; Secure"]}' 13 | directive_chrome1='{"directive":"~Chrom[^ \\/]+\\/[1][\\d][\\d][\\.\\d]*","args":["; Secure; SameSite=None"]}' 14 | directive_chrome2='{"directive":"~Chrom[^ \\/]+\\/[89][\\d][\\.\\d]*","args":["; Secure; SameSite=None"]}' 15 | 16 | directives=( default '~Chrom[^ \\/]+\\/[89][\\d][\\.\\d]*' ) 17 | directives_val=( default chrome1 chrome2 ) 18 | check_directives=() 19 | check_args=( '["; Secure"]' ) 20 | 21 | NginxAddDirective 3 22 | } 23 | -------------------------------------------------------------------------------- /src/nginx/add_ssl: -------------------------------------------------------------------------------- 1 | NginxAddSsl() 2 | { 3 | directive_ssl_session_cache='{"directive":"ssl_session_cache","args":["shared:SSL:20m"]}' 4 | directive_ssl_session_timeout='{"directive":"ssl_session_timeout","args":["2h"]}' 5 | directive_ssl_prefer_server_ciphers='{"directive":"ssl_prefer_server_ciphers","args":["on"]}' 6 | directive_ssl_protocols='{"directive":"ssl_protocols","args":["TLSv1.2","TLSv1.3"]}' 7 | directive_ssl_ciphers='{"directive":"ssl_ciphers","args":["HIGH:!aNULL:!MD5"]}' 8 | directive_ssl_stapling='{"directive":"ssl_stapling","args":["on"]}' 9 | directive_ssl_stapling_verify='{"directive":"ssl_stapling_verify","args":["on"]}' 10 | directive_resolver='{"directive":"resolver","args":["8.8.8.8"]}' 11 | 12 | directives=( ssl_session_cache ssl_session_timeout ssl_prefer_server_ciphers ssl_protocols 13 | ssl_ciphers ssl_stapling ssl_stapling_verify resolver ) 14 | directives_val=() 15 | check_directives=() 16 | check_args=() 17 | 18 | NginxAddDirective 2 19 | } 20 | -------------------------------------------------------------------------------- /src/nginx/add_stream: -------------------------------------------------------------------------------- 1 | NginxAddStream() 2 | { 3 | directive_stream=' 4 | {"directive":"stream","args":[],"block":[ 5 | {"directive":"map","args":["$ssl_preread_server_name","$upstream"],"block":[ 6 | {"directive":"default","args":["localhost"]} 7 | ]}, 8 | {"directive":"map","args":["$ssl_preread_protocol","$ssl_proxy"],"block":[ 9 | {"directive":"default","args":["$upstream"]} 10 | ]}, 11 | {"directive":"map","args":["$ssl_preread_alpn_protocols","$proxy_pass"],"block":[ 12 | {"directive":"default","args":["$ssl_proxy"]} 13 | ]}, 14 | {"directive":"upstream","args":["localhost"],"block":[ 15 | {"directive":"server","args":["'"${upstream_localhost_server:-127.0.0.1:8884}"'"]} 16 | ]}, 17 | {"directive":"server","args":[],"block":[ 18 | {"directive":"listen","args":["443","reuseport"]}, 19 | {"directive":"listen","args":["[::]:443","reuseport"]}, 20 | {"directive":"proxy_pass","args":["$proxy_pass"]}, 21 | {"directive":"proxy_protocol","args":["on"]}, 22 | {"directive":"ssl_preread","args":["on"]} 23 | ]} 24 | ]}' 25 | 26 | directives=( stream ) 27 | directives_val=() 28 | check_directives=() 29 | check_args=() 30 | 31 | NginxAddDirective 1 32 | } 33 | -------------------------------------------------------------------------------- /src/nginx/add_upstream_nodejs: -------------------------------------------------------------------------------- 1 | NginxAddUpstreamNodejs() 2 | { 3 | directive_upstream='{"directive":"upstream","args":["nodejs"],"block":[]}' 4 | 5 | directives=( upstream ) 6 | directives_val=() 7 | check_directives=() 8 | check_args=( '["nodejs"]' ) 9 | 10 | NginxAddDirective 2 11 | 12 | directive_server='{"directive":"server","args":["127.0.0.1:'"$nodejs_port"'"]}' 13 | 14 | directives=( server ) 15 | directives_val=() 16 | check_directives=() 17 | check_args=() 18 | 19 | NginxAddDirective 3 20 | } 21 | -------------------------------------------------------------------------------- /src/nginx/add_user: -------------------------------------------------------------------------------- 1 | NginxAddUser() 2 | { 3 | directive_user='{"directive":"user","args":["'"$nginx_name"'","'"$nginx_name"'"]}' 4 | directives=( user ) 5 | directives_val=() 6 | check_directives=() 7 | check_args=() 8 | 9 | NginxAddDirective 1 10 | } 11 | -------------------------------------------------------------------------------- /src/nginx/build_conf: -------------------------------------------------------------------------------- 1 | NginxBuildConf() 2 | { 3 | if TMP_FILE=$(mktemp -q) 4 | then 5 | chmod +r "$TMP_FILE" 6 | else 7 | exit $? 8 | fi 9 | 10 | trap ' 11 | rm -f "$TMP_FILE" 12 | ' EXIT 13 | 14 | if [ "$parse_domain" -eq 1 ] 15 | then 16 | parse_out_domain=${!1} 17 | jq_path='["config",0,"parsed",0,"block"]' 18 | JQs get parse_out_domain domain_conf 19 | jq_path='["config",0,"parsed"]' 20 | JQs replace parse_out_domain "$domain_conf" 21 | echo "$parse_out_domain" > "$TMP_FILE" 22 | else 23 | echo "${!1}" > "$TMP_FILE" 24 | fi 25 | 26 | crossplane build -f --no-headers "$TMP_FILE" 27 | 28 | rm -f "$TMP_FILE" 29 | 30 | trap - EXIT 31 | } 32 | -------------------------------------------------------------------------------- /src/nginx/config_block: -------------------------------------------------------------------------------- 1 | NginxConfigBlockAliyun() 2 | { 3 | echo 4 | inquirer list_input "是否屏蔽所有阿里云ip段" ny_options block_aliyun_yn 5 | if [ "$block_aliyun_yn" == "$i18n_yes" ] 6 | then 7 | Println "输入本机IP" 8 | echo -e "$tip 多个IP用空格分隔\n" 9 | 10 | while read -p "(默认: 自动检测): " server_ip 11 | do 12 | server_ip=${server_ip:-$(GetServerIp)} 13 | if [ -z "$server_ip" ] 14 | then 15 | Println "$error 无法获取本机IP, 请手动输入\n" 16 | else 17 | Println "$info 本机IP: $server_ip\n" 18 | break 19 | fi 20 | done 21 | 22 | start=0 23 | deny_aliyun=" 24 | location ${server_live_root#*$server_root}/${LIVE_ROOT##*/} {" 25 | 26 | IFS=" " read -ra server_ips <<< "$server_ip" 27 | for ip in "${server_ips[@]}" 28 | do 29 | deny_aliyun="$deny_aliyun 30 | allow $ip;" 31 | done 32 | 33 | while IFS= read -r line 34 | do 35 | if [[ $line == *"ipTabContent"* ]] 36 | then 37 | start=1 38 | elif [ "$start" -eq 1 ] && [[ $line == *"AS45102"* ]] 39 | then 40 | line=${line#*AS45102\/} 41 | ip=${line%\"*} 42 | deny_aliyun="$deny_aliyun 43 | deny $ip;" 44 | elif [ "$start" -eq 1 ] && [[ $line == *""* ]] 45 | then 46 | break 47 | fi 48 | done < <(curl -s -Lm 10 -H "User-Agent: $USER_AGENT_BROWSER" https://ipinfo.io/AS45102) 49 | deny_aliyun="$deny_aliyun 50 | allow all;" 51 | deny_aliyun="$deny_aliyun 52 | } 53 | 54 | " 55 | fi 56 | } 57 | -------------------------------------------------------------------------------- /src/nginx/config_domain: -------------------------------------------------------------------------------- 1 | NginxConfigDomain() 2 | { 3 | NginxListDomain 4 | 5 | NginxSelectDomainServer 6 | 7 | echo 8 | domain_server_options=( '修改指令' '更新证书' '添加 flv 设置' '添加 nodejs 设置' ) 9 | inquirer list_input_index "选择操作" domain_server_options domain_server_options_index 10 | 11 | if [ "$domain_server_options_index" -eq 0 ] 12 | then 13 | NginxConfigDirective level_2 14 | elif [ "$domain_server_options_index" -eq 1 ] 15 | then 16 | AcmeCheck 17 | NginxDomainServerUpdateCrt 18 | elif [ "$domain_server_options_index" -eq 2 ] 19 | then 20 | updated=0 21 | NginxAddFlv 22 | if [ "$updated" -eq 1 ] 23 | then 24 | NginxBuildConf parse_out 25 | fi 26 | Println "$info flv 配置添加成功\n" 27 | else 28 | server_name=${nginx_domain_servers_name[nginx_domain_servers_index]} 29 | 30 | if [[ $server_name =~ , ]] 31 | then 32 | IFS="," read -r -a domains <<< "$server_name" 33 | 34 | echo 35 | inquirer list_input "选择域名: " domains server_name 36 | fi 37 | updated=0 38 | NginxAddNodejs 39 | if [ "$updated" -eq 1 ] 40 | then 41 | NginxBuildConf parse_out 42 | fi 43 | Println "$info nodejs 配置添加成功\n" 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /src/nginx/config_postfix: -------------------------------------------------------------------------------- 1 | if [[ ! -x $(command -v postfix) ]] 2 | then 3 | Spinner "安装 postfix" PostfixInstall 4 | else 5 | echo 6 | ExitOnList y "`gettext \"postfix 已存在, 是否重新设置 smtp\"`" 7 | fi 8 | 9 | echo 10 | ExitOnText "请输入 smtp 地址 (比如 hwsmtp.exmail.qq.com) : " smtp_address 11 | 12 | echo 13 | ExitOnText "请输入 smtp 端口 (比如 465) : " smtp_port 14 | 15 | echo 16 | ExitOnText "请输入 smtp 邮箱 : " smtp_email 17 | 18 | echo 19 | ExitOnText "请输入 smtp 密码 : " smtp_pass 20 | 21 | hostname=$(hostname -f) 22 | echo "$(awk '!x{x=sub(/.*myhostname = .*/,"myhostname = '"$hostname"'")}1' /etc/postfix/main.cf)" > /etc/postfix/main.cf 23 | echo "$(awk '!x{x=sub(/.*relayhost = .*/,"relayhost = '"[$smtp_address]:$smtp_port"'")}1' /etc/postfix/main.cf)" > /etc/postfix/main.cf 24 | options=( 25 | smtp_tls_security_level=encrypt 26 | smtp_tls_wrappermode=yes 27 | smtp_sasl_auth_enable=yes 28 | smtp_sasl_security_options=noanonymous 29 | smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd 30 | smtp_generic_maps=hash:/etc/postfix/generic 31 | ) 32 | Println "$info 设置 postfix ..." 33 | echo "[$smtp_address]:$smtp_port $smtp_email:$smtp_pass" > /etc/postfix/sasl_passwd 34 | postmap /etc/postfix/sasl_passwd 35 | echo "$USER@$hostname $smtp_email" > /etc/postfix/generic 36 | postmap /etc/postfix/generic 37 | for option in "${options[@]}" 38 | do 39 | if grep -q "${option%=*} = " < /etc/postfix/main.cf 40 | then 41 | echo "$(awk '!x{x=sub(/.*'"${option%=*}"' = .*/,"'"${option%=*}"' = '"${option#*=}"'")}1' /etc/postfix/main.cf)" > /etc/postfix/main.cf 42 | elif grep -q "${option%=*}=" < /etc/postfix/main.cf 43 | then 44 | echo "$(awk '!x{x=sub(/.*'"${option%=*}"'=.*/,"'"${option%=*}"'='"${option#*=}"'")}1' /etc/postfix/main.cf)" > /etc/postfix/main.cf 45 | else 46 | echo "${option//=/ = }" >> /etc/postfix/main.cf 47 | fi 48 | done 49 | if ! grep -q "$USER:" < /etc/aliases 50 | then 51 | echo "$USER: $smtp_email" >> /etc/aliases 52 | newaliases 53 | fi 54 | if [[ $(ps --no-headers -o comm 1) == "systemd" ]] 55 | then 56 | systemctl restart postfix 57 | else 58 | service postfix restart 59 | fi 60 | Println "$info smtp 设置成功\n" 61 | -------------------------------------------------------------------------------- /src/nginx/config_server: -------------------------------------------------------------------------------- 1 | NginxConfigServerHttpPort() 2 | { 3 | echo 4 | inquirer text_input "输入 http 端口: " server_http_port 80 5 | } 6 | 7 | NginxConfigServerHttpsPort() 8 | { 9 | echo 10 | inquirer text_input "输入 https 端口: " server_https_port 443 11 | } 12 | 13 | NginxConfigServerRoot() 14 | { 15 | while true 16 | do 17 | echo 18 | inquirer text_input "设置公开的根目录" server_root "$nginx_prefix/html" 19 | 20 | if [ "${server_root:0:1}" != "/" ] 21 | then 22 | Println "$error 输入错误\n" 23 | else 24 | server_root="${server_root%/}" 25 | 26 | mkdir -p "$server_root" 27 | break 28 | fi 29 | done 30 | } 31 | 32 | NginxConfigServerLiveRoot() 33 | { 34 | while true 35 | do 36 | echo 37 | inquirer text_input "设置公开目录下的(live目录 - HLS输出目录)位置" server_live_root "$server_root" 38 | 39 | if [ "${server_live_root:0:1}" != "/" ] 40 | then 41 | Println "$error 输入错误\n" 42 | else 43 | server_live_root="${server_live_root%/}" 44 | 45 | mkdir -p "$server_live_root" 46 | 47 | if [ -d "$LIVE_ROOT" ] 48 | then 49 | ln -sf "$LIVE_ROOT" "$server_live_root"/ 50 | fi 51 | 52 | break 53 | fi 54 | done 55 | } 56 | -------------------------------------------------------------------------------- /src/nginx/delete_domain: -------------------------------------------------------------------------------- 1 | NginxDeleteDomain() 2 | { 3 | NginxListDomains 4 | 5 | [ "$nginx_domains_count" -eq 0 ] && Println "$error 没有域名\n" && exit 1 6 | 7 | echo "`gettext \"输入序号\"`" 8 | while read -p "$i18n_default_cancel" nginx_domains_index 9 | do 10 | case "$nginx_domains_index" in 11 | "") 12 | Println "$i18n_canceled...\n" && exit 1 13 | ;; 14 | *[!0-9]*) 15 | Println "$error $i18n_input_correct_no\n" 16 | ;; 17 | *) 18 | if [ "$nginx_domains_index" -gt 0 ] && [ "$nginx_domains_index" -le "$nginx_domains_count" ] 19 | then 20 | nginx_domains_index=$((nginx_domains_index-1)) 21 | break 22 | else 23 | Println "$error $i18n_input_correct_no\n" 24 | fi 25 | ;; 26 | esac 27 | done 28 | 29 | server_domain=${nginx_domains[nginx_domains_index]} 30 | if [ "${nginx_domains_status[nginx_domains_index]}" -eq 1 ] 31 | then 32 | NginxDisableDomain 33 | fi 34 | rm -f "$nginx_prefix/conf/sites_available/$server_domain.conf" 35 | Println "$info $server_domain 删除成功\n" 36 | } 37 | -------------------------------------------------------------------------------- /src/nginx/disable_domain: -------------------------------------------------------------------------------- 1 | NginxDisableDomain() 2 | { 3 | rm -f "$nginx_prefix/conf/sites_enabled/$server_domain.conf" 4 | NginxRestart 5 | } 6 | -------------------------------------------------------------------------------- /src/nginx/enable_domain: -------------------------------------------------------------------------------- 1 | NginxEnableDomain() 2 | { 3 | ln -sf "$nginx_prefix/conf/sites_available/$server_domain.conf" "$nginx_prefix/conf/sites_enabled/$server_domain.conf" 4 | NginxRestart 5 | } 6 | -------------------------------------------------------------------------------- /src/nginx/input_args: -------------------------------------------------------------------------------- 1 | NginxInputArgs() 2 | { 3 | new_args="" 4 | while true 5 | do 6 | Println "$tip 空字符用 '' 表示" 7 | inquirer text_input "输入单个指令值: " arg "$i18n_blank" 8 | 9 | if [ -z "$arg" ] 10 | then 11 | break 12 | fi 13 | 14 | [ -n "$new_args" ] && new_args="$new_args," 15 | 16 | if [ "$arg" == "''" ] 17 | then 18 | arg="" 19 | else 20 | arg=${arg//\\/\\\\} 21 | arg=${arg//\"/\\\"} 22 | fi 23 | 24 | new_args="$new_args\"$arg\"" 25 | 26 | echo 27 | inquirer list_input "继续添加指令值" ny_options yn_option 28 | if [ "$yn_option" == "$i18n_no" ] 29 | then 30 | break 31 | fi 32 | done 33 | } 34 | -------------------------------------------------------------------------------- /src/nginx/is_block_directive: -------------------------------------------------------------------------------- 1 | NginxIsBlockDirective() 2 | { 3 | local level=("level_${1}_block_directives"[@]) 4 | local block_directives=("${!level}") 5 | for block_directive in "${block_directives[@]}" 6 | do 7 | if [ "$block_directive" == "$2" ] 8 | then 9 | return 0 10 | fi 11 | done 12 | return 1 13 | } 14 | -------------------------------------------------------------------------------- /src/nginx/list_stream: -------------------------------------------------------------------------------- 1 | NginxListStream() 2 | { 3 | NginxGetStream 4 | 5 | Println "分流配置:\n\n SNI 域名分流:\n\n${nginx_stream_server_name_list:- 无}\n\n SSL 协议分流(\$ssl_proxy):\n\n${nginx_stream_protocol_list:- 无}\n\n ALPN 协议分流:\n\n${nginx_stream_alpn_protocols_list:- 无}\n\n 分流后端(\$upstream):\n\n${nginx_stream_upstream_list:- 无}\n\n" 6 | } 7 | -------------------------------------------------------------------------------- /src/nginx/menu_mongodb: -------------------------------------------------------------------------------- 1 | MongodbMenu() 2 | { 3 | echo 4 | mongodb_options=( '查看' '安装' '升级' ) 5 | inquirer list_input_index "选择操作" mongodb_options mongodb_options_index 6 | 7 | if [ "$mongodb_options_index" -eq 0 ] 8 | then 9 | if [[ ! -x $(command -v mongod) ]] 10 | then 11 | Println "$error 请先安装 mongodb\n" 12 | return 1 13 | fi 14 | 15 | systemctl --no-pager -l status mongod 16 | elif [ "$mongodb_options_index" -eq 1 ] 17 | then 18 | if [[ -x $(command -v mongod) ]] 19 | then 20 | Println "$error mongodb 已存在\n" 21 | return 1 22 | fi 23 | 24 | MongodbInstall 25 | else 26 | if [[ ! -x $(command -v mongod) ]] 27 | then 28 | Println "$error 请先安装 mongodb\n" 29 | return 1 30 | fi 31 | 32 | echo 33 | ExitOnList n "升级会清除现有 mongodb 配置, 是否继续" 34 | 35 | service mongod stop 36 | 37 | if [ "$dist" == "rpm" ] 38 | then 39 | yum -y erase mongodb-org* 40 | 41 | rm -f /etc/yum.repos.d/mongodb-org-*.repo 42 | else 43 | apt-get -y --purge remove mongodb-org* 44 | apt-get -y autoremove 45 | 46 | rm -f /etc/apt/sources.list.d/mongodb-org-*.list 47 | fi 48 | 49 | MongodbInstall 50 | fi 51 | } 52 | -------------------------------------------------------------------------------- /src/nginx/menu_nodejs: -------------------------------------------------------------------------------- 1 | NodejsMenu() 2 | { 3 | [ ! -d "$IPTV_ROOT" ] && Println "$error 请先输入 tv 安装 !\n" && exit 1 4 | 5 | echo 6 | nodejs_options=( '安装' '升级' '运行 iptv node' ) 7 | inquirer list_input_index "选择操作" nodejs_options nodejs_options_index 8 | 9 | if [ "$nodejs_options_index" -eq 0 ] 10 | then 11 | if [[ -x $(command -v node) ]] && [[ -x $(command -v npm) ]] 12 | then 13 | Println "$error nodejs 已存在\n" 14 | exit 1 15 | fi 16 | 17 | NodejsInstall 18 | elif [ "$nodejs_options_index" -eq 1 ] 19 | then 20 | if [[ ! -x $(command -v node) ]] || [[ ! -x $(command -v npm) ]] 21 | then 22 | Println "$error 请先安装 nodejs\n" 23 | exit 1 24 | fi 25 | 26 | NodejsInstall 27 | else 28 | if [ -f "$NODE_ROOT/index.js" ] 29 | then 30 | Println "$error iptv node 已存在\n" 31 | exit 1 32 | fi 33 | 34 | if [[ ! -x $(command -v node) ]] || [[ ! -x $(command -v npm) ]] 35 | then 36 | Println "$error 请先安装 nodejs\n" 37 | exit 1 38 | fi 39 | 40 | if [[ ! -x $(command -v mongod) ]] 41 | then 42 | Println "$error 请先安装 mongodb\n" 43 | exit 1 44 | fi 45 | 46 | NodejsConfig 47 | fi 48 | } 49 | -------------------------------------------------------------------------------- /src/nginx/parse_config: -------------------------------------------------------------------------------- 1 | NginxParseConfig() 2 | { 3 | CrossplaneInstall 4 | 5 | if TMP_FILE=$(mktemp -q) 6 | then 7 | chmod +r "$TMP_FILE" 8 | else 9 | exit $? 10 | fi 11 | 12 | trap ' 13 | rm -f "$TMP_FILE" 14 | ' EXIT 15 | 16 | if [ -z "${1:-}" ] 17 | then 18 | parse_file="$nginx_prefix/conf/nginx.conf" 19 | parse_in=$(< $parse_file) 20 | parse_domain=0 21 | else 22 | parse_file="$nginx_prefix/conf/sites_available/$1.conf" 23 | parse_in="http {$(< $parse_file)}" 24 | parse_domain=1 25 | fi 26 | 27 | echo "$parse_in" > "$TMP_FILE" 28 | 29 | parse_out=$(crossplane parse "$TMP_FILE" --single-file) 30 | 31 | rm -f "$TMP_FILE" 32 | 33 | trap - EXIT 34 | 35 | jq_path='["config",0,"file"]' 36 | JQs update parse_out "$parse_file" 37 | } 38 | -------------------------------------------------------------------------------- /src/nginx/restart: -------------------------------------------------------------------------------- 1 | NginxRestart() 2 | { 3 | if ! $NGINX_FILE -t 4 | then 5 | Println "$error 请检查配置错误\n" 6 | exit 1 7 | fi 8 | echo 9 | nginx_actions=( '重载配置' '强制重启' ) 10 | inquirer list_input_index "选择操作" nginx_actions nginx_actions_index 11 | if [ "$nginx_actions_index" -eq 0 ] 12 | then 13 | nginx_action=reload-or-restart 14 | else 15 | nginx_action=restart 16 | fi 17 | if systemctl $nginx_action $nginx_name 18 | then 19 | Println "$info $nginx_name 重启成功\n" 20 | else 21 | Println "$error $nginx_name 重启失败, 请检查配置\n" 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /src/nginx/select_domain: -------------------------------------------------------------------------------- 1 | NginxSelectDomain() 2 | { 3 | echo "选择域名" 4 | while read -p "$i18n_default_cancel" nginx_domains_index 5 | do 6 | case "$nginx_domains_index" in 7 | "") 8 | Println "$i18n_canceled...\n" 9 | exit 1 10 | ;; 11 | *[!0-9]*) 12 | Println "$error $i18n_input_correct_no\n" 13 | ;; 14 | *) 15 | if [ "$nginx_domains_index" -gt 0 ] && [ "$nginx_domains_index" -le "$nginx_domains_count" ] 16 | then 17 | nginx_domains_index=$((nginx_domains_index-1)) 18 | break 19 | else 20 | Println "$error $i18n_input_correct_no\n" 21 | fi 22 | ;; 23 | esac 24 | done 25 | } 26 | -------------------------------------------------------------------------------- /src/nginx/select_domain_server: -------------------------------------------------------------------------------- 1 | NginxSelectDomainServer() 2 | { 3 | echo "`gettext \"输入序号\"`" 4 | while read -p "$i18n_default_cancel" nginx_domain_servers_num 5 | do 6 | case "$nginx_domain_servers_num" in 7 | "") 8 | Println "$i18n_canceled...\n" 9 | exit 1 10 | ;; 11 | *[!0-9]*) 12 | Println "$error $i18n_input_correct_no\n" 13 | ;; 14 | *) 15 | if [ "$nginx_domain_servers_num" -gt 0 ] && [ "$nginx_domain_servers_num" -le "$nginx_domain_servers_count" ] 16 | then 17 | nginx_domain_servers_index=$((nginx_domain_servers_num-1)) 18 | level_2_add_indices=( "${nginx_domain_servers_indices[nginx_domain_servers_index]}" ) 19 | server_root=${nginx_domain_servers_root[nginx_domain_servers_index]} 20 | break 21 | else 22 | Println "$error $i18n_input_correct_no\n" 23 | fi 24 | ;; 25 | esac 26 | done 27 | } 28 | -------------------------------------------------------------------------------- /src/nginx/select_localhost_server: -------------------------------------------------------------------------------- 1 | NginxSelectLocalhostServer() 2 | { 3 | echo "`gettext \"输入序号\"`" 4 | while read -p "$i18n_default_cancel" nginx_localhost_server_num 5 | do 6 | case "$nginx_localhost_server_num" in 7 | "") 8 | Println "$i18n_canceled...\n" 9 | exit 1 10 | ;; 11 | *[!0-9]*) 12 | Println "$error $i18n_input_correct_no\n" 13 | ;; 14 | *) 15 | if [ "$nginx_localhost_server_num" -gt 0 ] && [ "$nginx_localhost_server_num" -le "$nginx_localhost_server_count" ] 16 | then 17 | nginx_localhost_server_index=$((nginx_localhost_server_num-1)) 18 | level_2_add_indices=( "${nginx_localhost_server_indices[nginx_localhost_server_index]}" ) 19 | server_root=${nginx_localhost_server_root[nginx_localhost_server_index]} 20 | break 21 | else 22 | Println "$error $i18n_input_correct_no\n" 23 | fi 24 | ;; 25 | esac 26 | done 27 | } 28 | -------------------------------------------------------------------------------- /src/nginx/toggle: -------------------------------------------------------------------------------- 1 | NginxToggle() 2 | { 3 | echo 4 | if [[ $(systemctl is-active $nginx_name) == "active" ]] 5 | then 6 | ExitOnList y "`eval_gettext \"\\\$nginx_name 正在运行, 是否关闭\"`" 7 | 8 | if [[ $(echo $SSH_CONNECTION | cut -d' ' -f3) == "127.0.0.1" ]] 9 | then 10 | Println "$error 请使用非 $nginx_name 监听端口连接 ssh 后重试\n" 11 | exit 1 12 | fi 13 | systemctl stop $nginx_name 14 | Println "$info $nginx_name 已关闭\n" 15 | else 16 | ExitOnList y "`eval_gettext \"\\\$nginx_name 未运行, 是否开启\"`" 17 | 18 | systemctl start $nginx_name 19 | Println "$info $nginx_name 已开启\n" 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /src/nginx/toggle_domain: -------------------------------------------------------------------------------- 1 | NginxToggleDomain() 2 | { 3 | NginxListDomains 4 | 5 | [ "$nginx_domains_count" -eq 0 ] && Println "$error 没有域名\n" && exit 1 6 | 7 | echo "`gettext \"输入序号\"`" 8 | while read -p "$i18n_default_cancel" nginx_domains_index 9 | do 10 | case "$nginx_domains_index" in 11 | "") 12 | Println "$i18n_canceled...\n" && exit 1 13 | ;; 14 | *[!0-9]*) 15 | Println "$error $i18n_input_correct_no\n" 16 | ;; 17 | *) 18 | if [ "$nginx_domains_index" -gt 0 ] && [ "$nginx_domains_index" -le "$nginx_domains_count" ] 19 | then 20 | nginx_domains_index=$((nginx_domains_index-1)) 21 | break 22 | else 23 | Println "$error $i18n_input_correct_no\n" 24 | fi 25 | ;; 26 | esac 27 | done 28 | 29 | server_domain=${nginx_domains[nginx_domains_index]} 30 | if [ "${nginx_domains_status[nginx_domains_index]}" -eq 1 ] 31 | then 32 | NginxDisableDomain 33 | Println "$info $server_domain 关闭成功\n" 34 | else 35 | NginxEnableDomain 36 | Println "$info $server_domain 开启成功\n" 37 | fi 38 | } 39 | -------------------------------------------------------------------------------- /src/nginx/update_ip: -------------------------------------------------------------------------------- 1 | NginxUpdateIp() 2 | { 3 | if [ ! -f $nginx_prefix/conf/nginx.conf ] 4 | then 5 | Println "$error 请先安装 $nginx_name\n" 6 | exit 1 7 | fi 8 | 9 | if ! grep -q "include cloudflare_ip.conf;" < $nginx_prefix/conf/nginx.conf 10 | then 11 | sed -i '/http {/a\ include cloudflare_ip.conf;' $nginx_prefix/conf/nginx.conf 12 | else 13 | Println "$error $nginx_name 配置已经存在\n" 14 | fi 15 | 16 | Println "$info 更新 ip ..." 17 | 18 | cat > "$HOME"/update_cf_ibm_ip.sh < $nginx_prefix/conf/cloudflare_ip.conf; 21 | ibm_ips=( 22 | 50.22.0.0/16 23 | 50.23.0.0/16 24 | 66.228.118.0/23 25 | 67.228.66.0/24 26 | 75.126.0.0/16 27 | 108.168.157.0/24 28 | 173.192.0.0/16 29 | 174.35.17.0/24 30 | 184.172.0.0/16 31 | 192.255.0.0/16 32 | 198.23.0.0/16 33 | 208.43.15.0/24 34 | 169.45.0.0/16 35 | 169.46.0.0/16 36 | 169.47.0.0/16 37 | 169.48.0.0/16 38 | 169.61.0.0/16 39 | 169.62.0.0/16 40 | ) 41 | for i in "\${ibm_ips[@]}"; do 42 | echo "set_real_ip_from \$i;" >> $nginx_prefix/conf/cloudflare_ip.conf; 43 | done 44 | for i in \$(curl https://www.cloudflare.com/ips-v4); do 45 | echo "set_real_ip_from \$i;" >> $nginx_prefix/conf/cloudflare_ip.conf; 46 | done 47 | for i in \$(curl https://www.cloudflare.com/ips-v6); do 48 | echo "set_real_ip_from \$i;" >> $nginx_prefix/conf/cloudflare_ip.conf; 49 | done 50 | echo >> $nginx_prefix/conf/cloudflare_ip.conf; 51 | echo '# use any of the following two' >> $nginx_prefix/conf/cloudflare_ip.conf; 52 | echo 'real_ip_header CF-Connecting-IP;' >> $nginx_prefix/conf/cloudflare_ip.conf; 53 | echo '#real_ip_header X-Forwarded-For;' >> $nginx_prefix/conf/cloudflare_ip.conf; 54 | EOF 55 | 56 | bash ~/update_cf_ibm_ip.sh 57 | 58 | Println "$info IP 更新成功\n" 59 | } 60 | -------------------------------------------------------------------------------- /src/nginx/view_status: -------------------------------------------------------------------------------- 1 | NginxViewStatus() 2 | { 3 | if [ ! -d "$nginx_prefix" ] 4 | then 5 | Println "$error $nginx_name 未安装 !\n" 6 | else 7 | systemctl --no-pager -l status $nginx_name 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /src/proxmox/get_vms: -------------------------------------------------------------------------------- 1 | PveGetVMs() 2 | { 3 | pve_vm_count=0 4 | 5 | IFS=" " read -r m_id m_name m_status m_mem m_boot_disk m_pid < <(qm list | awk '$1 {a=a $1",";b=b $2",";c=c $3",";d=d $4",";e=e $5",";f=f $6","} END {print a,b,c,d,e,f}') 6 | 7 | if [ -n "${m_id#*,}" ] 8 | then 9 | IFS="," read -r -a pve_vm_ids <<< "${m_id#*,}" 10 | IFS="," read -r -a pve_vm_name <<< "${m_name#*,}" 11 | IFS="," read -r -a pve_vm_status <<< "${m_status#*,}" 12 | IFS="," read -r -a pve_vm_mem <<< "${m_mem#*,}" 13 | IFS="," read -r -a pve_vm_boot_disk <<< "${m_boot_disk#*,}" 14 | IFS="," read -r -a pve_vm_pid <<< "${m_pid#*,}" 15 | 16 | pve_vm_count=${#pve_vm_ids[@]} 17 | fi 18 | } 19 | -------------------------------------------------------------------------------- /src/proxmox/list_vms: -------------------------------------------------------------------------------- 1 | PveListVMs() 2 | { 3 | PveGetVMs 4 | 5 | if [ "$pve_vm_count" -eq 0 ] 6 | then 7 | Println "$error 没有虚拟机\n" 8 | exit 1 9 | fi 10 | 11 | pve_vm_list="" 12 | 13 | for((i=0;i "$HOME/.config/rclone/rclone.conf" 17 | fi 18 | } 19 | -------------------------------------------------------------------------------- /src/service/base64: -------------------------------------------------------------------------------- 1 | Base64urlEncode() 2 | { 3 | OpensslInstall 4 | 5 | (if [ -z "${1:-}" ]; then cat -; else echo -n "$1"; fi) | 6 | openssl base64 -e -A | 7 | sed s/\\+/-/g | 8 | sed s/\\//_/g | 9 | sed -E s/=+$// 10 | } 11 | 12 | Base64urlDecode() 13 | { 14 | OpensslInstall 15 | 16 | local input=$(if [ -z "${1:-}" ]; then echo -n $(cat -); else echo -n "$1"; fi) 17 | local mod=$(($(echo -n "$input" | wc -c) % 4)) 18 | local padding=$(if [ $mod -eq 2 ]; then echo -n '=='; elif [ $mod -eq 3 ]; then echo -n '=' ; fi) 19 | echo -n "$input$padding" | 20 | sed s/-/+/g | 21 | sed s/_/\\//g | 22 | openssl base64 -d -A 23 | } 24 | -------------------------------------------------------------------------------- /src/service/obscure: -------------------------------------------------------------------------------- 1 | Obscure() 2 | { 3 | OpensslInstall 4 | 5 | local hexkey="9c935b48730a554d6bfd7c63c886a92bd390198eb8128afbf4de162b8b95f638" 6 | local hexiv=$(openssl rand -hex 16) 7 | local buff=$(echo "$1" | openssl enc -aes-256-ctr -K "$hexkey" -iv "$hexiv") 8 | hexiv=$(echo -n "$hexiv" | xxd -r -p) 9 | local encrypted=$(echo -n "$hexiv$buff" | Base64urlEncode) 10 | 11 | echo "$encrypted" 12 | } 13 | 14 | Deobscure() 15 | { 16 | OpensslInstall 17 | 18 | local hexkey="9c935b48730a554d6bfd7c63c886a92bd390198eb8128afbf4de162b8b95f638" 19 | local decoded=$(echo -n "$1" | Base64urlDecode) 20 | local hexiv=$(echo -n "$decoded" | head -c 16 | hexdump -v -e '/1 "%02x"') # xxd -p 21 | local buff=$(echo -n "$decoded" | tail -c +17) 22 | local decrypted=$(echo -n "$buff" | openssl enc -aes-256-ctr -d -K "$hexkey" -iv "$hexiv" -nopad) 23 | 24 | echo "$decrypted" 25 | } 26 | -------------------------------------------------------------------------------- /src/smb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woniuzfb/iptv/086a9b4c8036503ccdb4ea1857124a3b1de92f92/src/smb -------------------------------------------------------------------------------- /src/test/core: -------------------------------------------------------------------------------- 1 | TestCoreQuoteArray() 2 | { 3 | echo 4 | test_array=( a 'b "c' d) 5 | ToQuoteArray test_array 6 | printf '%s\n' "${test_array[@]}" 7 | } 8 | 9 | TestCoreJoinByChar() 10 | { 11 | echo 12 | test_array=( a 'b "c' d) 13 | ToQuoteArray test_array 14 | list=$(JoinByChar , "${test_array[@]}") 15 | echo "$list" 16 | } 17 | 18 | TestCore() 19 | { 20 | test_core_options=( 21 | '1. quote an array' 22 | '2. array -> list' 23 | ) 24 | 25 | test_core_actions=( 26 | TestCoreQuoteArray 27 | TestCoreJoinByChar 28 | ) 29 | 30 | echo 31 | inquirer checkbox_input_indices "选择测试项目" test_core_options test_core_options_indices 32 | 33 | for test_core_options_index in "${test_core_options_indices[@]}" 34 | do 35 | ${test_core_actions[test_core_options_index]} 36 | done 37 | 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /src/test/inquirer: -------------------------------------------------------------------------------- 1 | TestInquirerInputValidator() 2 | { 3 | if [ "$1" == "$i18n_cancel" ] || [[ "$1" =~ ^[0-9]+$ ]] 4 | then 5 | return 0 6 | fi 7 | 8 | return 1 9 | } 10 | 11 | TestInquirerInput() 12 | { 13 | Println "$tip 输入非数字会提示错误" 14 | inquirer text_input "测试输入 validator" user_input "$i18n_cancel" TestInquirerInputValidator "请输入数字" 15 | } 16 | 17 | TestInquirerList() 18 | { 19 | Println "$tip 选择 是 得 true, 否 得 false" 20 | inquirer list_input_yn "请选择" tf_options tf_result 21 | Println "$info 结果为 $tf_result\n" 22 | 23 | Println "$tip 选择 是 得 0, 否 得 1" 24 | inquirer list_input_ny "请选择" oz_options oz_result 25 | Println "$info 结果为 $oz_result\n" 26 | } 27 | 28 | TestInquirerBlank() 29 | { 30 | echo 31 | inquirer text_input "请输入" blank_input "$i18n_blank" 32 | 33 | if [ -z "$blank_input" ] 34 | then 35 | Println "$info 输入为空\n" 36 | else 37 | Println "$info 输入不为空\n" 38 | fi 39 | } 40 | 41 | TestInquirer() 42 | { 43 | test_inquirer_options=( 44 | '1. 输入非数字会提示错误' 45 | '2. 选择获得正确值' 46 | '3. 不设置输出为空' 47 | ) 48 | 49 | test_inquirer_actions=( 50 | TestInquirerInput 51 | TestInquirerList 52 | TestInquirerBlank 53 | ) 54 | 55 | echo 56 | inquirer checkbox_input_indices "选择测试项目" test_inquirer_options test_inquirer_options_indices 57 | 58 | for test_inquirer_options_index in "${test_inquirer_options_indices[@]}" 59 | do 60 | ${test_inquirer_actions[test_inquirer_options_index]} 61 | done 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /src/tr: -------------------------------------------------------------------------------- 1 | TrojanInstall() 2 | { 3 | if [ -s "$TR_CONFIG" ] 4 | then 5 | Println "$error $trojan_name 已存在...\n" 6 | ExitOnList n "`gettext \"是否覆盖原安装\"`" 7 | fi 8 | 9 | DepsCheck 10 | JQInstall 11 | 12 | if ! grep -q "$trojan_name:" < "/etc/passwd" 13 | then 14 | if grep -q '\--group ' < <(adduser --help) 15 | then 16 | adduser $trojan_name --system --group --no-create-home > /dev/null 17 | else 18 | adduser $trojan_name --system --no-create-home > /dev/null 19 | fi 20 | usermod -s /usr/sbin/nologin $trojan_name 21 | fi 22 | 23 | Println "$info 安装 $trojan_name..." 24 | 25 | { curl -s -m 10 "$TR_LINK" || curl -s -m 30 "$TR_LINK_FALLBACK"; } \ 26 | | sed "s+nobody+$trojan_name+g" \ 27 | | sed "s+ 'sha1'++g" \ 28 | | sed "s+ 'sha256'++g" \ 29 | | sed "s+ 'sha512'++g" \ 30 | | sed "s+https://api.github.com/repos/p4gefau1t/trojan-go/releases/latest+$FFMPEG_MIRROR_LINK/$trojan_name.json+g" \ 31 | | sed "s+https://github.com/p4gefau1t/trojan-go/releases/download+$FFMPEG_MIRROR_LINK/$trojan_name+g" | bash 32 | 33 | TrojanConfigInstall 34 | 35 | systemctl daemon-reload 36 | systemctl enable $trojan_name 37 | systemctl start $trojan_name 38 | 39 | Println "$info $trojan_name 安装成功\n" 40 | } 41 | 42 | exit 0 43 | -------------------------------------------------------------------------------- /src/tt: -------------------------------------------------------------------------------- 1 | Include src/test/core 2 | Include src/test/inquirer 3 | 4 | TestCore 5 | TestInquirer 6 | -------------------------------------------------------------------------------- /src/v2ray/add_nginx_proxy: -------------------------------------------------------------------------------- 1 | V2rayNginxDomainServerAddProxy() 2 | { 3 | V2rayListInbounds nginx 4 | V2raySelectInbound 5 | 6 | if [ "${inbounds_stream_network[inbounds_index]}" == "domainsocket" ] 7 | then 8 | Println "$error 不能使用此入站\n" 9 | exit 1 10 | fi 11 | 12 | if [ -z "${inbounds_stream_path[inbounds_index]}" ] 13 | then 14 | Println "$error 此入站没有路径\n" 15 | exit 1 16 | fi 17 | 18 | proxy_path=${inbounds_stream_path[inbounds_index]} 19 | proxy_port=${inbounds_port[inbounds_index]} 20 | 21 | if [ "${inbounds_stream_network[inbounds_index]}" == "ws" ] 22 | then 23 | new_proxy=' 24 | {"directive":"location","args":["=","'"$proxy_path"'"],"block":[ 25 | {"directive":"proxy_redirect","args":["off"]}, 26 | {"directive":"proxy_pass","args":["http://127.0.0.1:'"$proxy_port"'"]}, 27 | {"directive":"proxy_http_version","args":["1.1"]}, 28 | {"directive":"proxy_set_header","args":["Upgrade","$http_upgrade"]}, 29 | {"directive":"proxy_set_header","args":["Connection","upgrade"]} 30 | ]}' 31 | else 32 | new_proxy=' 33 | {"directive":"location","args":["=","'"$proxy_path"'"],"block":[ 34 | {"directive":"proxy_redirect","args":["off"]}, 35 | {"directive":"proxy_pass","args":["http://127.0.0.1:'"$proxy_port"'"]}, 36 | {"directive":"proxy_http_version","args":["1.1"]} 37 | ]}' 38 | fi 39 | 40 | jq_path='["config",0,"parsed",0,"block",'"$v2ray_nginx_domain_servers_index"',"block"]' 41 | JQs add parse_out "[$new_proxy]" 42 | 43 | NginxBuildConf parse_out 44 | 45 | Println "$info 代理添加成功\n" 46 | } 47 | -------------------------------------------------------------------------------- /src/v2ray/delete_inbound: -------------------------------------------------------------------------------- 1 | V2rayDeleteInbound() 2 | { 3 | V2rayListInbounds 4 | V2raySelectInbound 5 | jq_path='["inbounds",'"$inbounds_index"']' 6 | JQ delete "$V2_CONFIG" 7 | Println "$info 入站 ${inbounds_tag[inbounds_index]} 删除成功\n" 8 | } 9 | -------------------------------------------------------------------------------- /src/v2ray/delete_inbound_account: -------------------------------------------------------------------------------- 1 | V2rayDeleteInboundAccount() 2 | { 3 | V2rayListInboundAccounts 4 | 5 | if [ "${inbounds_protocol[inbounds_index]}" == "shadowsocks" ] 6 | then 7 | Println "$error 请直接删除此入站\n" 8 | exit 1 9 | fi 10 | 11 | echo -e "# ${green}$((accounts_count+1))${normal}${indent_6}删除所有账号\n\n" 12 | echo "`gettext \"输入序号\"`" 13 | while read -p "$i18n_default_cancel" accounts_index 14 | do 15 | case "$accounts_index" in 16 | "") 17 | Println "$i18n_canceled...\n" && exit 1 18 | ;; 19 | *[!0-9]*) 20 | Println "$error $i18n_input_correct_no\n" 21 | ;; 22 | *) 23 | if [ "$accounts_index" -gt 0 ] && [ "$accounts_index" -le $((accounts_count+1)) ] 24 | then 25 | accounts_index=$((accounts_index-1)) 26 | break 27 | else 28 | Println "$error $i18n_input_correct_no\n" 29 | fi 30 | ;; 31 | esac 32 | done 33 | 34 | if [ "${inbounds_protocol[inbounds_index]}" == "http" ] 35 | then 36 | group_name="accounts" 37 | else 38 | group_name="clients" 39 | fi 40 | 41 | if [ "$accounts_index" == "$accounts_count" ] 42 | then 43 | json=true 44 | jq_path='["inbounds",'"$inbounds_index"',"settings","'"$group_name"'"]' 45 | JQ update "$V2_CONFIG" [] 46 | else 47 | jq_path='["inbounds",'"$inbounds_index"',"settings","'"$group_name"'",'"$accounts_index"']' 48 | JQ delete "$V2_CONFIG" 49 | fi 50 | Println "$info 入站账号删除成功\n" 51 | } 52 | -------------------------------------------------------------------------------- /src/v2ray/delete_outbound: -------------------------------------------------------------------------------- 1 | V2rayDeleteOutbound() 2 | { 3 | V2rayListOutbounds 4 | V2raySelectOutbound 5 | jq_path='["outbounds",'"$outbounds_index"']' 6 | JQ delete "$V2_CONFIG" 7 | Println "$info 出站 ${outbounds_tag[outbounds_index]} 删除成功\n" 8 | } 9 | -------------------------------------------------------------------------------- /src/v2ray/delete_outbound_account: -------------------------------------------------------------------------------- 1 | V2rayDeleteOutboundAccount() 2 | { 3 | V2rayListOutboundAccounts 4 | 5 | if [ "${outbounds_protocol[outbounds_index]}" == "shadowsocks" ] 6 | then 7 | Println "$error 请直接删除此出站\n" 8 | exit 1 9 | fi 10 | 11 | echo -e "# ${green}$((accounts_count+1))${normal}${indent_6}删除所有账号\n\n" 12 | echo "`gettext \"输入序号\"`" 13 | while read -p "$i18n_default_cancel" accounts_index 14 | do 15 | case "$accounts_index" in 16 | "") 17 | Println "$i18n_canceled...\n" && exit 1 18 | ;; 19 | *[!0-9]*) 20 | Println "$error $i18n_input_correct_no\n" 21 | ;; 22 | *) 23 | if [ "$accounts_index" -gt 0 ] && [ "$accounts_index" -le $((accounts_count+1)) ] 24 | then 25 | accounts_index=$((accounts_index-1)) 26 | break 27 | else 28 | Println "$error $i18n_input_correct_no\n" 29 | fi 30 | ;; 31 | esac 32 | done 33 | 34 | if [ "${outbounds_protocol[outbounds_index]}" == "http" ] 35 | then 36 | group_name="servers" 37 | else 38 | group_name="vnext" 39 | fi 40 | 41 | if [ "$accounts_index" == "$accounts_count" ] 42 | then 43 | json=true 44 | jq_path='["outbounds",'"$outbounds_index"',"settings","'"$group_name"'",0,"users"]' 45 | JQ update "$V2_CONFIG" [] 46 | else 47 | jq_path='["outbounds",'"$outbounds_index"',"settings","'"$group_name"'",0,"users",'"$accounts_index"']' 48 | JQ delete "$V2_CONFIG" 49 | fi 50 | Println "$info 出站账号删除成功\n" 51 | } 52 | -------------------------------------------------------------------------------- /src/v2ray/get_dns: -------------------------------------------------------------------------------- 1 | V2rayGetDns() 2 | { 3 | IFS=$'`\t' read -r m_dns_hosts_domain m_dns_hosts_address m_dns_servers \ 4 | dns_client_ip dns_tag dns_query_strategy dns_disable_cache dns_disable_fallback \ 5 | dns_disable_fallback_if_match < <($JQ_FILE -r '[ 6 | ([.dns.hosts // {}|to_entries[]|.key|. + "^"]|join("") + "`"), 7 | ([.dns.hosts // {}|to_entries[]|.value|if (.|type) == "array" then 8 | (.|join(",")) 9 | else 10 | . end|. + "^"]|join("") + "`"), 11 | ([.dns.servers // []|.[]|if (.|type) == "object" then 12 | ([ 13 | .address, 14 | (.port // ""|tostring), 15 | (.domains // []|join(",")), 16 | (.expectIPs // []|join(",")) 17 | ]|join("|")) 18 | else 19 | . end|. + "^"]|join("") + "`"), 20 | (.dns.clientIp|. + "`"), 21 | (.dns.tag|. + "`"), 22 | (.dns.queryStrategy // "UseIP"|. + "`"), 23 | (.dns.disableCache // false|tostring|. + "`"), 24 | (.dns.disableFallback // false|tostring|. + "`"), 25 | (.dns.disableFallbackIfMatch // false|tostring|. + "`")]|@tsv' "$V2_CONFIG") 26 | 27 | if [ -z "$m_dns_hosts_domain" ] 28 | then 29 | dns_hosts_count=0 30 | else 31 | IFS="^" read -r -a dns_hosts_domain <<< "$m_dns_hosts_domain" 32 | IFS="^" read -r -a dns_hosts_address <<< "$m_dns_hosts_address" 33 | dns_hosts_count=${#dns_hosts_domain[@]} 34 | fi 35 | 36 | if [ -z "$m_dns_servers" ] 37 | then 38 | dns_servers_count=0 39 | else 40 | IFS="^" read -r -a dns_servers <<< "$m_dns_servers" 41 | dns_servers_count=${#dns_servers[@]} 42 | fi 43 | } 44 | -------------------------------------------------------------------------------- /src/v2ray/get_free_tag: -------------------------------------------------------------------------------- 1 | GetFreeTag() 2 | { 3 | while true 4 | do 5 | free_tag=$(RandStr) 6 | if ! grep -q '"tag": "'"$free_tag"'"' < "$V2_CONFIG" 7 | then 8 | echo "$free_tag" 9 | break 10 | fi 11 | done 12 | } 13 | -------------------------------------------------------------------------------- /src/v2ray/get_reverse: -------------------------------------------------------------------------------- 1 | V2rayGetReverse() 2 | { 3 | IFS=$'`\t' read -r m_reverse_bridges_tag m_reverse_bridges_domain m_reverse_portals_tag \ 4 | m_reverse_portals_domain < <($JQ_FILE -r '[ 5 | ([.reverse.bridges // []|.[].tag]|join("^") + "`"), 6 | ([.reverse.bridges // []|.[].domain]|join("^") + "`"), 7 | ([.reverse.portals // []|.[].tag]|join("^") + "`"), 8 | ([.reverse.portals // []|.[].domain]|join("^") + "`") 9 | ]|@tsv' "$V2_CONFIG") 10 | 11 | if [ -z "$m_reverse_bridges_tag" ] 12 | then 13 | reverse_bridges_count=0 14 | else 15 | IFS="^" read -r -a reverse_bridges_tag <<< "$m_reverse_bridges_tag" 16 | IFS="^" read -r -a reverse_bridges_domain <<< "$m_reverse_bridges_domain" 17 | reverse_bridges_count=${#reverse_bridges_tag[@]} 18 | fi 19 | 20 | if [ -z "$m_reverse_portals_tag" ] 21 | then 22 | reverse_portals_count=0 23 | else 24 | IFS="^" read -r -a reverse_portals_tag <<< "$m_reverse_portals_tag" 25 | IFS="^" read -r -a reverse_portals_domain <<< "$m_reverse_portals_domain" 26 | reverse_portals_count=${#reverse_portals_tag[@]} 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /src/v2ray/get_traffic: -------------------------------------------------------------------------------- 1 | V2rayGetTraffic() 2 | { 3 | while IFS= read -r line 4 | do 5 | if [[ $line =~ value: ]] 6 | then 7 | echo ${line#*:} | numfmt --to=iec --suffix=B 8 | break 9 | fi 10 | done < <($V2CTL_FILE api --server=$api_inbound_listen:$api_inbound_port StatsService.GetStats 'name: "'"$1"'>>>'"$2"'>>>traffic>>>'"$3"'" reset: false' 2> /dev/null) 11 | return 0 12 | } 13 | -------------------------------------------------------------------------------- /src/v2ray/list_inbound_share: -------------------------------------------------------------------------------- 1 | V2rayListInboundShare() 2 | { 3 | if [ "${inbounds_protocol[inbounds_index]}" == "vmess" ] && [ "${inbounds_listen[inbounds_index]}" != "127.0.0.1" ] 4 | then 5 | V2raySelectAccount 6 | if [ "${inbounds_listen[inbounds_index]}" == "0.0.0.0" ] 7 | then 8 | server_ip=$(GetServerIp) 9 | else 10 | server_ip=${inbounds_listen[inbounds_index]} 11 | fi 12 | vmess_link=$( 13 | $JQ_FILE -n --arg ps "${inbounds_tag[inbounds_index]}" --arg add "$server_ip" \ 14 | --arg port "${inbounds_port[inbounds_index]}" --arg id "${accounts_id[accounts_index]}" \ 15 | --arg aid "${accounts_alter_id[accounts_index]}" --arg net "${inbounds_stream_network[inbounds_index]}" \ 16 | --arg header_type "${inbounds_stream_header_type[inbounds_index]}" --arg host "${inbounds_stream_http_host[inbounds_index]}" \ 17 | --arg path "${inbounds_stream_path[inbounds_index]}" --arg tls "${inbounds_stream_security[inbounds_index]}" \ 18 | '{ 19 | "v": "2", 20 | "ps": $ps, 21 | "add": $add, 22 | "port": $port, 23 | "id": $id, 24 | "aid": $aid, 25 | "net": $net, 26 | "type": $header_type, 27 | "host": $host, 28 | "path": $path, 29 | "tls": $tls 30 | }' | base64 -w 0) 31 | Println "分享链接: ${green}vmess://$vmess_link${normal}\n" 32 | echo 33 | ExitOnList y "`gettext \"打印二维码\"`" 34 | if [ ! -e "/usr/local/bin/imgcat" ] 35 | then 36 | ImgcatInstall 37 | fi 38 | ImageMagickInstall 39 | DepInstall qrencode 40 | qrencode -s 1 -o "$HOME/vmess_link.png" "vmess://$vmess_link" 41 | /usr/local/bin/imgcat --half-height "$HOME/vmess_link.png" 42 | fi 43 | } 44 | -------------------------------------------------------------------------------- /src/v2ray/list_nginx_domains: -------------------------------------------------------------------------------- 1 | V2rayNginxListDomains() 2 | { 3 | v2ray_nginx_domains_list="" 4 | v2ray_nginx_domains_count=0 5 | v2ray_nginx_domains=() 6 | 7 | if ls -A "$nginx_prefix/conf/sites_available/"* > /dev/null 2>&1 8 | then 9 | for f in "$nginx_prefix/conf/sites_available/"* 10 | do 11 | domain=${f##*/} 12 | domain=${domain%.conf} 13 | v2ray_nginx_domains_count=$((v2ray_nginx_domains_count+1)) 14 | v2ray_nginx_domains+=("$domain") 15 | if [ -e "$nginx_prefix/conf/sites_enabled/$domain.conf" ] 16 | then 17 | v2ray_nginx_domain_status_text="${green} [开启] ${normal}" 18 | else 19 | v2ray_nginx_domain_status_text="${red} [关闭] ${normal}" 20 | fi 21 | v2ray_nginx_domains_list="$v2ray_nginx_domains_list ${green}$v2ray_nginx_domains_count.${normal}${indent_6}$domain $v2ray_nginx_domain_status_text\n\n" 22 | done 23 | fi 24 | 25 | Println "${green}域名列表:${normal}\n\n${v2ray_nginx_domains_list:-无}" 26 | } 27 | -------------------------------------------------------------------------------- /src/v2ray/list_reverse: -------------------------------------------------------------------------------- 1 | V2rayListReverse() 2 | { 3 | V2rayGetReverse 4 | 5 | if [ "$reverse_bridges_count" -eq 0 ] 6 | then 7 | reverse_bridges_list="bridge 列表: ${red}无${normal}\n\n" 8 | else 9 | reverse_bridges_list="bridge 列表:\n\n" 10 | for((reverse_bridges_i=0;reverse_bridges_i "$VIP_FILE" 12 | fi 13 | 14 | new_host=$( 15 | $JQ_FILE -n --arg ip "$vip_host_ip" --arg port "$vip_host_port" \ 16 | --arg seed "$vip_host_seed" --arg token "$vip_host_token" \ 17 | --arg status "$vip_host_status" \ 18 | '{ 19 | ip: $ip, 20 | port: $port | tonumber, 21 | seed: $seed, 22 | token: $token, 23 | status: $status, 24 | channels: [] 25 | }' 26 | ) 27 | 28 | jq_path='["hosts"]' 29 | JQ add "$VIP_FILE" "[$new_host]" 30 | Println "$info VIP 服务器添加成功\n" 31 | } 32 | -------------------------------------------------------------------------------- /src/vip/add_user: -------------------------------------------------------------------------------- 1 | VipAddUser() 2 | { 3 | if [ ! -s "$VIP_FILE" ] 4 | then 5 | printf '{"%s":{},"%s":[],"%s":[]}' "config" "users" "hosts" > "$VIP_FILE" 6 | fi 7 | 8 | VipSetUserIp 9 | VipSetUserLicense 10 | VipSetUserSum 11 | VipSetUserName 12 | 13 | new_user=$( 14 | $JQ_FILE -n --arg ip "$vip_user_ip" --arg license "$vip_user_license" \ 15 | --arg sum "$vip_user_sum" --arg expire "$vip_user_expire" \ 16 | --arg name "$vip_user_name" \ 17 | '{ 18 | ip: $ip, 19 | license: $license, 20 | sum: $sum, 21 | expire: $expire | tonumber, 22 | name: $name 23 | }' 24 | ) 25 | 26 | jq_path='["users"]' 27 | JQ add "$VIP_FILE" "[$new_user]" 28 | 29 | Println "$info 添加成功\n" 30 | } 31 | -------------------------------------------------------------------------------- /src/vip/delete_channel: -------------------------------------------------------------------------------- 1 | VipDelChannel() 2 | { 3 | VipListChannels 4 | 5 | if [ -z "${vip_hosts_index:-}" ] 6 | then 7 | echo 8 | inquirer list_input_index "清空所有频道" ny_options ny_index 9 | 10 | if [ "$ny_index" -eq 1 ] 11 | then 12 | for((vip_hosts_index=0;vip_hosts_index /dev/null 24 | Println "$info 频道 [ $vip_channel_name ] 添加成功" 25 | continue 26 | fi 27 | 28 | user_agent="" 29 | headers="" 30 | cookies="" 31 | stream_links=("$stream_link") 32 | 33 | Println "$info 添加频道 [ $vip_channel_name ]\n\n" 34 | inquirer list_input "是否推流 flv" ny_options add_channel_flv_yn 35 | if [ "$add_channel_flv_yn" == "$i18n_yes" ] 36 | then 37 | kind="flv" 38 | else 39 | kind="" 40 | fi 41 | 42 | stream_link_parsed=true 43 | AddChannel 44 | done 45 | } 46 | -------------------------------------------------------------------------------- /src/vip/disable: -------------------------------------------------------------------------------- 1 | VipDisable() 2 | { 3 | # deprecated 4 | if [ -s "/tmp/vip.pid" ] 5 | then 6 | vip_pid=$(< /tmp/vip.pid) 7 | if kill -0 "$vip_pid" 2> /dev/null 8 | then 9 | kill "$vip_pid" 2> /dev/null 10 | MonitorLog "关闭 VIP PID $vip_pid !" 11 | Println "$info VIP 关闭成功\n" 12 | else 13 | Println "$error VIP 未开启\n" 14 | fi 15 | rm -f "/tmp/vip.pid" 16 | elif [ -s "$IPTV_ROOT/vip.pid" ] 17 | then 18 | PID=$(< "$IPTV_ROOT/vip.pid") 19 | if kill -0 "$PID" 2> /dev/null 20 | then 21 | Println "$info 关闭 VIP, 稍等..." 22 | kill "$PID" 2> /dev/null 23 | if flock -E 1 -w 20 -x "$IPTV_ROOT/vip.pid" rm -f "$IPTV_ROOT/vip.pid" 24 | then 25 | MonitorLog "关闭 VIP PID $PID !" 26 | Println "$info VIP 关闭成功 !\n" 27 | else 28 | Println "$error VIP 关闭超时, 请重试\n" 29 | exit 1 30 | fi 31 | else 32 | rm -f "$IPTV_ROOT/vip.pid" 33 | Println "$error VIP 未开启\n" 34 | fi 35 | else 36 | [ -e "$IPTV_ROOT/vip.pid" ] && rm -f "$IPTV_ROOT/vip.pid" 37 | Println "$error VIP 未开启\n" 38 | fi 39 | } 40 | -------------------------------------------------------------------------------- /src/vip/enable: -------------------------------------------------------------------------------- 1 | VipEnable() 2 | { 3 | # deprecated 4 | if [ -s "/tmp/vip.pid" ] && kill -0 "$(< /tmp/vip.pid)" 2> /dev/null 5 | then 6 | Println "$error VIP 已开启\n" && exit 1 7 | fi 8 | 9 | if [ -s "$IPTV_ROOT/vip.pid" ] && kill -0 "$(< $IPTV_ROOT/vip.pid)" 2> /dev/null 10 | then 11 | Println "$error VIP 已开启\n" && exit 1 12 | fi 13 | 14 | if [ ! -s "$VIP_FILE" ] 15 | then 16 | Println "$error 请先添加 VIP 服务器\n" && exit 1 17 | fi 18 | 19 | echo 20 | inquirer text_input "输入检测间隔(秒): " sleep_time 86400 21 | 22 | VipGetHosts 23 | 24 | if [ "$vip_hosts_count" -gt 0 ] 25 | then 26 | VipGetUsers 27 | 28 | if [ "$vip_users_count" -gt 0 ] 29 | then 30 | if [ ! -e "$MD5SUM_FILE" ] 31 | then 32 | Println "$info 安装 md5sum..." 33 | if [[ ! -x $(command -v gcc) ]] 34 | then 35 | if [ "$dist" == "rpm" ] 36 | then 37 | yum -y install gcc gcc-c++ >/dev/null 2>&1 38 | else 39 | apt-get -y install build-essential >/dev/null 2>&1 40 | fi 41 | fi 42 | mkdir -p "$C_ROOT" 43 | wget --timeout=10 --tries=1 --no-check-certificate "$MD5SUM_LINK" -qO "$MD5SUM_FILE.c" \ 44 | || wget --timeout=10 --tries=3 --no-check-certificate "$MD5SUM_LINK_FALLBACK" -qO "$MD5SUM_FILE.c" 45 | gcc -Wall -O3 -o "$MD5SUM_FILE" "$MD5SUM_FILE.c" 46 | Println "$info md5sum 安装成功" 47 | fi 48 | if [ -z "${vip_public_root:-}" ] 49 | then 50 | VipConfig 51 | fi 52 | [ -n "$vip_public_root" ] && ln -sfT "$VIP_USERS_ROOT" "$vip_public_root/vip" 53 | 54 | ( VipMonitor ) > /dev/null 2> /dev/null < /dev/null & 55 | 56 | Println "$info VIP 开启成功\n" 57 | else 58 | Println "$error 请先添加用户\n" && exit 1 59 | fi 60 | else 61 | Println "$error 请先添加 VIP 服务器\n" && exit 1 62 | fi 63 | } 64 | -------------------------------------------------------------------------------- /src/vip/get_config: -------------------------------------------------------------------------------- 1 | VipGetConfig() 2 | { 3 | while IFS=" " read -r key value 4 | do 5 | if [ -z "$key" ] 6 | then 7 | break 8 | else 9 | read -r vip_${key?} <<< "$value" 10 | fi 11 | done < <($JQ_FILE -r '.config|to_entries[]|[.key,.value]|join(" ")' "$VIP_FILE") 12 | return 0 13 | } 14 | -------------------------------------------------------------------------------- /src/vip/get_hosts: -------------------------------------------------------------------------------- 1 | VipGetHosts() 2 | { 3 | vip_hosts_list="" 4 | vip_hosts_count=0 5 | vip_hosts_ip=() 6 | vip_hosts_port=() 7 | vip_hosts_seed=() 8 | vip_hosts_token=() 9 | vip_hosts_status=() 10 | vip_hosts_channel_count=() 11 | vip_hosts_channel_id=() 12 | vip_hosts_channel_name=() 13 | vip_hosts_channel_epg_id=() 14 | while IFS="^" read -r ip port seed token status channels_count channels_id channels_name channels_epg_id 15 | do 16 | vip_hosts_count=$((vip_hosts_count+1)) 17 | ip=${ip#\"} 18 | vip_hosts_ip+=("$ip") 19 | vip_hosts_port+=("$port") 20 | vip_hosts_seed+=("$seed") 21 | vip_hosts_token+=("$token") 22 | vip_hosts_status+=("$status") 23 | if [ "$status" == "on" ] 24 | then 25 | status_text="${green} [启用] ${normal}" 26 | else 27 | status_text="${red} [禁用] ${normal}" 28 | fi 29 | vip_hosts_channel_count+=("$channels_count") 30 | vip_hosts_channel_id+=("$channels_id") 31 | vip_hosts_channel_name+=("$channels_name") 32 | channels_epg_id=${channels_epg_id%\"} 33 | vip_hosts_channel_epg_id+=("$channels_epg_id") 34 | vip_hosts_list="$vip_hosts_list ${green}$vip_hosts_count.${normal}${indent_6}服务器: ${green}$ip${normal} 端口: ${green}$port${normal} 频道数: ${green}$channels_count${normal}$status_text\n${indent_6}seed: ${green}$seed${normal} token: ${green}${token:-无}${normal}\n\n" 35 | done < <($JQ_FILE '.hosts[]|[.ip,.port,.seed,.token,.status,(.channels|length),([.channels[].id]|join("|")),([.channels[].name]|join("|")),([.channels[].epg_id]|join("|"))]|join("^")' "$VIP_FILE") 36 | return 0 37 | } 38 | -------------------------------------------------------------------------------- /src/vip/get_schedules: -------------------------------------------------------------------------------- 1 | VipGetSchedules() 2 | { 3 | GetDefault 4 | if [ -n "$d_schedule_file" ] && [ -s "$d_schedule_file" ] 5 | then 6 | schedules_id=() 7 | schedules_sys_time=() 8 | schedules_title=() 9 | while IFS="%" read -r schedule_id schedule_sys_time schedule_tile 10 | do 11 | schedules_id+=("${schedule_id#\"}") 12 | schedules_sys_time+=("$schedule_sys_time") 13 | schedules_title+=("${schedule_tile%\"}") 14 | done < <($JQ_FILE -M 'to_entries[]|[.key,([.value[].sys_time]|join("^")),([.value[].title]|join("^"))]|join("%")' "$d_schedule_file") 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /src/vip/get_stream: -------------------------------------------------------------------------------- 1 | VipGetStreamLink() 2 | { 3 | seed="$vip_host_seed" 4 | tid="$vip_user_license" 5 | tid_lower=$(tr '[:upper:]' '[:lower:]' <<< "$tid") 6 | if [ "$vip_user_expire" -gt 0 ] 7 | then 8 | day=$((vip_user_expire/86400)) 9 | st2="$vip_user_expire" 10 | else 11 | printf -v now '%(%s)T' -1 12 | st2=$((now+86400*720)) 13 | fi 14 | 15 | token="$vip_host_token" 16 | ss=$(printf '%s' "$st2$token$vip_user_ip$tid" | md5sum) 17 | ss=${ss%% *} 18 | [ -z "${ct2:-}" ] && ct2=$(date +%s%3N) 19 | vip_channel_id_lower=$(tr '[:upper:]' '[:lower:]' <<< "$vip_channel_id") 20 | cs=$(printf '%s' "$st2$ss$ct2$vip_channel_id_lower$tid_lower" | md5sum) 21 | cs=${cs%% *} 22 | 23 | case $vip_user_sum in 24 | "ssum") 25 | ssum="$seed/$vip_channel_id/playlist.m3u8$tid$day" 26 | ssum=$(printf '%s' "$ssum" | md5sum) 27 | ssum=${ssum%% *} 28 | stream_link="http://$vip_host_ip:$vip_host_port/$vip_channel_id/playlist.m3u8?tid=$tid&ssum=$ssum&st2=$st2&ss=$ss&ct2=$ct2&cs=$cs" 29 | ;; 30 | "tsum") 31 | ct=$day 32 | tsum="$seed/$vip_channel_id/playlist.m3u8$tid$ct" 33 | tsum=$(printf '%s' "$tsum" | md5sum) 34 | tsum=${tsum%% *} 35 | stream_link="http://$vip_host_ip:$vip_host_port/$vip_channel_id/playlist.m3u8?tid=$tid&ct=$ct&tsum=$tsum&st2=$st2&ss=$ss&ct2=$ct2&cs=$cs" 36 | ;; 37 | "isum") 38 | isum="$seed$vip_user_ip/$vip_channel_id/playlist.m3u8$tid" 39 | isum=$(printf '%s' "$isum" | md5sum) 40 | isum=${isum%% *} 41 | stream_link="http://$vip_host_ip:$vip_host_port/$vip_channel_id/playlist.m3u8?tid=$tid&isum=$isum&st2=$st2&ss=$ss&ct2=$ct2&cs=$cs" 42 | ;; 43 | esac 44 | } 45 | -------------------------------------------------------------------------------- /src/vip/get_users: -------------------------------------------------------------------------------- 1 | VipGetUsers() 2 | { 3 | VipGetConfig 4 | vip_users_list="" 5 | vip_users_count=0 6 | vip_users_ip=() 7 | vip_users_license=() 8 | vip_users_sum=() 9 | vip_users_expire=() 10 | vip_users_name=() 11 | while IFS=":" read -r ip license sum expire name 12 | do 13 | vip_users_count=$((vip_users_count+1)) 14 | ip=${ip#\"} 15 | vip_users_ip+=("$ip") 16 | vip_users_license+=("$license") 17 | vip_users_sum+=("$sum") 18 | vip_users_expire+=("$expire") 19 | name=${name%\"} 20 | vip_users_name+=("$name") 21 | if [ "$expire" -gt 0 ] 22 | then 23 | expire_text=$(date +%c --date=@"$expire") 24 | else 25 | expire_text="无" 26 | fi 27 | if [ -n "${vip_public_host:-}" ] 28 | then 29 | m3u_link="$vip_public_host/vip/$license/playlist.m3u" 30 | else 31 | m3u_link="${FFMPEG_MIRROR_LINK%/*}/vip/$license/playlist.m3u" 32 | fi 33 | vip_users_list="$vip_users_list ${green}$vip_users_count.${normal}${indent_6}用户名: ${green}$name${normal} ip: ${green}$ip${normal} 到期日: ${green}$expire_text${normal}\n${indent_6}授权码: ${green}$license${normal} 认证方式: ${green}$sum${normal}\n${indent_6}m3u 播放链接: ${green}$m3u_link${normal}\n\n" 34 | done < <($JQ_FILE '.users[]|[.ip,.license,.sum,.expire,.name]|join(":")' "$VIP_FILE") 35 | return 0 36 | } 37 | -------------------------------------------------------------------------------- /src/vip/list_hosts: -------------------------------------------------------------------------------- 1 | VipListHosts() 2 | { 3 | if [ ! -s "$VIP_FILE" ] 4 | then 5 | Println "$error 请先添加 VIP 服务器\n" && exit 1 6 | fi 7 | 8 | VipGetHosts 9 | 10 | if [ "$vip_hosts_count" -gt 0 ] 11 | then 12 | Println "$vip_hosts_list" 13 | else 14 | Println "$error 请先添加 VIP 服务器\n" && exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /src/vip/list_user: -------------------------------------------------------------------------------- 1 | VipListUser() 2 | { 3 | VipListUsers 4 | } 5 | -------------------------------------------------------------------------------- /src/vip/list_user_channel: -------------------------------------------------------------------------------- 1 | VipListUserChannel() 2 | { 3 | if [ ! -s "$VIP_FILE" ] 4 | then 5 | Println "$error 请先输入授权码, 加微信 woniuzfb 或 tg @ woniuzfb\n" 6 | else 7 | VipGetUsers 8 | printf -v now '%(%s)T' -1 9 | vip_users_list="" 10 | for((i=0;i "$VIP_USERS_ROOT/$vip_user_license/${vip_host_ip//./}$vip_host_port/${vip_channels_id[k]}/playlist.m3u8" 6 | if [ "$k" -eq 0 ] 7 | then 8 | m3u_list="$m3u_list#EXTINF:-1,===== $vip_host_ip:$vip_host_port ${vip_channels_name[k]} =====\n$stream_link\n" 9 | else 10 | m3u_list="$m3u_list#EXTINF:-1,${vip_channels_name[k]}\n$stream_link\n" 11 | fi 12 | epg_id=${vip_channels_epg_id[k]} 13 | if [ -n "$epg_id" ] && [ -n "${schedules_id:-}" ] 14 | then 15 | for((m=0;m<${#schedules_id[@]};m++)); 16 | do 17 | if [ "${schedules_id[m]}" == "$epg_id" ] && [ -n "${schedules_sys_time[m]}" ] 18 | then 19 | IFS="^" read -r -a sys_times <<< "${schedules_sys_time[m]}^" 20 | IFS="^" read -r -a titles <<< "${schedules_title[m]}^" 21 | epg_list="$epg_list\n${vip_channels_name[k]}\n\n" 22 | programs_count=${#sys_times[@]} 23 | for((n=0;n\n${titles[n]}\n\n" 33 | done 34 | break 35 | fi 36 | done 37 | fi 38 | } 39 | -------------------------------------------------------------------------------- /src/vip/set_channel: -------------------------------------------------------------------------------- 1 | VipSetChannelId() 2 | { 3 | Println "请输入频道 ID, 同时也是目录名称" 4 | read -p "$i18n_default_cancel" vip_channel_id 5 | 6 | [ -z "$vip_channel_id" ] && Println "$i18n_canceled...\n" && exit 1 7 | 8 | if [[ -n $($JQ_FILE --arg vip_host_ip "$vip_host_ip" --arg vip_channel_id "$vip_channel_id" '.hosts[] | select(.ip==$vip_host_ip).channels[] | select(.id==$vip_channel_id)' "$VIP_FILE") ]] 9 | then 10 | Println "$error $vip_channel_id 频道已经存在\n" && exit 1 11 | fi 12 | } 13 | 14 | VipSetChannelName() 15 | { 16 | Println "请输入频道名称(可以是中文)" 17 | read -p "$i18n_default_cancel" vip_channel_name 18 | [ -z "$vip_channel_name" ] && Println "$i18n_canceled...\n" && exit 1 19 | Println " VIP 频道名称: ${green} $vip_channel_name ${normal}\n" 20 | } 21 | 22 | VipSetChannelEpgId() 23 | { 24 | echo 25 | inquirer text_input "请输入频道 epg id: " vip_channel_epg_id "$i18n_blank" 26 | } 27 | -------------------------------------------------------------------------------- /src/vip/set_host: -------------------------------------------------------------------------------- 1 | VipSetHostIp() 2 | { 3 | Println "请输入 VIP 频道所在服务器 IP/域名" 4 | read -p "$i18n_default_cancel" vip_host_ip 5 | [ -z "$vip_host_ip" ] && Println "$i18n_canceled...\n" && exit 1 6 | Println " VIP 服务器 IP/域名: ${green} $vip_host_ip ${normal}\n" 7 | } 8 | 9 | VipSetHostPort() 10 | { 11 | Println "请输入 VIP 频道所在服务器端口" 12 | read -p "$i18n_default_cancel" vip_host_port 13 | [ -z "$vip_host_port" ] && Println "$i18n_canceled...\n" && exit 1 14 | Println " VIP 服务器端口: ${green} $vip_host_port ${normal}\n" 15 | } 16 | 17 | VipSetHostSeed() 18 | { 19 | Println "请输入 VIP 频道所在服务器的 seed" 20 | read -p "$i18n_default_cancel" vip_host_seed 21 | [ -z "$vip_host_seed" ] && Println "$i18n_canceled...\n" && exit 1 22 | Println " VIP 服务器 seed: ${green} $vip_host_seed ${normal}\n" 23 | } 24 | 25 | VipSetHostToken() 26 | { 27 | echo 28 | inquirer text_input "请输入 VIP 频道所在服务器的 token: " vip_host_token "$i18n_blank" 29 | } 30 | 31 | VipSetHostStatus() 32 | { 33 | echo 34 | inquirer list_input "是否开启用此 VIP 服务器" yn_options vip_host_status 35 | 36 | if [[ $vip_host_status == "$i18n_yes" ]] 37 | then 38 | vip_host_status="on" 39 | vip_host_status_text="${green}启用${normal}" 40 | else 41 | vip_host_status="off" 42 | vip_host_status_text="${red}禁用${normal}" 43 | fi 44 | Println " VIP 服务器状态: $vip_host_status_text\n" 45 | } 46 | -------------------------------------------------------------------------------- /src/vip/set_public: -------------------------------------------------------------------------------- 1 | VipSetPublicRoot() 2 | { 3 | Println "请输入公开目录, 比如 /usr/local/nginx/html 或 /usr/local/openresty/nginx/html" 4 | read -p "(默认: 不公开): " vip_public_root 5 | if [ -n "$vip_public_root" ] 6 | then 7 | vip_public_root=${vip_public_root%\/} 8 | fi 9 | jq_path='["config","public_root"]' 10 | JQ update "$VIP_FILE" "$vip_public_root" 11 | Println " VIP 公开目录: ${green} ${vip_public_root:-无} ${normal}\n" 12 | } 13 | 14 | VipSetPublicHost() 15 | { 16 | Println "$tip 比如 http://localhost" 17 | inquirer text_input "请输入公开目录的 域名 或者 IP 网址: " vip_public_host "$i18n_blank" 18 | jq_path='["config","public_host"]' 19 | JQ update "$VIP_FILE" "$vip_public_host" 20 | } 21 | 22 | VipConfig() 23 | { 24 | VipSetPublicRoot 25 | if [ -n "$vip_public_root" ] 26 | then 27 | VipSetPublicHost 28 | fi 29 | } 30 | -------------------------------------------------------------------------------- /src/vip/verify_license: -------------------------------------------------------------------------------- 1 | VipVerifyLicense() 2 | { 3 | Println "请输入授权码" 4 | read -p "$i18n_default_cancel" vip_user_license 5 | [ -z "$vip_user_license" ] && Println "$i18n_canceled...\n" && exit 1 6 | 7 | if vip_user=$(wget --timeout=10 --tries=3 --no-check-certificate "${FFMPEG_MIRROR_LINK%/*}/vip/$vip_user_license/license.json" -qO- 2> /dev/null) 8 | then 9 | if [ ! -s "$VIP_FILE" ] 10 | then 11 | printf '{"%s":{},"%s":[],"%s":[]}' "config" "users" "hosts" > "$VIP_FILE" 12 | fi 13 | 14 | while IFS= read -r license_ip 15 | do 16 | map_string=true 17 | jq_path='["users"]' 18 | JQ delete "$VIP_FILE" ip "$license_ip" 19 | done < <($JQ_FILE -r '.ip' <<< "$vip_user") 20 | 21 | jq_path='["users"]' 22 | JQ add "$VIP_FILE" "[$vip_user]" 23 | Println "$info 授权码验证成功\n" 24 | else 25 | Println "$error 授权码验证失败, 请联系微信 woniuzfb 或 tg @ woniuzfb\n" 26 | fi 27 | } 28 | -------------------------------------------------------------------------------- /src/xtream_codes/add_account: -------------------------------------------------------------------------------- 1 | XtreamCodesAddAccount() 2 | { 3 | echo && read -p "请输入账号(需包含服务器地址): " xtream_codes_input 4 | [ -z "$xtream_codes_input" ] && Println "$i18n_canceled...\n" && exit 1 5 | 6 | if [[ $xtream_codes_input == *"username="* ]] 7 | then 8 | domain=${xtream_codes_input#*http://} 9 | domain=${domain%%/*} 10 | username=${xtream_codes_input#*username=} 11 | username=${username%%&*} 12 | password=${xtream_codes_input#*password=} 13 | password=${password%%&*} 14 | ip=$(getent ahosts "${domain%%:*}" | awk '{ print $1 ; exit }') || true 15 | elif [[ $xtream_codes_input =~ ^http://([^/]+)/([^/]+)/([^/]+)/ ]] 16 | then 17 | if [ "${BASH_REMATCH[2]}" == "live" ] 18 | then 19 | if [[ $line =~ ^http://([^/]+)/live/([^/]+)/([^/]+)/ ]] 20 | then 21 | domain=${BASH_REMATCH[1]} 22 | username=${BASH_REMATCH[2]} 23 | password=${BASH_REMATCH[3]} 24 | else 25 | Println "$error 输入错误\n" && exit 1 26 | fi 27 | else 28 | domain=${BASH_REMATCH[1]} 29 | username=${BASH_REMATCH[2]} 30 | password=${BASH_REMATCH[3]} 31 | fi 32 | ip=$(getent ahosts "${domain%%:*}" | awk '{ print $1 ; exit }') || true 33 | else 34 | Println "$error 输入错误\n" && exit 1 35 | fi 36 | 37 | [ -z "${ip:-}" ] && Println "$error 无法解析域名 !\n" && exit 1 38 | printf '%s\n' "$ip $domain $username:$password" >> "$XTREAM_CODES_EXAM" 39 | 40 | Println "$info 账号添加成功 !\n" 41 | } 42 | -------------------------------------------------------------------------------- /src/xtream_codes/domain_filter: -------------------------------------------------------------------------------- 1 | XtreamCodesDomainFilter() 2 | { 3 | local domain=$1 4 | 5 | if [ ! -e "$XTREAM_CODES"_domain_filter ] 6 | then 7 | ShFallback 8 | if ! curl -s -Lm 10 "$SH_FALLBACK/xtream_codes_domain_filter" -o "$XTREAM_CODES"_domain_filter 9 | then 10 | echo "$domain" 11 | return 0 12 | fi 13 | fi 14 | 15 | local domain_match domain_replace 16 | 17 | while IFS= read -r line 18 | do 19 | domain_match=${line% *} 20 | domain_replace=${line#* } 21 | if [[ $domain_match =~ : ]] 22 | then 23 | if [ "$domain" == "$domain_match" ] 24 | then 25 | domain="$domain_replace" 26 | break 27 | fi 28 | elif [[ $domain_replace =~ : ]] 29 | then 30 | if [ "${domain%:*}" == "$domain_match" ] 31 | then 32 | domain="$domain_replace" 33 | break 34 | fi 35 | elif [[ $domain =~ : ]] 36 | then 37 | if [ "${domain%:*}" == "$domain_match" ] 38 | then 39 | domain="$domain_replace:${domain#*:}" 40 | break 41 | fi 42 | elif [ "$domain" == "$domain_match" ] 43 | then 44 | domain="$domain_replace" 45 | break 46 | fi 47 | done < "$XTREAM_CODES"_domain_filter 48 | 49 | echo "$domain" 50 | } 51 | -------------------------------------------------------------------------------- /src/xtream_codes/get_channels: -------------------------------------------------------------------------------- 1 | XtreamCodesGetChnls() 2 | { 3 | xc_chnls=() 4 | xc_chnls_mac=() 5 | if [ -n "${xtream_codes_domains:-}" ] 6 | then 7 | GetChannels 8 | if [ "$chnls_count" -gt 0 ] 9 | then 10 | for((xc_i=0;xc_i /dev/null) || ordered_list_page="" 27 | fi 28 | ordered_list_pages[page_index]="$ordered_list_page" 29 | fi 30 | 31 | while IFS= read -r name 32 | do 33 | name=${name#\"} 34 | name=${name%\"} 35 | name_lower=$(tr '[:upper:]' '[:lower:]' <<< "$name") 36 | if [[ $name_lower == *"$search_phrase"* ]] 37 | then 38 | search_result="$search_result页数: ${green}$i${normal} 频道名称: ${green}$name${normal}\n\n" 39 | fi 40 | done < <($JQ_FILE '.js.data[].name' <<< "$ordered_list_page") 41 | done 42 | } 43 | -------------------------------------------------------------------------------- /utils/crossplane: -------------------------------------------------------------------------------- 1 | CrossplaneInstall() 2 | { 3 | if [[ -x $(command -v crossplane) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | Println "$info 安装 crossplane ..." 9 | 10 | PythonInstall 11 | pip3 install crossplane 12 | } 13 | -------------------------------------------------------------------------------- /utils/dnscrypt: -------------------------------------------------------------------------------- 1 | DNSCryptConfig() 2 | { 3 | if [ "$location_china" == "$i18n_yes" ] 4 | then 5 | echo "$(awk '!x{x=sub(/.*\[static\..*/," [static.\047alidns-doh-fix\047]")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 6 | echo "$(awk '!x{x=sub(/.* stamp = .*/," stamp = \047sdns://AgAAAAAAAAAAACCY49XlNq8pWM0vfxT3BO9KJ20l4zzWXy5l9eTycnwTMA5kbnMuYWxpZG5zLmNvbQovZG5zLXF1ZXJ5\047")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 7 | echo "$(awk '!x{x=sub(/.*server_names = \[.*/,"server_names = [\047dnspod-doh\047,\047alidns-doh-fix\047]")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 8 | echo "$(awk '!x{x=sub(/.*bootstrap_resolvers = .*/,"bootstrap_resolvers = [\047119.29.29.29:53\047, \047180.76.76.76:53\047, \0471.1.1.1:53\047, \047114.114.114.114:53\047, \0478.8.8.8:53\047]")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 9 | echo "$(awk '!x{x=sub(/.*netprobe_address = .*/,"netprobe_address = \0471.1.1.1:53\047")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 10 | else 11 | echo "$(awk '!x{x=sub(/.*server_names = \[.*/,"server_names = [\047google\047, \047cloudflare\047]")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 12 | fi 13 | 14 | echo "$(awk '!x{x=sub(/^listen_addresses = .*/,"listen_addresses = [\047[::]:'"${listen_port:-53}"'\047]")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 15 | echo "$(awk '!x{x=sub(/.*block_ipv6 = .*/,"block_ipv6 = '"${block_ipv6:-false}"'")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 16 | echo "$(awk '!x{x=sub(/.*require_dnssec = .*/,"require_dnssec = '"${require_dnssec:-true}"'")}1' dnscrypt-proxy.toml)" > dnscrypt-proxy.toml 17 | } 18 | -------------------------------------------------------------------------------- /utils/git: -------------------------------------------------------------------------------- 1 | GitInstall() 2 | { 3 | if [[ -x $(command -v git) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | Println "$info 安装 git" 9 | 10 | case $dist in 11 | mac) brew install git 12 | ;; 13 | rpm) yum -y install git 14 | ;; 15 | deb) apt-get -y install git 16 | ;; 17 | ubu) 18 | add-apt-repository ppa:git-core/ppa -y 19 | apt-get -y update 20 | apt-get -y install git 21 | ;; 22 | esac 23 | 24 | Println "$info git 安装成功\n" 25 | } 26 | -------------------------------------------------------------------------------- /utils/go: -------------------------------------------------------------------------------- 1 | GoInstall() 2 | { 3 | if [[ ! "$PATH" =~ /usr/local/go/bin ]] 4 | then 5 | PATH="$PATH":/usr/local/go/bin 6 | fi 7 | 8 | go_path=${GOPATH:-"$HOME"/go} 9 | 10 | if [[ ! "$PATH" =~ $go_path ]] 11 | then 12 | PATH="${PATH}:${go_path}/bin" 13 | fi 14 | 15 | export PATH 16 | 17 | if [[ -x $(command -v go) ]] 18 | then 19 | go_version_list=($(go version)) 20 | if [[ "${go_version_list[2]}" =~ ^go([0-9]+)\.([0-9]+)\. ]] && [ "${BASH_REMATCH[1]}" -ge 1 ] && [ "${BASH_REMATCH[2]}" -ge 11 ] 21 | then 22 | return 0 23 | fi 24 | fi 25 | 26 | if [ "$dist" == "mac" ] 27 | then 28 | DepInstall go 29 | return 0 30 | fi 31 | 32 | ArchCheck 33 | 34 | DepInstall curl 35 | 36 | JQInstall 37 | 38 | if ! go_version=$(curl -s -Lm 20 https://go.dev/dl/?mode=json | $JQ_FILE -r '.[0].version') 39 | then 40 | Println "$error 无法连接 go.dev ?" 41 | go_version=1.22.2 42 | fi 43 | 44 | if [ "$arch" == "i386" ] 45 | then 46 | go_package="$go_version.linux-386.tar.gz" 47 | elif [ "$arch" == "x86_64" ] 48 | then 49 | go_package="$go_version.linux-amd64.tar.gz" 50 | elif [ "$arch" == "arm64" ] || [ "$arch" == "armv6l" ] 51 | then 52 | go_package="$go_version.linux-$arch.tar.gz" 53 | else 54 | DepInstall golang 55 | return 0 56 | fi 57 | 58 | if ! curl -L https://golang.org/dl/$go_package -o ~/$go_package && ! curl -L https://gomirrors.org/dl/$go_package -o ~/$go_package 59 | then 60 | Println "$error 下载 golang 失败, 请稍后再试\n" 61 | return 1 62 | fi 63 | 64 | rm -rf /usr/local/go 65 | tar -C /usr/local -xzf ~/$go_package 66 | } 67 | -------------------------------------------------------------------------------- /utils/htpasswd: -------------------------------------------------------------------------------- 1 | HtpasswdInstall() 2 | { 3 | if [[ -x $(command -v htpasswd) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | if [ "$dist" == "mac" ] 9 | then 10 | DepInstall httpd 11 | elif [ "$dist" == "rpm" ] 12 | then 13 | DepInstall httpd-tools 14 | else 15 | DepInstall apache2-utils 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /utils/imagemagick: -------------------------------------------------------------------------------- 1 | ImageMagickInstall() 2 | { 3 | if [[ -x $(command -v convert) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | rm -f "$IPTV_ROOT/magick" 9 | 10 | if [ "$dist" == "rpm" ] 11 | then 12 | DepInstall ImageMagick 13 | else 14 | DepInstall imagemagick 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /utils/imgcat: -------------------------------------------------------------------------------- 1 | ImgcatInstall() 2 | { 3 | echo 4 | ExitOnList y "`gettext \"缺少 imgcat, 是否现在安装\"`" 5 | 6 | Progress & 7 | progress_pid=$! 8 | trap ' 9 | kill $progress_pid 10 | wait $progress_pid 2> /dev/null 11 | ' EXIT 12 | if [ "$dist" == "rpm" ] 13 | then 14 | yum -y install gcc gcc-c++ make ncurses-devel autoconf libjpeg-turbo-devel libpng-devel >/dev/null 2>&1 15 | echo -n "...50%..." 16 | else 17 | apt-get -y install debconf-utils libncurses5-dev autotools-dev autoconf libjpeg-dev libpng-dev >/dev/null 2>&1 18 | echo '* libraries/restart-without-asking boolean true' | debconf-set-selections 19 | apt-get -y install software-properties-common pkg-config build-essential >/dev/null 2>&1 20 | echo -n "...50%..." 21 | fi 22 | 23 | cd ~ 24 | 25 | if [ ! -d imgcat-master ] 26 | then 27 | wget --timeout=10 --tries=3 --no-check-certificate "$FFMPEG_MIRROR_LINK/imgcat.zip" -qO imgcat.zip 28 | unzip imgcat.zip >/dev/null 2>&1 29 | fi 30 | 31 | cd ./imgcat-master 32 | rm -rf CImg 33 | wget --timeout=10 --tries=3 --no-check-certificate "$FFMPEG_MIRROR_LINK/CImg.zip" -qO CImg.zip 34 | unzip CImg.zip >/dev/null 2>&1 35 | mv CImg-master CImg 36 | ./configure >/dev/null 2>&1 37 | make >/dev/null 2>&1 38 | make install >/dev/null 2>&1 39 | kill $progress_pid 40 | wait $progress_pid 2> /dev/null || true 41 | trap - EXIT 42 | echo -n "...100%" && Println "$info imgcat 安装完成" 43 | } 44 | -------------------------------------------------------------------------------- /utils/log: -------------------------------------------------------------------------------- 1 | ChangeLogFile() 2 | { 3 | if [ "${change_log_file:-true}" = false ] 4 | then 5 | return 0 6 | fi 7 | 8 | if [ -d "${MONITOR_LOG%/*}" ] 9 | then 10 | LOG_FILE="$MONITOR_LOG" 11 | fi 12 | 13 | change_log_file=false 14 | } 15 | 16 | LogInfo() 17 | { 18 | ChangeLogFile 19 | printf -v date_now '%(%m-%d %H:%M:%S)T' -1 20 | printf '%s\n' "$date_now $1" >> "$LOG_FILE" 21 | } 22 | 23 | LogErr() 24 | { 25 | ChangeLogFile 26 | printf -v date_now '%(%m-%d %H:%M:%S)T' -1 27 | printf '%s\n' "$date_now [ERROR: $1]" >> "$LOG_FILE" 28 | } 29 | -------------------------------------------------------------------------------- /utils/node: -------------------------------------------------------------------------------- 1 | NodeInstallToutiao() 2 | { 3 | if [ -f "$NODE_ROOT"/toutiao/browser.js ] 4 | then 5 | if ver=$(grep -oP 'const ver = "\K[^"]+' "$NODE_ROOT"/toutiao/browser.js) && [ "$NODE_TOUTIAO_VER" == "$ver" ] 6 | then 7 | return 8 | fi 9 | fi 10 | 11 | mkdir -p "$NODE_ROOT"/toutiao/ 12 | DepInstall curl 13 | if ! curl -Lm 20 -o "$NODE_ROOT"/toutiao/browser.js "$NODE_SCRIPTS_LINK"/browser.js 14 | then 15 | curl -L -o "$NODE_ROOT"/toutiao/browser.js "$FFMPEG_MIRROR_LINK"/browser.js 16 | fi 17 | cd "$NODE_ROOT"/toutiao/ 18 | npm i playwright-chromium 19 | Println "$info 头条签名 node 安装成功" 20 | } 21 | -------------------------------------------------------------------------------- /utils/nodejs: -------------------------------------------------------------------------------- 1 | NodejsInstall() 2 | { 3 | if [[ -x $(command -v node) ]] && [[ -x $(command -v npm) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | DepsCheck 9 | 10 | if [ "$dist" == "mac" ] 11 | then 12 | DepInstall node 13 | return 0 14 | elif [ "$dist" == "rpm" ] && { [[ -x $(command -v node) ]] || [[ -x $(command -v npm) ]]; } 15 | then 16 | yum remove -y nodejs npm || true 17 | fi 18 | 19 | Progress & 20 | progress_pid=$! 21 | 22 | trap ' 23 | kill $progress_pid 24 | wait $progress_pid 2> /dev/null 25 | ' EXIT 26 | 27 | if [ "$dist" == "rpm" ] 28 | then 29 | yum -y install gcc-c++ make >/dev/null 2>&1 30 | # yum groupinstall 'Development Tools' 31 | 32 | if curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - >/dev/null 33 | then 34 | sudo yum install -y nsolid >/dev/null 2>&1 35 | fi 36 | else 37 | if curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - >/dev/null 38 | then 39 | sudo apt-get install -y nodejs >/dev/null 2>&1 40 | fi 41 | fi 42 | 43 | kill $progress_pid 44 | wait $progress_pid 2> /dev/null || true 45 | trap - EXIT 46 | 47 | echo -n "...100%" 48 | Println "$info nodejs 安装完成" 49 | } 50 | -------------------------------------------------------------------------------- /utils/postfix: -------------------------------------------------------------------------------- 1 | PostfixInstall() 2 | { 3 | if [ "$dist" == "rpm" ] 4 | then 5 | yum -y install postfix > /dev/null 6 | else 7 | DEBIAN_FRONTEND=noninteractive apt-get -y install postfix > /dev/null 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /utils/progress: -------------------------------------------------------------------------------- 1 | Progress() 2 | { 3 | local msg 4 | 5 | if [ -z "${1:-}" ] 6 | then 7 | msg="`eval_gettext \"\\\$info 安装中, 请等待...\"`" 8 | else 9 | msg="$info $1..." 10 | fi 11 | 12 | echo -ne "\n$msg" 13 | 14 | while true 15 | do 16 | echo -n "." 17 | sleep 5 18 | done 19 | } 20 | -------------------------------------------------------------------------------- /utils/python: -------------------------------------------------------------------------------- 1 | PythonInstall() 2 | { 3 | if [[ -x $(command -v python3) ]] && [[ -x $(command -v pip3) ]] 4 | then 5 | return 0 6 | fi 7 | 8 | Println "`eval_gettext \"\\\$info 安装 python3 ...\"`" 9 | 10 | Progress & 11 | progress_pid=$! 12 | 13 | trap ' 14 | kill $progress_pid 15 | wait $progress_pid 2> /dev/null 16 | ' EXIT 17 | 18 | if [ "$dist" == "mac" ] 19 | then 20 | DepInstall python > /dev/null 21 | elif [ "$dist" == "rpm" ] 22 | then 23 | if ! yum -y install python3 python3-pip > /dev/null 2>&1 24 | then 25 | yum groupinstall -y 'Development Tools' >/dev/null 2>&1 26 | yum install -y gcc openssl-devel bzip2-devel libffi-devel >/dev/null 2>&1 27 | echo -n "...50%..." 28 | cd ~ 29 | wget --timeout=10 --tries=3 --no-check-certificate https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tgz -qO Python-3.12.3.tgz 30 | tar xzf Python-3.12.3.tgz 31 | cd Python-3.12.3 32 | ./configure >/dev/null 2>&1 33 | make >/dev/null 2>&1 34 | make install >/dev/null 2>&1 35 | pip3 install requests > /dev/null 36 | fi 37 | else 38 | apt-get -y install python3 python3-pip >/dev/null 2>&1 39 | fi 40 | 41 | kill $progress_pid 42 | wait $progress_pid 2> /dev/null || true 43 | trap - EXIT 44 | echo "...100%" 45 | } 46 | -------------------------------------------------------------------------------- /utils/spinner: -------------------------------------------------------------------------------- 1 | # based on https://raw.githubusercontent.com/kahkhang/ora.sh/master/ora.sh 2 | Spinner() 3 | { 4 | DepInstall tput 5 | 6 | local i=1 delay=0.05 FUNCTION_NAME="$2" VARIABLE_NAME="${3:-}" list TMP_FILE 7 | local green cyan normal 8 | green='\033[32m' 9 | cyan='\033[36m' 10 | normal='\033[0m' 11 | 12 | list=( '\xe2\xa0\x8b' '\xe2\xa0\x99' '\xe2\xa0\xb9' '\xe2\xa0\xb8' '\xe2\xa0\xbc' '\xe2\xa0\xb4' '\xe2\xa0\xa6' '\xe2\xa0\xa7' '\xe2\xa0\x87' '\xe2\xa0\x8f' ) 13 | 14 | if TMP_FILE=$(mktemp -q) 15 | then 16 | chmod +r "$TMP_FILE" 17 | else 18 | printf -v TMP_FILE '%(%m-%d-%H:%M:%S)T' -1 19 | fi 20 | 21 | trap ' 22 | rm -f "$TMP_FILE" 23 | inquirer cleanup 24 | ' EXIT 25 | 26 | stty -echo 27 | tput civis 28 | 29 | $FUNCTION_NAME > "$TMP_FILE" 2>> "$TMP_FILE" & 30 | local pid=$! 31 | 32 | echo 33 | tput sc 34 | printf '%b %b' "${list[i]}" "${green}$1${normal}" 35 | tput el 36 | tput rc 37 | 38 | while ps -p $pid -o pid= >/dev/null 39 | do 40 | printf '%b' "$cyan${list[i]}${normal}" 41 | i=$(((i+1)%10)) 42 | sleep $delay 43 | printf "\b\b\b" 44 | done 45 | 46 | tput el 47 | 48 | if [[ -n $VARIABLE_NAME ]] 49 | then 50 | read -r ${VARIABLE_NAME?} < "$TMP_FILE" 51 | else 52 | awk '{print}' "$TMP_FILE" 53 | fi 54 | 55 | rm -f "$TMP_FILE" 56 | 57 | tput cnorm 58 | stty echo 59 | 60 | trap - EXIT 61 | 62 | wait $pid 63 | } 64 | -------------------------------------------------------------------------------- /utils/tesseract: -------------------------------------------------------------------------------- 1 | TesseractInstall() 2 | { 3 | if [[ -x $(command -v tesseract) ]] 4 | then 5 | Println "$error tesseract 已存在!\n" 6 | return 0 7 | fi 8 | 9 | DepsCheck 10 | 11 | if [ "$dist" == "ubu" ] 12 | then 13 | add-apt-repository ppa:alex-p/tesseract-ocr -y 14 | apt-get -y update 15 | apt-get -y install tesseract 16 | elif [ "$dist" == "deb" ] 17 | then 18 | Println "$info 参考 https://notesalexp.org/tesseract-ocr/ ...\n" 19 | else 20 | Println "$info 参考 https://tesseract-ocr.github.io/tessdoc/Home.html ...\n" 21 | fi 22 | } 23 | -------------------------------------------------------------------------------- /utils/vim: -------------------------------------------------------------------------------- 1 | VimConfig() 2 | { 3 | if [[ ! -x $(command -v vim) ]] 4 | then 5 | Println "$info 安装 vim ..." 6 | AptUpdate 7 | apt-get -y install vim 8 | fi 9 | 10 | if [ -e ~/.vimrc ] 11 | then 12 | echo 13 | ExitOnList n "`gettext \"将安装 vim-plug 并覆盖 ~/.vimrc , 是否继续\"`" 14 | fi 15 | 16 | if curl -s -fLo ~/.vim/autoload/plug.vim --create-dirs "$FFMPEG_MIRROR_LINK/vim-plug.vim" 17 | then 18 | cat > "$HOME"/.vimrc < /dev/null) 25 | 26 | if [ ! -d "$DEPENDS_ROOT/$zlib_name/$build_dir" ] 27 | then 28 | mkdir -p "$DEPENDS_ROOT" 29 | cd "$DEPENDS_ROOT" 30 | curl -L "https://www.zlib.net/$zlib_name.tar.gz" -o "$zlib_name".tar.gz 31 | Println "$info 解压 $zlib_name ..." 32 | tar xzf "$zlib_name".tar.gz 33 | cd "$zlib_name" 34 | ./configure --prefix="$DEPENDS_ROOT/$zlib_name/$build_dir" ${args[@]+"${args[@]}"} 35 | make 36 | make install 37 | fi 38 | 39 | ln -sfn "$DEPENDS_ROOT/$zlib_name/$build_dir" "$zlib_path" 40 | } 41 | -------------------------------------------------------------------------------- /ver: -------------------------------------------------------------------------------- 1 | sh_ver=2.0.0 2 | --------------------------------------------------------------------------------