Initial commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/downloaded
|
||||||
|
playlists.ini
|
||||||
|
*.m3u
|
||||||
|
*.m3u8
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Антон Аксенов (Anthony Axenov)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
80
README.md
Normal file
80
README.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Инструменты для работы с IPTV-плейлистами
|
||||||
|
|
||||||
|
Проект, содержащий в себе скрипты для работы с IPTV-плейлистами.
|
||||||
|
|
||||||
|
Для начала работы необходимо скачать файл [playlists.ini](https://git.axenov.dev/IPTV/playlists/raw/branch/master/playlists.ini).
|
||||||
|
|
||||||
|
- [Инструменты для работы с IPTV-плейлистами](#инструменты-для-работы-с-iptv-плейлистами)
|
||||||
|
- [Скачать все плейлисты](#скачать-все-плейлисты)
|
||||||
|
- [Проверить каналы плейлиста](#проверить-каналы-плейлиста)
|
||||||
|
- [Поиск каналов в одном плейлисте](#поиск-каналов-в-одном-плейлисте)
|
||||||
|
- [Поиск каналов во всех плейлистах](#поиск-каналов-во-всех-плейлистах)
|
||||||
|
- [Создать плейлист из нужных каналов](#создать-плейлист-из-нужных-каналов)
|
||||||
|
- [Как создать свой собственный плейлист?](#как-создать-свой-собственный-плейлист)
|
||||||
|
- [Лицензия](#лицензия)
|
||||||
|
|
||||||
|
## Скачать все плейлисты
|
||||||
|
|
||||||
|
Команда: `./download-all.sh`
|
||||||
|
|
||||||
|
Скачивает все плейлисты из `playlists.ini` в локальную директорию `./downloaded/`.
|
||||||
|
|
||||||
|
## Проверить каналы плейлиста
|
||||||
|
|
||||||
|
Команда: `./check-pls.sh`
|
||||||
|
|
||||||
|
Проверяет каждый канал в плейлисте на доступность и выводит результат проверки.
|
||||||
|
|
||||||
|
Поддерживаются `*.m3u` и `*.m3u8`; как локальные файлы, так по прямым ссылкам.
|
||||||
|
|
||||||
|
Коды ошибок доступны [здесь](https://everything.curl.dev/usingcurl/returns).
|
||||||
|
|
||||||
|
## Поиск каналов в одном плейлисте
|
||||||
|
|
||||||
|
Команда: `./find-in-pls.sh`
|
||||||
|
|
||||||
|
Находит каналы по заданному регулярному выражению в одном указанном плейлисте.
|
||||||
|
|
||||||
|
Поддерживаются `*.m3u` и `*.m3u8`; как локальные файлы, так по прямым ссылкам.
|
||||||
|
|
||||||
|
## Поиск каналов во всех плейлистах
|
||||||
|
|
||||||
|
Команда: `./find-in-all.sh`
|
||||||
|
|
||||||
|
Находит каналы по заданному регулярному выражению во всех плейлистах, скачанных через `download-all.sh`.
|
||||||
|
|
||||||
|
## Создать плейлист из нужных каналов
|
||||||
|
|
||||||
|
Команда: `./make-pls.sh`
|
||||||
|
|
||||||
|
Находит каналы по заданному регулярному выражению во всех плейлистах, скачанных через `download-all.sh`.
|
||||||
|
|
||||||
|
Отличается от `find-in-all.sh` тем, что тот выводит результат в человекочитаемом формате, а этот -- в готовом m3u
|
||||||
|
формате для сохранения в файл.
|
||||||
|
|
||||||
|
Для сохранения в файл следует добавить `> myfile.m3u` или `>> myfile.m3u` в конец команды.
|
||||||
|
|
||||||
|
## Как создать свой собственный плейлист?
|
||||||
|
|
||||||
|
1. Скачать все плейлисты, указанные в `playlists.ini`:
|
||||||
|
```
|
||||||
|
$ ./download-all.sh
|
||||||
|
```
|
||||||
|
2. Вытащить из них нужные каналы и сохранить в отдельный файл:
|
||||||
|
```
|
||||||
|
$ ./make-pls.sh "(fox|disney)" > my.m3u8
|
||||||
|
```
|
||||||
|
Так в плейлисте `./my.m3u8` окажутся все каналы из скачанных плейлистов, в названиях которых встретились `fox`
|
||||||
|
или `disney`.
|
||||||
|
3. Проверить доступность каналов в полученном плейлисте:
|
||||||
|
```
|
||||||
|
$ ./check-pls.sh my.m3u8
|
||||||
|
```
|
||||||
|
> Результат `ОК` не означает, что канал действительно работает и отдаёт видео/аудио потоки.
|
||||||
|
> Результат `ERROR` с любыми кодами ошибок гарантированно означает, что канал не работает.
|
||||||
|
4. Вручную: удалить нерабочие, мусорные и продублировавшиеся (по ссылкам) каналы.
|
||||||
|
5. Вручную: добавить плейлист в IPTV-плеер и перепроверить результат.
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
[The MIT License](LICENSE)
|
||||||
77
check-pls.sh
Executable file
77
check-pls.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
#
|
||||||
|
# IPTV Playlist check tool
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./check-pls.sh local/pls.m3u
|
||||||
|
# ./check-pls.sh https://example.com/pls.m3u
|
||||||
|
#
|
||||||
|
# 1st argument is playlist file name or URL.
|
||||||
|
# If it is an URL it will be saved in /tmp and
|
||||||
|
# checked as local file.
|
||||||
|
#
|
||||||
|
# Both *.m3u and *.m3u8 are supported.
|
||||||
|
#
|
||||||
|
# Anthony Axenov (c) 2022
|
||||||
|
# The MIT License:
|
||||||
|
# https://git.axenov.dev/IPTV/tools/src/branch/master/LICENSE
|
||||||
|
#
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
awk '
|
||||||
|
BEGIN {
|
||||||
|
total_count=0
|
||||||
|
success_count=0
|
||||||
|
fail_count=0
|
||||||
|
print "\033[20m\033[97mPlaylist:\033[0m " ARGV[1]
|
||||||
|
if (ARGV[1] ~ /^http(s)?:\/\/.*/) {
|
||||||
|
parts_count = split(ARGV[1], parts, "/")
|
||||||
|
file_name = parts[parts_count]
|
||||||
|
code = system("wget " ARGV[1] " -qO /tmp/" file_name " > /dev/null")
|
||||||
|
if (code == 0) {
|
||||||
|
print "Saved in /tmp/" file_name
|
||||||
|
} else {
|
||||||
|
print "ERROR: cannot download playlist: " ARGV[1]
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
ARGV[1] = "/tmp/" file_name
|
||||||
|
}
|
||||||
|
print ""
|
||||||
|
print "\033[20m\033[97mNote 1:\033[0m operation may take some time, press CTRL+C to stop."
|
||||||
|
print "\033[20m\033[97mNote 2:\033[0m results may be inaccurate, you should use proper IPTV software to re-check."
|
||||||
|
print "\033[20m\033[97mNote 3:\033[0m error codes listed here - https://everything.curl.dev/usingcurl/returns"
|
||||||
|
print "--------------------"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
sub("\r$", "", $0) # crlf -> lf
|
||||||
|
if ($0 ~ /^#EXTINF:.+,/) {
|
||||||
|
total_count++
|
||||||
|
channel_name = substr($0, index($0, ",") + 1, length($0))
|
||||||
|
print "\n[" total_count "] " channel_name
|
||||||
|
}
|
||||||
|
if ($0 ~ /^http(s)?:\/\/.*/) {
|
||||||
|
url = sprintf("%c%s%c", 34, $0, 34) # 34 is "
|
||||||
|
cmd = "curl -fs --max-time 5 -w \"%{http_code}\" --max-filesize 5000 -o /dev/null " url
|
||||||
|
cmd | getline http_code
|
||||||
|
code = close(cmd)
|
||||||
|
if (http_code == "000") {
|
||||||
|
http_code = "-"
|
||||||
|
}
|
||||||
|
if (code == 0 || code == 63) {
|
||||||
|
print "\033[32mOK:\033[0m " url
|
||||||
|
success_count++
|
||||||
|
} else {
|
||||||
|
print "\033[91mERROR\033[0m " code " (" http_code "): " url
|
||||||
|
fail_count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print "--------------------"
|
||||||
|
print "\033[20m\033[97mPlaylist:\033[0m " ARGV[1]
|
||||||
|
print "- Success:\t\033[32m" success_count "\033[0m/" total_count
|
||||||
|
print "- Failed: \t\033[91m" fail_count "\033[0m/" total_count
|
||||||
|
}
|
||||||
|
' $1
|
||||||
31
download-all.sh
Executable file
31
download-all.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
#
|
||||||
|
# IPTV Playlist download tool
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./download-all.sh
|
||||||
|
#
|
||||||
|
# All playlists from playlists.ini will be
|
||||||
|
# downloaded in ./downloaded directory
|
||||||
|
#
|
||||||
|
# Anthony Axenov (c) 2022
|
||||||
|
# The MIT License:
|
||||||
|
# https://git.axenov.dev/IPTV/tools/src/branch/master/LICENSE
|
||||||
|
#
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
TOOLS_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
DL_DIR="$TOOLS_DIR/downloaded"
|
||||||
|
INI_FILE="$(dirname "$TOOLS_DIR")/playlists.ini"
|
||||||
|
|
||||||
|
rm -rf "$DL_DIR" && \
|
||||||
|
mkdir -p "$DL_DIR" && \
|
||||||
|
cd "$DL_DIR" && \
|
||||||
|
cat "$INI_FILE" \
|
||||||
|
| grep -P "pls\s*=\s*'(.*)'" \
|
||||||
|
| sed "s#^pls\s*=\s*##g" \
|
||||||
|
| sed "s#'##g" \
|
||||||
|
| tr -d '\r' \
|
||||||
|
| xargs wget
|
||||||
25
find-in-all.sh
Executable file
25
find-in-all.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
#
|
||||||
|
# IPTV channel finder (all playlists)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./download-all.sh
|
||||||
|
# ./find-in-all.sh "(disney|atv)"
|
||||||
|
#
|
||||||
|
# 1st argument is channel name pattern.
|
||||||
|
#
|
||||||
|
# Anthony Axenov (c) 2022
|
||||||
|
# The MIT License:
|
||||||
|
# https://git.axenov.dev/IPTV/tools/src/branch/master/LICENSE
|
||||||
|
#
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
TOOLS_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
DL_DIR="$TOOLS_DIR/downloaded"
|
||||||
|
[ ! -d "$DL_DIR" ] && echo "Error: '$DL_DIR' directory does not exist. Run ./download-all.sh" && exit 1
|
||||||
|
[ ! "$(ls -A "$DL_DIR")" ] && echo "Error: '$DL_DIR' directory is empty. Run ./download-all.sh" && exit 2
|
||||||
|
for file in $TOOLS_DIR/downloaded/*; do
|
||||||
|
$TOOLS_DIR/find-in-pls.sh "$1" "$file"
|
||||||
|
done
|
||||||
84
find-in-pls.sh
Executable file
84
find-in-pls.sh
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
#
|
||||||
|
# IPTV channel finder (one playlist)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./find-in-pls.sh "disney" local/pls.m3u
|
||||||
|
# ./find-in-pls.sh "disney" https://example.com/pls.m3u
|
||||||
|
#
|
||||||
|
# 1st argument is channel name pattern.
|
||||||
|
#
|
||||||
|
# 2nd argument is playlist file name or URL.
|
||||||
|
# If it is an URL it will be saved in /tmp and
|
||||||
|
# checked as local file.
|
||||||
|
#
|
||||||
|
# Both *.m3u and *.m3u8 are supported.
|
||||||
|
#
|
||||||
|
# Anthony Axenov (c) 2022
|
||||||
|
# The MIT License:
|
||||||
|
# https://git.axenov.dev/IPTV/tools/src/branch/master/LICENSE
|
||||||
|
#
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
channel="$1"
|
||||||
|
playlist="$2"
|
||||||
|
playlist_url="$playlist"
|
||||||
|
regex_ch="^#extinf:\s*-?[01]\s*.*,(.*${channel,,}.*)"
|
||||||
|
regex_url="^https?:\/\/.*$"
|
||||||
|
|
||||||
|
is_downloaded=0
|
||||||
|
download_dir="/tmp/$(date '+%s%N')"
|
||||||
|
|
||||||
|
found_count=0
|
||||||
|
found_last=0
|
||||||
|
line_count=1
|
||||||
|
|
||||||
|
if [[ "$playlist" =~ $regex_url ]]; then
|
||||||
|
mkdir -p "$download_dir"
|
||||||
|
cd "$download_dir"
|
||||||
|
wget "$playlist" -q > /dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
is_downloaded=1
|
||||||
|
playlist="$download_dir/$(ls -1 "$download_dir")"
|
||||||
|
cd - > /dev/null
|
||||||
|
else
|
||||||
|
echo "ERROR: cannot download playlist: $playlist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "--------------------"
|
||||||
|
echo -e "\033[20m\033[97mChannel:\033[0m $channel"
|
||||||
|
echo -e "\033[20m\033[97mPlaylist:\033[0m $playlist_url"
|
||||||
|
echo -e "\033[20m\033[97mRegex:\033[0m $regex_ch"
|
||||||
|
echo "--------------------"
|
||||||
|
|
||||||
|
while read line; do
|
||||||
|
if [[ "${line,,}" =~ $regex_ch ]]; then
|
||||||
|
echo -e "\n\033[32m$line_count FOUND:\033[0m\t$line"
|
||||||
|
((found_count += 1))
|
||||||
|
found_last=$found_count
|
||||||
|
fi
|
||||||
|
if [ $found_last -gt 0 ]; then
|
||||||
|
if [[ "${line,,}" =~ $regex_url ]]; then
|
||||||
|
echo -e "\t\t$line"
|
||||||
|
found_last=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
((line_count += 1))
|
||||||
|
done < $playlist
|
||||||
|
|
||||||
|
if [ $found_count -eq 0 ]; then
|
||||||
|
echo -e "\033[91mNothing found\033[0m"
|
||||||
|
else
|
||||||
|
echo "--------------------"
|
||||||
|
echo -e "\033[20m\033[97mChannel:\033[0m $channel"
|
||||||
|
echo -e "\033[20m\033[97mPlaylist:\033[0m $playlist_url"
|
||||||
|
echo -e "\033[20m\033[97mFound:\033[0m\033[32m $found_count\033[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $is_downloaded -eq 1 ]; then
|
||||||
|
rm -rf "$download_dir"
|
||||||
|
fi
|
||||||
55
make-pls.sh
Executable file
55
make-pls.sh
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
#
|
||||||
|
# IPTV channel maker (all playlists)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./download-all.sh
|
||||||
|
# ./make-pls.sh "disney"
|
||||||
|
#
|
||||||
|
# 1st argument is channel name pattern.
|
||||||
|
#
|
||||||
|
# To save output in file use redirection:
|
||||||
|
# ./make-pls.sh "disney" > disney.m3u8
|
||||||
|
#
|
||||||
|
# Anthony Axenov (c) 2022
|
||||||
|
# The MIT License:
|
||||||
|
# https://git.axenov.dev/IPTV/tools/src/branch/master/LICENSE
|
||||||
|
#
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
TOOLS_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
DL_DIR="$TOOLS_DIR/downloaded"
|
||||||
|
[ ! -d "$DL_DIR" ] && echo "Error: '$DL_DIR' directory does not exist. Run ./download-all.sh" && exit 1
|
||||||
|
[ ! "$(ls -A "$DL_DIR")" ] && echo "Error: '$DL_DIR' directory is empty. Run ./download-all.sh" && exit 2
|
||||||
|
|
||||||
|
channel="$1"
|
||||||
|
playlist="$2"
|
||||||
|
|
||||||
|
regex_ch="^#extinf:\s*-?[01]\s*.*,(.*${channel,,}.*)"
|
||||||
|
regex_url="^https?:\/\/.*$"
|
||||||
|
|
||||||
|
found_count=0
|
||||||
|
found_last=0
|
||||||
|
|
||||||
|
echo "#EXTM3U"
|
||||||
|
echo "# Autogenerated at `date +%d.%m.%Y`"
|
||||||
|
echo "# https://git.axenov.dev/IPTV/tools"
|
||||||
|
echo
|
||||||
|
|
||||||
|
for file in ./downloaded/*; do
|
||||||
|
while read line; do
|
||||||
|
if [[ "${line,,}" =~ $regex_ch ]]; then
|
||||||
|
echo -e "$line"
|
||||||
|
((found_count += 1))
|
||||||
|
found_last=$found_count
|
||||||
|
fi
|
||||||
|
if [ $found_last -gt 0 ]; then
|
||||||
|
if [[ "${line,,}" =~ $regex_url ]]; then
|
||||||
|
echo -e "$line\n"
|
||||||
|
found_last=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < $file
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user