Handled a particular error.
This commit is contained in:
25
README.md
25
README.md
@@ -1,16 +1,14 @@
|
||||
# tv.sh
|
||||
|
||||
A small script to offer a selection menu of channels in thetvapp.to using tofi (or dmenu etc if you change the code) and start playing in mpv.
|
||||
A small script to offer a channel selection menu through fuzzel and pass the streaming links to mpv.
|
||||
|
||||
Intended for those who like the selection but don't necessarily want to add the m3u file to a program like vlc.
|
||||
|
||||
It clones the repo [thetvappstream](https://github.com/habitual69/thetvappstream) which actually does everything for this to be useful. Meaning this is the program that actually provides the channels and the script is just a menu to select them.
|
||||
It performs requests to epg.pw to retrieve the data.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
git clone https://git.konsthol.eu/konsthol/TheTvApp_Menu
|
||||
cd TheTvApp_Menu
|
||||
git clone https://git.konsthol.eu/konsthol/MPVTV
|
||||
cd MPVTV
|
||||
sudo cp tv.sh /usr/bin/
|
||||
(Or cp tv.sh anywhere in your $PATH)
|
||||
```
|
||||
@@ -24,21 +22,16 @@ Simply run `tv.sh stream` or `tv.sh schedule` or even better assign those two fu
|
||||
* git
|
||||
* curl
|
||||
* sed
|
||||
* tofi (or dmenu if you change the code)
|
||||
* awk
|
||||
* fuzzel (or dmenu if you change the code)
|
||||
* mpv
|
||||
* grep
|
||||
* npm
|
||||
* node
|
||||
* pup
|
||||
* jq
|
||||
* cut
|
||||
* notify-send (Optional)
|
||||
|
||||
## Credits go to
|
||||
|
||||
* [habitual69](https://github.com/habitual69)
|
||||
* [EPG](https://epg.pw/)
|
||||
|
||||
As this script uses his [thetvappstream](https://github.com/habitual69/thetvappstream) script.
|
||||
|
||||
* [THETVAPP](https://thetvapp.to/)
|
||||
|
||||
As they provide the channel streams.
|
||||
As they provide the data.
|
||||
|
||||
316
tv.sh
316
tv.sh
@@ -1,130 +1,214 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function streamChannel {
|
||||
MPVTV_DATA_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/mpvtv
|
||||
|
||||
if [[ ! -d "$HOME/Gits/thetvappstream/" ]]; then
|
||||
mkdir -p "$HOME/Gits"
|
||||
notify-send -t 2000 "Cloning necessary repositories"
|
||||
git clone https://github.com/habitual69/thetvappstream "$HOME/Gits/thetvappstream"
|
||||
cd "$HOME/Gits/thetvappstream/" || exit
|
||||
notify-send -t 2000 "Copying .env_example to .env"
|
||||
cp .env_example .env
|
||||
notify-send -t 2000 "Installing necessary dependencies"
|
||||
npm -g install
|
||||
fi
|
||||
apiurl="https://epg.pw"
|
||||
RESPONSE1_FILE="${MPVTV_DATA_DIR}/response1.txt"
|
||||
RESPONSE2_FILE="${MPVTV_DATA_DIR}/response2.txt"
|
||||
RESPONSE3_FILE="${MPVTV_DATA_DIR}/response3.txt"
|
||||
COOKIES_FILE="${MPVTV_DATA_DIR}/cookies.txt"
|
||||
CHANNELS_FILE="${MPVTV_DATA_DIR}/channels.txt"
|
||||
SCHEDULE_FILE="${MPVTV_DATA_DIR}/schedule.json"
|
||||
CORRECT_SCHEDULE_FILE="${MPVTV_DATA_DIR}/correct_schedule.json"
|
||||
M3U8_FILE="${MPVTV_DATA_DIR}/m3u8.txt"
|
||||
|
||||
cd "$HOME/Gits/thetvappstream/" || exit
|
||||
mkdir -p "${MPVTV_DATA_DIR}"
|
||||
|
||||
PIDAPP="$(pgrep -f "node app.js")"
|
||||
PIDMPV="$(pgrep mpv)"
|
||||
IP=$(hostname -i)
|
||||
|
||||
[[ -n "$PIDAPP" ]] && kill -9 "$PIDAPP"
|
||||
|
||||
notify-send -t 2000 "Getting channels list"
|
||||
|
||||
setsid -f node app.js
|
||||
sleep 5
|
||||
|
||||
channel="$(curl -s "http://$IP:5000/channels.m3u" | grep "channel/" | xargs -L1 basename | tofi --include "$HOME"/.config/tofi/tofiColors --num-results 10 --prompt-text Channels: )"
|
||||
|
||||
# channel="$(grep tvg-id "$PLAYLIST_FILE" | awk '{print $2}' | sed 's/[^"]*"\([^"]*\)".*/\1/' | rofi -dmenu -i -l 20 -p 'Channels')"
|
||||
|
||||
if [[ -n "$channel" ]]; then
|
||||
[[ -n "$PIDMPV" ]] && kill -9 "$PIDMPV"
|
||||
notify-send -t 2000 "Playing $channel"
|
||||
setsid -f mpv --vo=gpu --gpu-api=opengl --opengl-es=yes --term-osd-bar "http://$IP:5000/channel/$channel"
|
||||
sleep 5
|
||||
kill -9 "$(pgrep -f "node app.js")"
|
||||
else
|
||||
notify-send -t 2000 "Nothing was chosen"
|
||||
kill -9 "$(pgrep -f "node app.js")"
|
||||
fi
|
||||
|
||||
[[ -n "$PIDAPP" ]] && kill -9 "$PIDAPP"
|
||||
cd ~ || exit
|
||||
function usage {
|
||||
echo "Usage: $(basename "$0") [stream|schedule]"
|
||||
exit 0
|
||||
|
||||
}
|
||||
|
||||
function clearFiles {
|
||||
[[ -f "$RESPONSE2_FILE" ]] && rm "$RESPONSE2_FILE"
|
||||
[[ -f "$RESPONSE3_FILE" ]] && rm "$RESPONSE3_FILE"
|
||||
[[ -f "$SCHEDULE_FILE" ]] && rm "$SCHEDULE_FILE"
|
||||
[[ -f "$CORRECT_SCHEDULE_FILE" ]] && rm "$CORRECT_SCHEDULE_FILE"
|
||||
}
|
||||
|
||||
[[ "$#" -lt 1 ]] && usage
|
||||
[[ "$#" -gt 1 ]] && usage
|
||||
if [[ "$#" -eq 1 ]]; then
|
||||
if [[ "$1" != "stream" ]] && [[ "$1" != "schedule" ]]; then
|
||||
usage
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "$RESPONSE1_FILE" ]]; then
|
||||
response="$(curl -s -L -c "$COOKIES_FILE" -b "$COOKIES_FILE" \
|
||||
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" \
|
||||
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" \
|
||||
"$apiurl/areas/gb.html?lang=en"
|
||||
)"
|
||||
|
||||
echo "$response" > "$RESPONSE1_FILE"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1143
|
||||
if [[ ! -f "$CHANNELS_FILE" ]]; then
|
||||
CHANNELS="$(
|
||||
sed '1,/<tbody>/d' "$RESPONSE1_FILE" | \
|
||||
sed '/<\/table>/,$d' | \
|
||||
awk '/<tr>/ {block = ""} {block = block $0 "\n"} /<\/tr>/ {if (!(block ~ /<td>0<\/td>/)) print block}' | \
|
||||
sed '/^[[:space:]]*$/d' | \
|
||||
awk 'NF {print $0}' | \
|
||||
sed 's/^[[:space:]]*//' | \
|
||||
sed 'N;/\n<\/tr>/!P;D' | \
|
||||
# awk '/<tr>/ {block=""; count=0} {block=block $0 "\n"} /<\/tr>/ {if (count >= 2) block=""; else printf "%s", block; block=""; next} /<td><\/td>/ {count++}' | \
|
||||
# awk 'NR==1 {prev=$0; next} /<td><\/td>/ {prev=""; next} {if (prev) print prev; prev=$0} END {if (prev) print prev}' | \
|
||||
sed '/<td>SkyTV<\/td>/d' | \
|
||||
sed -n '/href=/ {s/.*href="\([^"]*\)".*/\1/p; b}; p' | \
|
||||
sed -n '/title=/ {s/.*>\(.*\)<\/a>/\1/p; b}; p' | \
|
||||
sed '/<tr>/d; /<\/tr>/d; /<td>/d' | \
|
||||
sed 's/<\/td>//g' | \
|
||||
awk 'NR%2{printf "%s ", $0; next} 1' | \
|
||||
awk '!seen[$2]++'
|
||||
)"
|
||||
|
||||
echo "$CHANNELS" > "$CHANNELS_FILE"
|
||||
fi
|
||||
|
||||
function getSchedule {
|
||||
|
||||
BASEURL="https://thetvapp.to/tv"
|
||||
RESULTS="$(curl -s "$BASEURL")"
|
||||
CHANNELS="$(printf "%s" "$RESULTS" | pup 'ol json{}' | jq -r '.[].children.[].text' | sed 's/amp;//g')"
|
||||
CHANNEL="$(printf "%s" "$CHANNELS" | cat -n | tofi --include "$HOME"/.config/tofi/tofiColors --num-results 10 --prompt-text Channels: | sed 's/^ *//g' | awk '{print $1}')"
|
||||
CHANNELURL="$(printf "%s" "$RESULTS" | pup 'ol json{}' | jq -r '.[].children.[].href' | sed "${CHANNEL}q;d")"
|
||||
CHANNELID="$(curl -s "https://thetvapp.to$CHANNELURL" | grep jsonUrl | cut -d'+' -f3 | head -n1 | sed 's/^ *//g' | cut -d'"' -f2)"
|
||||
CHANNELJSONURL="https://thetvapp.to/json/$CHANNELID.json"
|
||||
|
||||
GETSCHEDULE=$(cat <<EOF
|
||||
#!/usr/bin/env node
|
||||
const axios = require('axios');
|
||||
let jsonUrl = "$CHANNELJSONURL"
|
||||
axios.get(jsonUrl)
|
||||
.then(function(response) {
|
||||
let mydata = response.data;
|
||||
let index = 0;
|
||||
for (let i = 0; i < mydata.length; i++) {
|
||||
let now = new Date();
|
||||
let startTime = new Date(mydata[i]["startTime"] * 1000);
|
||||
let endTime = new Date(mydata[i]["endTime"] * 1000);
|
||||
if (startTime >= now && now <= endTime) {
|
||||
index = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = index; i < mydata.length; i++) {
|
||||
let title = mydata[i]["title"];
|
||||
if (mydata[i]["episodeTitle"] != null) {
|
||||
title = title + " - " + mydata[i]["episodeTitle"];
|
||||
}
|
||||
let airTime = new Date(mydata[i]["startTime"] * 1000);
|
||||
console.log(
|
||||
airTime.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
hour12: true
|
||||
}) +
|
||||
": " + title
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
EOF
|
||||
)
|
||||
|
||||
printf "%s" "$GETSCHEDULE" > /tmp/getSchedule.js
|
||||
chmod +x /tmp/getSchedule.js
|
||||
export NODE_PATH="$HOME/.local/share/npm/lib/node_modules"
|
||||
node /tmp/getSchedule.js
|
||||
rm /tmp/getSchedule.js
|
||||
if [[ ! -f "$CORRECT_SCHEDULE_FILE" ]]; then
|
||||
echo "Nothing to get"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
channel_name="$(jq -r '.name' "$CORRECT_SCHEDULE_FILE")"
|
||||
echo -e "Schedule for $channel_name\n"
|
||||
|
||||
# Get the current time in your local time zone in the format used in start_date
|
||||
current_time=$(date +"%d-%m-%Y %H:%M")
|
||||
|
||||
# Print the current time
|
||||
echo "Current Time: $current_time"
|
||||
|
||||
# Use jq to find the item with the closest start_date and calculate time differences in a readable format
|
||||
closest_item=$(jq --arg current_time "$current_time" '
|
||||
.epg_list
|
||||
| map(
|
||||
.start_date |= sub(" \\+0200"; "")
|
||||
| .current_time_parsed = ($current_time | strptime("%d-%m-%Y %H:%M") | mktime)
|
||||
| .start_time_parsed = (try (.start_date | strptime("%d-%m-%Y %H:%M") | mktime) catch 99999999)
|
||||
| .time_diff = (.start_time_parsed - .current_time_parsed)
|
||||
| .abs_time_diff = (if .time_diff < 0 then -(.time_diff) else .time_diff end)
|
||||
)
|
||||
| sort_by(.abs_time_diff)
|
||||
| map(
|
||||
.hours_diff = (.time_diff / 3600 | floor)
|
||||
| .minutes_diff = ((.time_diff % 3600) / 60 | floor)
|
||||
| .seconds_diff = (.time_diff % 60 | floor)
|
||||
| .time_diff_readable = (.hours_diff | tostring) + " hours, " + (.minutes_diff | tostring) + " minutes, " + (.seconds_diff | tostring) + " seconds"
|
||||
)
|
||||
| .[0]
|
||||
' "$CORRECT_SCHEDULE_FILE")
|
||||
|
||||
echo "Closest Item:"
|
||||
echo "$closest_item"
|
||||
exit 0
|
||||
}
|
||||
|
||||
function getChannel {
|
||||
|
||||
CHANNEL_SELECTED="$(awk '{for (i=2; i<=NF; i++) printf "%s ", $i; print ""}' "$CHANNELS_FILE" | fuzzel -d -p "Channel? ")"
|
||||
if [[ -z "$CHANNEL_SELECTED" ]]; then
|
||||
echo "Nothing selected"
|
||||
exit 0
|
||||
else
|
||||
CHANNEL_SELECTED="$(echo "$CHANNEL_SELECTED" | awk '{$1=$1};1')"
|
||||
CHANNEL_LINE="$(grep -n "$CHANNEL_SELECTED" "$CHANNELS_FILE" | awk -F: '{print $1}')"
|
||||
CHANNEL_URL="$(sed -n "${CHANNEL_LINE}"p "$CHANNELS_FILE" | cut -d' ' -f1)"
|
||||
CHANNEL_URL="${apiurl}${CHANNEL_URL}"
|
||||
fi
|
||||
|
||||
if [[ -f "$CORRECT_SCHEDULE_FILE" ]]; then
|
||||
PREVIOUS_CHANNEL="$(jq -r '.name' "$CORRECT_SCHEDULE_FILE")"
|
||||
if [[ "$PREVIOUS_CHANNEL" == "$CHANNEL_SELECTED" ]]; then
|
||||
echo "No need for new stuff"
|
||||
play
|
||||
exit 0
|
||||
else
|
||||
clearFiles
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$CORRECT_SCHEDULE_FILE" ]]; then
|
||||
mydate="$(date '+%Y-%m-%d')"
|
||||
schedule_end_date="$(jq -r '.end_date' "$CORRECT_SCHEDULE_FILE")"
|
||||
timestamp1="$(date -d "$mydate" +%s)"
|
||||
timestamp2="$(date -d "$schedule_end_date" +%s)"
|
||||
|
||||
if [[ "$timestamp1" -gt "$timestamp2" ]]; then
|
||||
clearFiles
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "$RESPONSE2_FILE" ]]; then
|
||||
response2="$(curl -s -L -c "$COOKIES_FILE" -b "$COOKIES_FILE" \
|
||||
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" \
|
||||
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" \
|
||||
"$CHANNEL_URL"
|
||||
)"
|
||||
|
||||
echo "$response2" > "$RESPONSE2_FILE"
|
||||
fi
|
||||
|
||||
CHANNEL_LIVE="$(grep livestream "$RESPONSE2_FILE" | sed 's/^[[:space:]]*//' | sed -n '/href=/ {s/.*href="\([^"]*\)".*/\1/p; b}; p' )"
|
||||
CHANNEL_LIVE="${apiurl}${CHANNEL_LIVE}"
|
||||
|
||||
if [[ ! -f "$RESPONSE3_FILE" ]]; then
|
||||
response3="$(curl -s -L -c "$COOKIES_FILE" -b "$COOKIES_FILE" \
|
||||
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" \
|
||||
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" \
|
||||
"$CHANNEL_LIVE"
|
||||
)"
|
||||
|
||||
echo "$response3" > "$RESPONSE3_FILE"
|
||||
fi
|
||||
|
||||
M3U8_LINK="$(grep m3u8 "$RESPONSE3_FILE" | sed 's/^[[:space:]]*//' | grep -v href | head -n1 | sed -n 's/.*"\([^"]*\)".*/\1/p')"
|
||||
|
||||
echo "$M3U8_LINK" > "$M3U8_FILE"
|
||||
SCHEDULE="$(grep json "$RESPONSE2_FILE" | sed 's/^[[:space:]]*//' | grep -v "Content-Type" | grep -v "class" | sed 's/\(.*\)<\/a.*/\1/')"
|
||||
|
||||
if [[ ! -f "$SCHEDULE_FILE" ]]; then
|
||||
response4="$(curl -s -L -c "$COOKIES_FILE" -b "$COOKIES_FILE" \
|
||||
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0" \
|
||||
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" \
|
||||
"$SCHEDULE"
|
||||
)"
|
||||
|
||||
echo "$response4" > "$SCHEDULE_FILE"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$CORRECT_SCHEDULE_FILE" ]]; then
|
||||
jq '.epg_list |= map(if .start_date then .start_date |= (sub(" +0000"; "") | strptime("%Y%m%d%H%M%S") | mktime | . + 7200 | strftime("%d-%m-%Y %H:%M +0200")) else . end)' "$SCHEDULE_FILE" > "$CORRECT_SCHEDULE_FILE"
|
||||
fi
|
||||
|
||||
play
|
||||
|
||||
}
|
||||
|
||||
[[ -z "$1" ]] && printf "Usage: tv.sh [stream|schedule]\n" && exit 1
|
||||
[[ "$1" != "stream" && "$1" != "schedule" ]] && printf "Usage: tv.sh [stream|schedule]\n" && exit 1
|
||||
[[ -z "$(command -v tofi)" ]] && printf "tofi is not installed\n" && exit 1
|
||||
[[ -z "$(command -v pup)" ]] && printf "pup is not installed\n" && exit 1
|
||||
[[ -z "$(command -v jq)" ]] && printf "jq is not installed\n" && exit 1
|
||||
[[ -z "$(command -v notify-send)" ]] && printf "notify-send is not installed\n" && exit 1
|
||||
[[ -z "$(command -v mpv)" ]] && printf "mpv is not installed\n" && exit 1
|
||||
[[ -z "$(command -v node)" ]] && printf "node is not installed\n" && exit 1
|
||||
[[ -z "$(command -v npm)" ]] && printf "npm is not installed\n" && exit 1
|
||||
[[ -z "$(command -v curl)" ]] && printf "curl is not installed\n" && exit 1
|
||||
[[ -z "$(command -v git)" ]] && printf "git is not installed\n" && exit 1
|
||||
[[ -z "$(command -v sed)" ]] && printf "sed is not installed\n" && exit 1
|
||||
[[ -z "$(command -v awk)" ]] && printf "awk is not installed\n" && exit 1
|
||||
[[ -z "$(command -v cat)" ]] && printf "cat is not installed\n" && exit 1
|
||||
[[ -z "$(command -v printf)" ]] && printf "printf is not installed\n" && exit 1
|
||||
[[ -z "$(command -v xargs)" ]] && printf "xargs is not installed\n" && exit 1
|
||||
[[ -z "$(command -v basename)" ]] && printf "basename is not installed\n" && exit 1
|
||||
[[ -z "$(command -v pgrep)" ]] && printf "pgrep is not installed\n" && exit 1
|
||||
[[ -z "$(command -v setsid)" ]] && printf "setsid is not installed\n" && exit 1
|
||||
[[ -z "$(command -v sleep)" ]] && printf "sleep is not installed\n" && exit 1
|
||||
[[ "$1" == "stream" ]] && streamChannel
|
||||
function play {
|
||||
STREAM_LINK="$(cat "$M3U8_FILE")"
|
||||
MPV_OPTS="--vo=gpu \
|
||||
--gpu-api=opengl \
|
||||
--opengl-es=yes \
|
||||
--really-quiet \
|
||||
--msg-level=all=no \
|
||||
--msg-level=ffmpeg/demuxer=no \
|
||||
--msg-level=ffmpeg=no \
|
||||
--no-load-scripts \
|
||||
--profile=sw-fast \
|
||||
--no-ytdl"
|
||||
notify-send -t 2000 "Loading $CHANNEL_SELECTED"
|
||||
MPVPID="$(pidof mpv | cut -d' ' -f1)"
|
||||
[[ -n "$MPVPID" ]] && kill -s SIGTERM "$MPVPID"
|
||||
|
||||
eval setsid -f mpv "${MPV_OPTS}" "${STREAM_LINK}"
|
||||
}
|
||||
|
||||
[[ "$1" == "schedule" ]] && getSchedule
|
||||
[[ "$1" == "stream" ]] && getChannel
|
||||
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user