tv / iptv.sh
PXHero's picture
Update iptv.sh
c18a390 verified
Raw
History Blame Contribute Delete
25.1 kB
#!/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