#!/bin/bash # ===================== 适配 Hugging Face ===================== IPTV_DIR="/usr/share/nginx/html/files" GITHUB_TOKEN="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" export PATH="/usr/bin:/bin:/usr/sbin:/sbin" mkdir -p "$IPTV_DIR" chmod -R 777 "$IPTV_DIR" # ============================================================== replace_channel_name() { local channel="$1" local result case "$channel" in *" CCTV"*) result="CCTV" ;; *"CCTV发现之旅"*) result="发现之旅" ;; *"CCTV1综合"*|*"CCTV-1 综合"*|*"CCTV1超清"*|*"CCTV1HD"*|*"CCTV1高清"*|*"CCTV-1高清"*|*"CCTV-1咪咕"*) result="CCTV1" ;; *"CCTV2财经"*|*"CCTV-2 财经"*|*"CCTV2超清"*|*"CCTV2HD"*|*"CCTV2高清"*|*"CCTV-2高清"*|*"CCTV-2咪咕"*) result="CCTV2" ;; *"CCTV3综艺"*|*"CCTV-3 综艺"*|*"CCTV3超清"*|*"CCTV3HD"*|*"CCTV3高清"*|*"CCTV-3高清"*|*"CCTV-3咪咕"*) result="CCTV3" ;; *"CCTV4中文国际"*|*"CCTV-4 中文国际"*|*"CCTV4超清"*|*"CCTV4HD"*|*"CCTV4高清"*|*"CCTV-4高清"*|*"CCTV-4咪咕"*) result="CCTV4" ;; *"CCTV5体育"*|*"CCTV-5 体育"*|*"CCTV5超清"*|*"CCTV5HD"*|*"CCTV5高清"*|*"CCTV-5高清"*|*"CCTV-5咪咕"*) result="CCTV5" ;; *"CCTV5+体育赛事"*|*"CCTV-5+ 体育赛事"*|*"CCTV5+超清"*|*"CCTV5+HD"*|*"CCTV5+高清"*|*"CCTV-5+咪咕"*|*"CCTV-5+高清"*) result="CCTV5+" ;; *"CCTV6电影"*|*"CCTV-6 电影"*|*"CCTV6超清"*|*"CCTV6HD"*|*"CCTV6高清"*|*"CCTV-6高清"*|*"CCTV-6咪咕"*) result="CCTV6" ;; *"CCTV7国防军事"*|*"CCTV-7 国防军事"*|*"CCTV7超清"*|*"CCTV7HD"*|*"CCTV7高清"*|*"CCTV-7高清"*|*"CCTV-7咪咕"*) result="CCTV7" ;; *"CCTV8电视剧"*|*"CCTV-8 电视剧"*|*"CCTV8超清"*|*"CCTV8HD"*|*"CCTV8高清"*|*"CCTV-8高清"*|*"CCTV-8咪咕"*) result="CCTV8" ;; *"CCTV9纪录"*|*"CCTV-9 纪录"*|*"CCTV9超清"*|*"CCTV9HD"*|*"CCTV9高清"*|*"CCTV-9高清"*|*"CCTV-9咪咕"*) result="CCTV9" ;; *"CCTV10科教"*|*"CCTV-10 科教"*|*"CCTV10超清"*|*"CCTV10HD"*|*"CCTV10高清"*|*"CCTV-10高清"*|*"CCTV-10咪咕"*) result="CCTV10" ;; *"CCTV11戏曲"*|*"CCTV-11 戏曲"*|*"CCTV11超清"*|*"CCTV11HD"*|*"CCTV11高清"*|*"CCTV-11高清"*|*"CCTV-11咪咕"*) result="CCTV11" ;; *"CCTV12社会与法"*|*"CCTV-12 社会与法"*|*"CCTV12超清"*|*"CCTV12HD"*|*"CCTV12高清"*|*"CCTV-12高清"*|*"CCTV-12咪咕"*) result="CCTV12" ;; *"CCTV13新闻"*|*"CCTV-13 新闻"*|*"CCTV13超清"*|*"CCTV13HD"*|*"CCTV13高清"*|*"CCTV-13高清"*|*"CCTV-13咪咕"*) result="CCTV13" ;; *"CCTV14少儿"*|*"CCTV-14 少儿"*|*"CCTV14超清"*|*"CCTV14HD"*|*"CCTV14高清"*|*"CCTV-14高清"*|*"CCTV-14咪咕"*) result="CCTV14" ;; *"CCTV15音乐"*|*"CCTV-15 音乐"*|*"CCTV15超清"*|*"CCTV15HD"*|*"CCTV15高清"*|*"CCTV-15高清"*|*"CCTV-15咪咕"*) result="CCTV15" ;; *"CCTV16奥林匹克"*|*"CCTV-16 奥林匹克"*|*"CCTV16超清"*|*"CCTV16HD"*|*"CCTV16高清"*|*"CCTV-16高清"*|*"CCTV-16咪咕"*) result="CCTV16" ;; *"CCTV17农业农村"*|*"CCTV-17 农业农村"*|*"CCTV17超清"*|*"CCTV17HD"*|*"CCTV17高清"*|*"CCTV-17高清"*|*"CCTV-17咪咕"*) result="CCTV17" ;; *"综合,http"*|*"财经,http"*|*"综艺,http"*|*"中文国际,http"*|*"体育,http"*|*"体育赛事,http"*|*"电影,http"*|*"国防军事,http"*|*"电视剧,http"*|*"纪录,http"*|*"科教,http"*|*"戏曲,http"*|*"社会与法,http"*|*"新闻,http"*|*"少儿,http"*|*"音乐,http"*|*"奥林匹克,http"*|*"农业农村,http"*|*" 综合,http"*|*" 财经,http"*|*" 综艺,http"*|*" 中文国际,http"*|*" 体育,http"*|*" 体育赛事,http"*|*" 电影,http"*|*" 国防军事,http"*|*" 电视剧,http"*|*" 纪录,http"*|*" 科教,http"*|*" 戏曲,http"*|*" 社会与法,http"*|*" 新闻,http"*|*" 少儿,http"*|*" 音乐,http"*|*" 奥林匹克,http"*|*" 农业农村,http"*|*" 4Gtv,http"*|*" 欣赏,http"*|*" Pdtv,http"*) result=",http" ;; *"湖南卫视超清"*|*"湖南卫视高清"*) result="湖南卫视" ;; *"江苏卫视超清"*|*"江苏卫视高清"*) result="江苏卫视" ;; *"广东卫视超清"*|*"广东卫视高清"*) result="广东卫视" ;; *"东方卫视超清"*|*"东方卫视高清"*) result="东方卫视" ;; *"辽宁卫视超清"*|*"辽宁卫视高清"*) result="辽宁卫视" ;; *"东南卫视超清"*|*"东南卫视高清"*) result="东南卫视" ;; *"湖北卫视超清"*|*"湖北卫视高清"*) result="湖北卫视" ;; *"江西卫视超清"*|*"江西卫视高清"*) result="江西卫视" ;; *"北京卫视超清"*|*"北京卫视高清"*) result="北京卫视" ;; *) result="$channel" ;; esac # 纯 bash 参数展开替代 sed,零子进程 result="${result//CCTV-/CCTV}" printf '%s\n' "$result" } collect_develop202() { echo "[步骤1] 收集 iTCoffe、adminouyang 和 qingtingjjjjjjj 源..." # 源列表:可在此数组中添加更多源 local sources=( "https://gh-proxy.com/https://raw.githubusercontent.com/iTCoffe/Collect-iTV/refs/heads/main/KuTV.txt" "https://gh-proxy.com/https://raw.githubusercontent.com/adminouyang/231006/main/py/%E4%BC%98%E8%B4%A8%E6%BA%90/output/ipv4/result.txt" "https://gh-proxy.com/https://raw.githubusercontent.com/qingtingjjjjjjj/iptv-auto-update/main/my.txt" ) local merged_file=$(mktemp) for url in "${sources[@]}"; do local tmp_file=$(mktemp) local tmp_normalized=$(mktemp) echo "正在下载: $url" if curl -sL --connect-timeout 30 "$url" -o "$tmp_file"; then while IFS= read -r line; do if [[ "$line" == *","* ]]; then local name="${line%%,*}" local url_part="${line#*,}" local norm_name norm_name=$(replace_channel_name "$name") printf '%s,%s\n' "$norm_name" "$url_part" >> "$tmp_normalized" else printf '%s\n' "$line" >> "$tmp_normalized" fi done < <(grep -E "http://" "$tmp_file") cat "$tmp_normalized" >> "$merged_file" else echo "源下载失败: $url" fi rm -f "$tmp_file" "$tmp_normalized" done # 去重并保存 sort -u "$merged_file" > "$IPTV_DIR/develop202.txt" echo "develop202 + cqshushu: 保存 $(wc -l < "$IPTV_DIR/develop202.txt") 条" rm -f "$merged_file" } collect_kakaxi() { echo "[步骤2] 收集 kakaxi 源..." local urls=( "https://kakaxi.indevs.in/iptv.txt" "https://gh-proxy.com/https://raw.githubusercontent.com/kakaxi-1/IPTV/refs/heads/main/iptv.txt" ) local tmp_file=$(mktemp) local success=0 for url in "${urls[@]}"; do echo " 尝试: $url" if curl -sL --connect-timeout 30 "$url" -o "$tmp_file"; then echo " 下载成功" success=1 break fi echo " 下载失败,尝试下一个..." done if [ $success -eq 1 ]; then local tmp_normalized=$(mktemp) while IFS= read -r line; do if [[ "$line" == *","* ]]; then local name="${line%%,*}" local url_part="${line#*,}" local norm_name norm_name=$(replace_channel_name "$name") printf '%s,%s\n' "$norm_name" "$url_part" >> "$tmp_normalized" else printf '%s\n' "$line" >> "$tmp_normalized" fi done < <(grep -E "联通" "$tmp_file" | sed 's/\$.*$//') cp "$tmp_normalized" "$IPTV_DIR/kakaxi.txt" rm -f "$tmp_normalized" echo "kakaxi: 保存 $(wc -l < "$IPTV_DIR/kakaxi.txt") 条" else echo "kakaxi 源所有地址下载失败" fi rm -f "$tmp_file" } collect_github() { echo "[步骤3] 处理 GitHub 源..." local year_str=$(date +%Y%m%d) local query=$(printf '%s' "timestamp=$year_str" | jq -sRr @uri) local search_url="https://api.github.com/search/code?q=$query" local response=$(curl -sL -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" "$search_url") local file_urls=$(echo "$response" | jq -r '.items[].html_url' 2>/dev/null) local count=$(echo "$file_urls" | grep -c .) echo " 找到 $count 个文件" if [ -z "$file_urls" ]; then echo " 未找到GitHub文件" return fi local all_lines=$(mktemp) local file_count=0 while IFS= read -r url; do [ -z "$url" ] && continue [ $file_count -ge 10 ] && break local raw_url=$(echo "$url" | sed 's|github.com|raw.githubusercontent.com|; s|/blob/|/|') local proxied_url="https://gh-proxy.com/$raw_url" echo " 下载: $proxied_url" curl -sL --connect-timeout 30 "$proxied_url" >> "$all_lines" file_count=$((file_count + 1)) done <<< "$file_urls" echo " 共取得 $(wc -l < "$all_lines") 行" # 分离含 timestamp 的行和不含 timestamp 的行(咪咕源等) grep "timestamp=" "$all_lines" > "${all_lines}_ts" echo " 含timestamp的行: $(wc -l < "${all_lines}_ts")" grep -v "timestamp=" "$all_lines" > "${all_lines}_nots" echo " 不含timestamp的行: $(wc -l < "${all_lines}_nots")" local now_ts=$(date +%s) declare -A entries declare -A hours_diff local valid_count=0 # 处理含 timestamp 的行(用 bash 内置正则替代 grep -oP,零子进程) while IFS= read -r line; do if [[ "$line" =~ timestamp=([0-9]+) ]]; then ts_match="${BASH_REMATCH[1]}" else continue fi local name="${line%%,*}" [[ "$name" == *"#genre#"* ]] && continue [[ "$name" =~ ^[0-9]+$ ]] && continue [[ "$name" == *"http://"* || "$name" == *"https://"* ]] && continue [ -z "$name" ] && continue local ts_len=${#ts_match} if [ "$ts_len" -eq 14 ] && [[ "$ts_match" =~ ^[0-9]{14}$ ]]; then local year=${ts_match:0:4} local month=${ts_match:4:2} local day=${ts_match:6:2} local hour=${ts_match:8:2} local min=${ts_match:10:2} local sec=${ts_match:12:2} if [[ "$year" =~ ^[0-9]{4}$ && "$month" =~ ^(0[1-9]|1[0-2])$ && "$day" =~ ^(0[1-9]|[12][0-9]|3[01])$ ]]; then local line_ts=$(date -d "$year-$month-$day $hour:$min:$sec UTC" +%s 2>/dev/null) else continue fi else if ! [[ "$ts_match" =~ ^[0-9]+$ ]]; then continue fi if [ "$ts_match" -lt 10000000000 ]; then local line_ts=$ts_match else local line_ts=$((ts_match / 1000)) fi fi [ -z "$line_ts" ] && continue local diff=$(( (now_ts - line_ts) / 3600 )) if [ "$diff" -gt 24 ]; then continue fi if [ -z "${entries[$name]}" ] || [ $diff -lt ${hours_diff[$name]} ]; then entries[$name]="$line" hours_diff[$name]=$diff valid_count=$((valid_count + 1)) fi done < "${all_lines}_ts" # 处理不含 timestamp 的行(咪咕视频源等),直接保留 local nots_count=0 while IFS= read -r line; do [ -z "$line" ] && continue local name="${line%%,*}" local url_part="${line#*,}" [[ "$name" == *"#genre#"* ]] && continue [[ "$name" =~ ^[0-9]+$ ]] && continue [[ "$name" == *"http://"* || "$name" == *"https://"* ]] && continue [ -z "$name" ] && continue [ -z "$url_part" ] && continue if [ -z "${entries[$name]}" ]; then entries[$name]="$line" hours_diff[$name]=99999 nots_count=$((nots_count + 1)) fi done < "${all_lines}_nots" echo " 有效条目: ${#entries[@]} (其中无timestamp: $nots_count)" local normalized=$(mktemp) for name in "${!entries[@]}"; do local line="${entries[$name]}" local url_part="${line#*,}" local norm_name norm_name=$(replace_channel_name "$name") printf '%s,%s\n' "$norm_name" "$url_part" >> "$normalized" done cp "$normalized" "$IPTV_DIR/github_tv.txt" echo "GitHub: 保存 $(wc -l < "$IPTV_DIR/github_tv.txt") 条" rm -f "$all_lines" "${all_lines}_ts" "${all_lines}_nots" "$normalized" } cctv_sort_key() { local line="$1" local name="${line%%,*}" local url="${line#*,}" local mgv_flag=1 if [[ "$url" == *"miguvideo"* ]]; then mgv_flag=0 fi if [[ "$name" =~ CCTV([0-9]+)(\+|Plus)? ]]; then local num=${BASH_REMATCH[1]} local suffix=${BASH_REMATCH[2]} if [ -n "$suffix" ]; then printf "0_%05d_1_%d" "$num" "$mgv_flag" else printf "0_%05d_0_%d" "$num" "$mgv_flag" fi else printf "1_%s_%d" "$name" "$mgv_flag" fi } satellite_sort_key() { local line="$1" local name="${line%%,*}" local url="${line#*,}" local order=("湖南卫视" "浙江卫视" "东方卫视" "江苏卫视" "北京卫视" "广东卫视" "深圳卫视") local mgv_flag=1 if [[ "$url" == *"miguvideo"* ]]; then mgv_flag=0 fi for i in "${!order[@]}"; do if [ "$name" == "${order[$i]}" ]; then printf "0_%02d_%d" "$i" "$mgv_flag" return fi done printf "1_%s_%d" "$name" "$mgv_flag" } classify_and_sort() { echo "[步骤4-7] 分类排序并生成 tv.txt..." local cctv_github=$(mktemp) local cctv_caliph=$(mktemp) local cctv_kakaxi=$(mktemp) local sat_github=$(mktemp) local sat_caliph=$(mktemp) local sat_kakaxi=$(mktemp) local dig_github=$(mktemp) local dig_caliph=$(mktemp) local dig_kakaxi=$(mktemp) local digital_whitelist=( "CHC动作电影" "CHC家庭影院" "CHC影迷电影" "兵器科技" "纪实科教" "央视台球" "风云足球" "高尔夫网球" "风云音乐" "风云剧场" "四海钓鱼" "快乐垂钓" "环球旅游" "财富天下" "金鹰卡通" "金鹰纪实" "金牌综艺" "黑莓动画" "黑莓电影" "世界地理" "发现之旅" "嘉佳卡通" "卡酷少儿" "天元围棋" "央视文化精品" "女性时尚" "茶频道" ) for src in "github_tv.txt" "develop202.txt" "kakaxi.txt"; do local file="$IPTV_DIR/$src" [ ! -f "$file" ] && continue local cctv_tmp sat_tmp dig_tmp case "$src" in "github_tv.txt") cctv_tmp="$cctv_github"; sat_tmp="$sat_github"; dig_tmp="$dig_github" ;; "develop202.txt") cctv_tmp="$cctv_caliph"; sat_tmp="$sat_caliph"; dig_tmp="$dig_caliph" ;; "kakaxi.txt") cctv_tmp="$cctv_kakaxi"; sat_tmp="$sat_kakaxi"; dig_tmp="$dig_kakaxi" ;; esac while IFS= read -r line; do [ -z "$line" ] && continue local name="${line%%,*}" if [[ "$line" =~ CCTV([^,]{1,2}),http ]]; then printf '%s\n' "$line" >> "$cctv_tmp" elif [[ "$name" == *"卫视"* ]]; then printf '%s\n' "$line" >> "$sat_tmp" else for ch in "${digital_whitelist[@]}"; do if [[ "$name" == *"$ch"* ]]; then printf '%s\n' "$line" >> "$dig_tmp" break fi done fi done < "$file" done sort_cctv() { local file="$1" while IFS= read -r line; do printf '%s|%s\n' "$(cctv_sort_key "$line")" "$line" done < "$file" | sort | cut -d'|' -f2- } sort_satellite() { local file="$1" while IFS= read -r line; do printf '%s|%s\n' "$(satellite_sort_key "$line")" "$line" done < "$file" | sort | cut -d'|' -f2- } sort_digital() { while IFS= read -r line; do local url="${line#*,}" local mgv_flag=1 if [[ "$url" == *"miguvideo"* ]]; then mgv_flag=0 fi printf '%s,%s\n' "$mgv_flag" "$line" done < "$1" | sort -t',' -k1,1 -k2,2 | cut -d',' -f2- } local cctv_sorted=$(mktemp) local sat_sorted=$(mktemp) local dig_sorted=$(mktemp) { sort_cctv "$cctv_github" sort_cctv "$cctv_caliph" sort_cctv "$cctv_kakaxi" } > "$cctv_sorted" { sort_satellite "$sat_github" sort_satellite "$sat_caliph" sort_satellite "$sat_kakaxi" } > "$sat_sorted" { sort_digital "$dig_github" sort_digital "$dig_caliph" sort_digital "$dig_kakaxi" } > "$dig_sorted" local cctv_count=$(wc -l < "$cctv_sorted") local sat_count=$(wc -l < "$sat_sorted") local dig_count=$(wc -l < "$dig_sorted") local combined=$(mktemp) { printf '🇨🇳央视频道🇨🇳,#genre#\n' cat "$cctv_sorted" printf '\n🛰️卫视频道🛰️,#genre#\n' cat "$sat_sorted" printf '\n🥝数字频道🥝,#genre#\n' cat "$dig_sorted" } > "$combined" # 删除不需要的行(欧洲、美洲、教育、CETV、CGTN、以及包含时间格式的行) grep -v -E "更新日期|CCTV,http|URL: http|欧洲|美洲|教育|CCTV4K|CETV|CGTN|[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}" "$combined" > "${IPTV_DIR}/tv_filtered.txt" # 去重(基于整行内容) awk '!seen[$0]++' "${IPTV_DIR}/tv_filtered.txt" > "${IPTV_DIR}/tv_deduped.txt" # 并发连通性测试,只保留有视频流的行(xargs -P 64 真流水线并发) echo "[步骤7.5] 并发连通性测试(64路并行)..." local tv_checked=$(mktemp) local channels_file=$(mktemp) local test_results=$(mktemp) local test_err=$(mktemp) local cctv_pass=$(mktemp) sat_pass=$(mktemp) dig_pass=$(mktemp) local cctv_skip=$(mktemp) sat_skip=$(mktemp) dig_skip=$(mktemp) grep -v ",#genre#" "${IPTV_DIR}/tv_deduped.txt" | grep "," > "$channels_file" local total_channels=$(wc -l < "$channels_file") echo " 待测试频道: $total_channels 个" # 将单频道测试逻辑写入临时脚本,避免依赖 export -f(HF 等环境可能剥离导出函数) local test_script=$(mktemp) cat > "$test_script" <<'TESTEOF' #!/bin/bash line="$1" [ -z "$line" ] && exit name="${line%%,*}" url="${line#*,}" [ -z "$url" ] && exit if [[ "$name" =~ ^CCTV ]]; then grp="cctv" elif [[ "$name" == *"卫视"* ]]; then grp="sat" else grp="dig"; fi # 咪咕源跳过连通性测试,直接保留 if [[ "$url" == *"miguvideo"* ]]; then printf 'SKIP\t%s\t%s\n' "$grp" "$line" exit fi resp=$(curl -sL --connect-timeout 3 --max-time 8 --range 0-1023 \ -o /dev/null -w '%{http_code} %{content_type}' "$url" 2>/dev/null) hc="${resp%% *}" ct="${resp#* }" if [[ "$hc" == "200" || "$hc" == "206" ]]; then if [[ -z "$ct" || "$ct" == *"video"* || "$ct" == *"mpegurl"* \ || "$ct" == *"application/octet-stream"* || "$ct" == *"application/vnd.apple.mpegurl"* ]]; then printf 'PASS\t%s\t%s\n' "$grp" "$line" fi fi TESTEOF # xargs -0 流水线并发:tr 将换行转 NUL,-0 按 NUL 分割(GNU/BusyBox 通用),-P 64 并行 tr '\n' '\0' < "$channels_file" \ | xargs -0 -P 64 -I {} bash "$test_script" {} > "$test_results" 2>"$test_err" # 若结果为空,打印错误日志前几行以便排查 if [ ! -s "$test_results" ]; then echo " ⚠️ 测试结果为空,错误信息(前10行):" head -n 10 "$test_err" 2>/dev/null | sed 's/^/ /' fi rm -f "$test_script" "$test_err" # 按状态+分类拆分结果(用 tab 分隔,避免频道名含空格被误切) awk -F'\t' '$1=="PASS"&&$2=="cctv"{sub(/^PASS\tcctv\t/,"");print}' "$test_results" > "$cctv_pass" awk -F'\t' '$1=="PASS"&&$2=="sat"{sub(/^PASS\tsat\t/,"");print}' "$test_results" > "$sat_pass" awk -F'\t' '$1=="PASS"&&$2=="dig"{sub(/^PASS\tdig\t/,"");print}' "$test_results" > "$dig_pass" awk -F'\t' '$1=="SKIP"&&$2=="cctv"{sub(/^SKIP\tcctv\t/,"");print}' "$test_results" > "$cctv_skip" awk -F'\t' '$1=="SKIP"&&$2=="sat"{sub(/^SKIP\tsat\t/,"");print}' "$test_results" > "$sat_skip" awk -F'\t' '$1=="SKIP"&&$2=="dig"{sub(/^SKIP\tdig\t/,"");print}' "$test_results" > "$dig_skip" local cctv_mgv=$(wc -l < "$cctv_skip") local sat_mgv=$(wc -l < "$sat_skip") local dig_mgv=$(wc -l < "$dig_skip") # 合并测试通过和跳过测试(咪咕)的频道 cat "$cctv_skip" >> "$cctv_pass" cat "$sat_skip" >> "$sat_pass" cat "$dig_skip" >> "$dig_pass" local cctv_count=$(wc -l < "$cctv_pass") local sat_count=$(wc -l < "$sat_pass") local dig_count=$(wc -l < "$dig_pass") local checked_channels=$((cctv_count + sat_count + dig_count)) echo " 通过测试: $checked_channels 个 (央视:$cctv_count[含咪咕$cctv_mgv], 卫视:$sat_count[含咪咕$sat_mgv], 数字:$dig_count[含咪咕$dig_mgv])" { printf '🇨🇳央视频道🇨🇳,#genre#\n' cat "$cctv_pass" printf '\n🛰️卫视频道🛰️,#genre#\n' cat "$sat_pass" printf '\n🥝数字频道🥝,#genre#\n' cat "$dig_pass" } > "$tv_checked" mv "$tv_checked" "${IPTV_DIR}/tv_deduped.txt" rm -f "$channels_file" "$test_results" \ "$cctv_pass" "$sat_pass" "$dig_pass" \ "$cctv_skip" "$sat_skip" "$dig_skip" echo "[步骤7.6] 重新排序(生成标准电视节目排序)..." local sorted_result=$(mktemp) local temp_cctv=$(mktemp) local temp_sat=$(mktemp) local temp_dig=$(mktemp) while IFS= read -r line; do [ -z "$line" ] && continue [[ "$line" == *",#genre#" ]] && continue local name="${line%%,*}" if [[ "$name" =~ ^CCTV ]]; then printf '%s\n' "$line" >> "$temp_cctv" elif [[ "$name" == *"卫视"* ]]; then printf '%s\n' "$line" >> "$temp_sat" else printf '%s\n' "$line" >> "$temp_dig" fi done < "${IPTV_DIR}/tv_deduped.txt" { printf '🇨🇳央视频道🇨🇳,#genre#\n' sort_cctv "$temp_cctv" printf '\n🛰️卫视频道🛰️,#genre#\n' sort_satellite "$temp_sat" printf '\n🥝数字频道🥝,#genre#\n' sort_digital "$temp_dig" } > "$sorted_result" mv "$sorted_result" "${IPTV_DIR}/tv_deduped.txt" rm -f "$temp_cctv" "$temp_sat" "$temp_dig" # 追加tv2.txt内容(如果存在) if [ -f "$IPTV_DIR/tv2.txt" ]; then cat "$IPTV_DIR/tv2.txt" >> "${IPTV_DIR}/tv_deduped.txt" echo "tv2.txt 内容已追加" fi # 最终去重并保存 awk '!seen[$0]++' "${IPTV_DIR}/tv_deduped.txt" > "$IPTV_DIR/tv.txt" chmod o+r "$IPTV_DIR/tv.txt" # 步骤8:转换为m3u格式 echo "[步骤8] 转换为m3u格式..." local m3u_file="$IPTV_DIR/tv.m3u" # 添加x-tvg-url printf '#EXTM3U x-tvg-url="https://epg.catvod.com/epg.xml"\n' > "$m3u_file" local current_genre="" while IFS= read -r line; do if [[ "$line" == *",#genre#" ]]; then # 分类标签 current_genre="${line%%,*}" # 清理分类名称,去掉emoji(仅3行,sed 开销可忽略) current_genre=$(echo "$current_genre" | sed 's/[🇨🇳🛰️🥝]//g') elif [[ "$line" == *","* ]] && [[ "$line" != *"#genre#" ]]; then # 频道行 local name="${line%%,*}" local url="${line#*,}" # 生成tvg-logo URL local logo_url="https://github.com/fanmingming/live/tree/main/tv${name}.png" # 写入m3u格式 printf '#EXTINF:-1 group-title="%s" tvg-logo="%s",%s\n' "$current_genre" "$logo_url" "$name" >> "$m3u_file" printf '%s\n' "$url" >> "$m3u_file" fi done < "$IPTV_DIR/tv.txt" # 清理临时文件 rm -f "$combined" "${IPTV_DIR}/tv_filtered.txt" "${IPTV_DIR}/tv_deduped.txt" echo "✅ 生成完成:tv.txt tv.m3u" echo " 央视频道: $cctv_count 条" echo " 卫视频道: $sat_count 条" echo " 数字频道: $dig_count 条" rm -f "$cctv_github" "$cctv_caliph" "$cctv_kakaxi" \ "$sat_github" "$sat_caliph" "$sat_kakaxi" \ "$dig_github" "$dig_caliph" "$dig_kakaxi" \ "$cctv_sorted" "$sat_sorted" "$dig_sorted" } main() { echo "============================================================" echo "IPTV 频道收集与处理脚本" echo "============================================================" collect_develop202 collect_kakaxi collect_github classify_and_sort echo "" echo "[清理] 删除中间文件..." rm -f "$IPTV_DIR/develop202.txt" "$IPTV_DIR/kakaxi.txt" "$IPTV_DIR/github_tv.txt" echo "============================================================" echo "🎉 全部完成!" echo "============================================================" } main