| #!/bin/bash |
|
|
| |
| 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 |
| |
| 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") 行" |
|
|
| |
| 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 |
| |
| 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" |
|
|
| |
| 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" |
|
|
| |
| 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" |
|
|
| |
| 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 个" |
|
|
| |
| local test_script=$(mktemp) |
| cat > "$test_script" <<'TESTEOF' |
| |
| 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 |
|
|
| |
| 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" |
|
|
| |
| 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" |
|
|
| |
| 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" |
|
|
| |
| echo "[步骤8] 转换为m3u格式..." |
| local m3u_file="$IPTV_DIR/tv.m3u" |
| |
| 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%%,*}" |
| |
| current_genre=$(echo "$current_genre" | sed 's/[🇨🇳🛰️🥝]//g') |
| elif [[ "$line" == *","* ]] && [[ "$line" != *"#genre#" ]]; then |
| |
| local name="${line%%,*}" |
| local url="${line#*,}" |
|
|
| |
| local logo_url="https://github.com/fanmingming/live/tree/main/tv${name}.png" |
|
|
| |
| 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 |
|
|