diff --git a/README.md b/README.md index 3b43265..e139a21 100644 --- a/README.md +++ b/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. diff --git a/tv.sh b/tv.sh index 58c817d..12ba562 100755 --- a/tv.sh +++ b/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 - -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 -exit 0 +mkdir -p "${MPVTV_DATA_DIR}" +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,//d' "$RESPONSE1_FILE" | \ + sed '/<\/table>/,$d' | \ + awk '// {block = ""} {block = block $0 "\n"} /<\/tr>/ {if (!(block ~ /0<\/td>/)) print block}' | \ + sed '/^[[:space:]]*$/d' | \ + awk 'NF {print $0}' | \ + sed 's/^[[:space:]]*//' | \ + sed 'N;/\n<\/tr>/!P;D' | \ + # awk '// {block=""; count=0} {block=block $0 "\n"} /<\/tr>/ {if (count >= 2) block=""; else printf "%s", block; block=""; next} /<\/td>/ {count++}' | \ + # awk 'NR==1 {prev=$0; next} /<\/td>/ {prev=""; next} {if (prev) print prev; prev=$0} END {if (prev) print prev}' | \ + sed '/SkyTV<\/td>/d' | \ + sed -n '/href=/ {s/.*href="\([^"]*\)".*/\1/p; b}; p' | \ + sed -n '/title=/ {s/.*>\(.*\)<\/a>/\1/p; b}; p' | \ + sed '//d; /<\/tr>/d; //d' | \ + sed 's/<\/td>//g' | \ + awk 'NR%2{printf "%s ", $0; next} 1' | \ + awk '!seen[$2]++' + )" + + echo "$CHANNELS" > "$CHANNELS_FILE" +fi + function getSchedule { + if [[ ! -f "$CORRECT_SCHEDULE_FILE" ]]; then + echo "Nothing to get" + exit 0 + fi -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" + channel_name="$(jq -r '.name' "$CORRECT_SCHEDULE_FILE")" + echo -e "Schedule for $channel_name\n" -GETSCHEDULE=$(cat <= now && now <= endTime) { - index = i - 1; - break; - } - } + # Get the current time in your local time zone in the format used in start_date + current_time=$(date +"%d-%m-%Y %H:%M") - 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 -) + # Print the current time + echo "Current Time: $current_time" -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 -exit 0 + # 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