mirror of
https://github.com/CoolnsX/dra-cla.git
synced 2025-12-20 07:15:25 +05:30
545 lines
14 KiB
Bash
Executable File
545 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
VERSION="2.2.2"
|
|
|
|
#######################
|
|
# AUXILIARY FUNCTIONS #
|
|
#######################
|
|
|
|
help_text () {
|
|
while IFS= read -r line; do
|
|
printf "%s\n" "$line"
|
|
done <<-EOF
|
|
|
|
Usage:
|
|
${0##*/} [-v] [-q <quality>] [-a <episode>] [-d | -p <download_dir>] [<query>]
|
|
${0##*/} [-v] [-q <quality>] -c
|
|
${0##*/} -h | -D | -U | -V
|
|
|
|
Options:
|
|
-c continue watching drama from history
|
|
-a specify episode to watch
|
|
-h show helptext
|
|
-d download episode
|
|
-p download episode to specified directory
|
|
-q set video quality (best|worst|360|480|720|1080)
|
|
-v use VLC as the media player
|
|
-D delete history
|
|
-U fetch update from github
|
|
-V print version number and exit
|
|
|
|
Episode selection:
|
|
Multiple episodes can be chosen given a range
|
|
Choose episode [1-13]: 1 6
|
|
This would choose episodes 1 2 3 4 5 6
|
|
To select the last episode use -1
|
|
|
|
When selecting non-interactively, the first result will be
|
|
selected, if drama is passed
|
|
EOF
|
|
}
|
|
|
|
version_text () {
|
|
inf "Version: $VERSION" >&2
|
|
}
|
|
|
|
die () {
|
|
err "$*"
|
|
exit 1
|
|
}
|
|
|
|
# get the newest version of this script from github and replace it
|
|
update_script () {
|
|
update="$(curl -s "https://raw.githubusercontent.com/CoolnsX/dra-cla/main/dra-cla" | diff -u "$0" -)"
|
|
if [ -z "$update" ]; then
|
|
inf "Script is up to date :)"
|
|
else
|
|
if printf '%s\n' "$update" | patch "$0" - ; then
|
|
inf "Script has been updated"
|
|
else
|
|
die "Can't update for some reason!"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# checks if dependencies are present
|
|
dep_ch () {
|
|
for dep; do
|
|
if ! command -v "$dep" >/dev/null ; then
|
|
err "Program \"$dep\" not found. Please install it."
|
|
#aria2c is in the package aria2
|
|
[ "$dep" = "aria2c" ] && err "To install aria2c, Type <your_package_manager> aria2"
|
|
die
|
|
fi
|
|
done
|
|
}
|
|
|
|
download () {
|
|
case $2 in
|
|
*m3u8*)
|
|
ffmpeg -loglevel error -stats -referer "$1" -i "$2" -c copy "$download_dir/${3}${4}.mp4" ;;
|
|
*)
|
|
aria2c --summary-interval=0 -x 16 -s 16 --referer="$1" "$2" --dir="$download_dir" -o "${3}${4}.mp4" --download-result=hide ;;
|
|
esac
|
|
}
|
|
|
|
#############
|
|
# SEARCHING #
|
|
#############
|
|
|
|
# gets drama names along with its id for search term
|
|
search_drama () {
|
|
search=$(printf '%s' "$1" | tr ' ' '-' )
|
|
curl -s "$base_url/search.html?keyword=$search" |
|
|
sed -nE 's_^[[:space:]]*<a href="/videos/([^"]*)">_\1_p'
|
|
}
|
|
|
|
check_episode () {
|
|
curl -s "$base_url/videos/$1" | sed '/Latest Episodes/,$d' | sed -nE "s_^[[:space:]]*<a href.*videos/${2}(.*)\">_\1_p"
|
|
}
|
|
|
|
process_hist_entry () {
|
|
temp_drama_id=$(printf "%s" "$drama_id" | sed 's/[0-9]*.$//')
|
|
latest_ep=$(printf "%s" "$drama_id" | sed "s/$temp_drama_id//g")
|
|
current_ep=$(check_episode "$drama_id" "$temp_drama_id" | head -n 1)
|
|
if [ -n "$current_ep" ] && [ "$current_ep" -ge "$latest_ep" ]; then
|
|
printf "%s\n" "$drama_id"
|
|
fi
|
|
}
|
|
|
|
# compares history with asianembed, only shows unfinished drama
|
|
search_history () {
|
|
tput clear
|
|
[ ! -s "$logfile" ] && die "History is empty"
|
|
search_results=$(while read -r drama_id; do process_hist_entry & done < "$logfile"; wait)
|
|
[ -z "$search_results" ] && die "No unwatched episodes"
|
|
one_hist=$(printf '%s\n' "$search_results" | grep -e "$" -c)
|
|
[ "$one_hist" = 1 ] && select_first=1
|
|
drama_selection "$search_results"
|
|
ep_choice_start=$(sed -n -E "s/${selection_id}(.*)/\1/p" "$logfile")
|
|
}
|
|
|
|
##################
|
|
# URL PROCESSING #
|
|
##################
|
|
|
|
# get the download page url
|
|
get_dpage_link() {
|
|
drama_id="$1"
|
|
ep_no="$2"
|
|
|
|
curl -s "$base_url/videos/${drama_id}${ep_no}" | sed -nE 's_^[[:space:]]*<iframe src="([^"]*)".*_\1_p' |
|
|
sed 's/^/https:/g'
|
|
}
|
|
|
|
decrypt_link() {
|
|
fembed_id=$(curl -A "uwu" -s "$1" | sed -nE 's_.*fembed.*/v/([^#]*).*_\1_p')
|
|
[ -z "$fembed_id" ] || video_links=$(curl -A "uwu" -s -X POST "https://fembed9hd.com/api/source/$fembed_id" -H "x-requested-with:XMLHttpRequest" | sed -e 's/\\//g' -e 's/.*data"://' | tr "}" "\n" | sed -nE 's/.*file":"(.*)","label":"([^"]*)".*/\2>\1/p')
|
|
[ -z "$video_links" ] || return 0
|
|
secret_key='3933343232313932343333393532343839373532333432393038353835373532'
|
|
iv='39323632383539323332343335383235'
|
|
ajax_url="$base_url/encrypt-ajax.php"
|
|
ajax=$(printf "%s" "$1" | sed -nE 's/.*id=([^&]*)&.*/\1/p' | openssl enc -e -aes256 -K "$secret_key" -iv "$iv" -a)
|
|
video_links=$(curl -s -H "X-Requested-With:XMLHttpRequest" "$ajax_url" -d "id=$ajax" | sed -e 's/{"data":"//' -e 's/"}/\n/' -e 's/\\//g' | base64 -d | openssl enc -d -aes256 -K "$secret_key" -iv "$iv" | sed -e 's/\].*/\]/' -e 's/\\//g' | tr '{|}' '\n' | sed -nE 's/\"file\":"([^"]*)".*label.*P.*/\1/p')
|
|
}
|
|
|
|
# chooses the link for the set quality
|
|
get_video_quality() {
|
|
dpage_url="$1"
|
|
decrypt_link "$dpage_url"
|
|
case $quality in
|
|
best)
|
|
video_link=$(printf '%s' "$video_links" | head -n 4 | tail -n 1 | cut -d'>' -f2)
|
|
;;
|
|
worst)
|
|
video_link=$(printf '%s' "$video_links" | head -n 1 | cut -d'>' -f2)
|
|
;;
|
|
*)
|
|
video_link=$(printf '%s' "$video_links" | grep -i "${quality}p" | head -n 1 | cut -d'>' -f2)
|
|
if [ -z "$video_link" ]; then
|
|
err "Current video quality is not available (defaulting to best quality)"
|
|
quality=best
|
|
video_link=$(printf '%s' "$video_links" | head -n 4 | tail -n 1 | cut -d'>' -f2)
|
|
fi
|
|
;;
|
|
esac
|
|
printf '%s' "$video_link"
|
|
}
|
|
|
|
|
|
###############
|
|
# TEXT OUTPUT #
|
|
###############
|
|
|
|
# display an error message to stderr (in red)
|
|
err () {
|
|
printf "\033[1;31m%s\033[0m\n" "$*" >&2
|
|
}
|
|
|
|
# display an informational message (first argument in green, second in magenta)
|
|
inf () {
|
|
printf "\033[1;35m%s \033[1;35m%s\033[0m\n" "$1" "$2"
|
|
}
|
|
|
|
# prompts the user with message in $1-2 ($1 in blue, $2 in magenta) and saves the input to the variables in $REPLY and $REPLY2
|
|
prompt () {
|
|
printf "\033[1;35m%s\033[1;35m%s\033[1;34m\033[0m" "$1" "$2"
|
|
read -r REPLY REPLY2
|
|
}
|
|
|
|
# displays an even (cyan) line of a menu line with $2 as an indicator in () and $1 as the option
|
|
menu_line_even () {
|
|
printf "\033[1;36m(\033[1;36m%s\033[1;36m) \033[1;36m%s\033[0m\n" "$2" "$1"
|
|
}
|
|
|
|
# displays an odd (yellow) line of a menu line with $2 as an indicator in () and $1 as the option
|
|
menu_line_odd() {
|
|
printf "\033[1;33m(\033[1;33m%s\033[1;33m) \033[1;33m%s\033[0m\n" "$2" "$1"
|
|
}
|
|
|
|
# display alternating menu lines (even and odd)
|
|
menu_line_alternate() {
|
|
menu_line_parity=${menu_line_parity:-0}
|
|
if [ "$menu_line_parity" -eq 0 ]; then
|
|
menu_line_odd "$1" "$2"
|
|
menu_line_parity=1
|
|
else
|
|
menu_line_even "$1" "$2"
|
|
menu_line_parity=0
|
|
fi
|
|
}
|
|
|
|
# displays a warning (red) line of a menu line with $2 as an indicator in [] and $1 as the option
|
|
menu_line_strong() {
|
|
printf "\033[1;31m(\033[1;31m%s\033[1;31m) \033[1;31m%s\033[0m\n" "$2" "$1"
|
|
}
|
|
|
|
|
|
#################
|
|
# INPUT PARSING #
|
|
#################
|
|
|
|
# only lets the user pass in case of a valid search
|
|
process_search () {
|
|
search_results=$(search_drama "$query")
|
|
while [ -z "$search_results" ]; do
|
|
err 'No search results found'
|
|
prompt 'Search Drama: '
|
|
query="$REPLY $REPLY2"
|
|
search_results=$(search_drama "$query")
|
|
done
|
|
drama_selection "$search_results"
|
|
episode_selection
|
|
}
|
|
|
|
# drama-selection menu handling function
|
|
drama_selection () {
|
|
count=1
|
|
while read -r drama_id; do
|
|
menu_line_alternate "$drama_id" "$count"
|
|
: $((count+=1))
|
|
done <<-EOF
|
|
$search_results
|
|
EOF
|
|
if [ -n "$select_first" ]; then
|
|
tput clear
|
|
choice=1
|
|
elif [ -z "$ep_choice_to_start" ] || { [ -n "$ep_choice_to_start" ] && [ -z "$select_first" ]; }; then
|
|
menu_line_strong "exit" "q"
|
|
prompt "> "
|
|
choice="$REPLY"
|
|
while ! [ "$choice" -eq "$choice" ] 2>/dev/null || [ "$choice" -lt 1 ] || [ "$choice" -ge "$count" ] || [ "$choice" = " " ]; do
|
|
[ "$choice" = "q" ] && exit 0
|
|
err "Invalid choice entered"
|
|
prompt "> "
|
|
choice="$REPLY"
|
|
done
|
|
fi
|
|
# Select respective drama_id
|
|
selection_id="$(printf "%s" "$search_results" | sed -n "${choice}p")"
|
|
temp_drama_id=$(printf "%s" "$selection_id" | sed 's/[0-9]*.$//')
|
|
select_ep_result=$(check_episode "$selection_id" "$temp_drama_id")
|
|
last_ep_number=$(printf "%s" "$select_ep_result" | head -n 1)
|
|
first_ep_number=$(printf "%s" "$select_ep_result" | tail -n 1)
|
|
selection_id=$temp_drama_id
|
|
}
|
|
|
|
# gets episode number from user, makes sure it's in range, skips input if only one episode exists
|
|
episode_selection () {
|
|
if [ "$last_ep_number" -gt "$first_ep_number" ]; then
|
|
if [ -z "$ep_choice_to_start" ]; then
|
|
# if branches, because order matters this time
|
|
while : ; do
|
|
inf "To specify a range, use: start_number end_number"
|
|
inf "Episodes:" "($first_ep_number-$last_ep_number)"
|
|
prompt "> "
|
|
ep_choice_start="$REPLY"
|
|
ep_choice_end="$REPLY2"
|
|
if [ "$REPLY" = q ]; then
|
|
exit 0
|
|
fi
|
|
[ "$ep_choice_end" = "-1" ] && ep_choice_end="$last_ep_number"
|
|
if ! [ "$ep_choice_start" -eq "$ep_choice_start" ] 2>/dev/null || { [ -n "$ep_choice_end" ] && ! [ "$ep_choice_end" -eq "$ep_choice_end" ] 2>/dev/null; }; then
|
|
err "Invalid number(s)"
|
|
continue
|
|
fi
|
|
if [ "$ep_choice_start" -gt "$last_ep_number" ] 2>/dev/null || [ "$ep_choice_end" -gt "$last_ep_number" ] 2>/dev/null || [ "$ep_choice_start" -lt "$first_ep_number" ] 2>/dev/null; then
|
|
err "Episode out of range"
|
|
continue
|
|
fi
|
|
if [ "$ep_choice_end" -le "$ep_choice_start" ]; then
|
|
err "Invalid range"
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
else
|
|
ep_choice_start="$ep_choice_to_start" && unset ep_choice_to_start
|
|
fi
|
|
else
|
|
# In case the drama contains only a single episode
|
|
ep_choice_start=1
|
|
fi
|
|
if [ -z "$ep_choice_end" ]; then
|
|
auto_play=0
|
|
else
|
|
auto_play=1
|
|
fi
|
|
}
|
|
|
|
# creates $episodes from $ep_choice_start and $ep_choice_end
|
|
generate_ep_list() {
|
|
episodes=$ep_choice_start
|
|
[ -n "$ep_choice_end" ] && episodes=$(seq "$ep_choice_start" "$ep_choice_end")
|
|
}
|
|
|
|
|
|
##################
|
|
# VIDEO PLAYBACK #
|
|
##################
|
|
|
|
append_history () { # todo: unite this with the temporary histfile writing
|
|
grep -q "${selection_id}" "$logfile" || printf "%s%s\n" "$selection_id" $((episode+1)) >> "$logfile"
|
|
}
|
|
|
|
# opens selected episodes one-by-one
|
|
open_selection() {
|
|
for ep in $episodes; do
|
|
open_episode "$selection_id" "$ep"
|
|
done
|
|
episode=${ep_choice_end:-$ep_choice_start}
|
|
}
|
|
|
|
open_episode () {
|
|
drama_id="$1"
|
|
episode="$2"
|
|
|
|
tput clear
|
|
inf "Loading episode $episode..."
|
|
# decrypting url
|
|
dpage_link=$(get_dpage_link "$drama_id" "$episode")
|
|
echo "$dpage_link"
|
|
video_url=$(get_video_quality "$dpage_link")
|
|
echo "$video_url"
|
|
if [ "$is_download" -eq 0 ]; then
|
|
# write drama and episode number and save to temporary history
|
|
sed -E "
|
|
s/^${selection_id}[0-9]*/${selection_id}$((episode+1))/
|
|
" "$logfile" > "${logfile}.new"
|
|
[ ! "$PID" = "0" ] && kill "$PID" >/dev/null 2>&1
|
|
[ -z "$video_url" ] && die "Video URL not found"
|
|
play_episode
|
|
# overwrite history with temporary history
|
|
mv "${logfile}.new" "$logfile"
|
|
else
|
|
mkdir -p "$download_dir"
|
|
inf "Downloading episode $episode ..."
|
|
episode=$(printf "%03d" "$episode")
|
|
{
|
|
if download "$dpage_link" "$video_url" "$drama_id" "$episode" ; then
|
|
inf "Downloaded episode: $episode"
|
|
else
|
|
err "Download failed episode: $episode , please retry or check your internet connection"
|
|
fi
|
|
}
|
|
fi
|
|
}
|
|
|
|
play_episode () {
|
|
# Build command
|
|
set -- "$player_fn" "$video_url"
|
|
case "$player_fn" in
|
|
vlc)
|
|
[ ! "$auto_play" -eq 0 ] && set -- "$@" "--play-and-exit"
|
|
set -- "$@" --http-referrer="$dpage_link"
|
|
;;
|
|
*)
|
|
set -- "$@" --referrer="$dpage_link" --force-media-title="${drama_id}${episode}"
|
|
;;
|
|
esac
|
|
# Run Command
|
|
if [ "$auto_play" -eq 0 ]; then
|
|
nohup "$@" > /dev/null 2>&1 &
|
|
else
|
|
inf "Currently playing $display_name episode" "$episode/$last_ep_number, Range: $ep_choice_start-$ep_choice_end"
|
|
"$@" > /dev/null 2>&1
|
|
sleep 2
|
|
fi
|
|
PID=$!
|
|
}
|
|
|
|
############
|
|
# START UP #
|
|
############
|
|
|
|
# clears the colors and deletes temporary logfile when exited using SIGINT
|
|
trap 'printf "\033[0m";[ -f "$logfile".new ] && rm "$logfile".new;exit 1' INT HUP
|
|
|
|
# default options
|
|
player_fn="mpv" #video player needs to be able to play urls
|
|
is_download=0
|
|
PID=0
|
|
quality=best
|
|
scrape=query
|
|
download_dir="."
|
|
choice=
|
|
auto_play=0
|
|
# history file path
|
|
logfile="${XDG_CACHE_HOME:-$HOME/.cache}/dra-hsts"
|
|
logdir="${XDG_CACHE_HOME:-$HOME/.cache}"
|
|
|
|
# create history file and history dir if none found
|
|
[ -d "$logdir" ] || mkdir "$logdir"
|
|
[ -f "$logfile" ] || : > "$logfile"
|
|
|
|
while getopts 'vq:dp:chDUVa:' OPT; do
|
|
case $OPT in
|
|
d)
|
|
is_download=1
|
|
;;
|
|
a)
|
|
ep_choice_to_start=$OPTARG
|
|
;;
|
|
D)
|
|
: > "$logfile"
|
|
exit 0
|
|
;;
|
|
p)
|
|
is_download=1
|
|
download_dir=$OPTARG
|
|
;;
|
|
q)
|
|
quality=$OPTARG
|
|
;;
|
|
c)
|
|
scrape=history
|
|
;;
|
|
v)
|
|
player_fn="vlc"
|
|
;;
|
|
U)
|
|
update_script
|
|
exit 0
|
|
;;
|
|
V)
|
|
version_text
|
|
exit 0
|
|
;;
|
|
*)
|
|
help_text
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
dep_ch "curl" "sed" "grep" "openssl"
|
|
if [ "$is_download" -eq 0 ]; then
|
|
dep_ch "$player_fn"
|
|
else
|
|
dep_ch "aria2c" "ffmpeg"
|
|
fi
|
|
|
|
# Get the current working URL
|
|
base_url=$(curl -Ls -o /dev/null -w %{url_effective} https://asianembed.io)
|
|
|
|
case $scrape in
|
|
query)
|
|
if [ -z "$*" ]; then
|
|
prompt "Search Drama: "
|
|
query="$REPLY $REPLY2"
|
|
else
|
|
if [ -n "$ep_choice_to_start" ]; then
|
|
REPLY=1
|
|
select_first=1
|
|
fi
|
|
query="$*"
|
|
fi
|
|
process_search
|
|
;;
|
|
history)
|
|
search_history
|
|
[ "$REPLY" = "q" ] && exit 0
|
|
first_ep_number=$(check_episode "${selection_id}1" "$selection_id" | tail -1)
|
|
;;
|
|
*)
|
|
die "Unexpected scrape type"
|
|
esac
|
|
|
|
generate_ep_list
|
|
append_history
|
|
open_selection
|
|
|
|
########
|
|
# LOOP #
|
|
########
|
|
|
|
while :; do
|
|
if [ -z "$select_first" ]; then
|
|
if [ "$auto_play" -eq 0 ]; then
|
|
display_name=$(printf '%s' "$selection_id" | sed 's/-episode-//')
|
|
inf "Currently playing $display_name episode" "$episode/$last_ep_number"
|
|
else
|
|
auto_play=0
|
|
fi
|
|
[ "$episode" -ne "$last_ep_number" ] && menu_line_alternate 'next' 'n'
|
|
[ "$episode" -ne "$first_ep_number" ] && menu_line_alternate 'previous' 'p'
|
|
menu_line_alternate "replay" "r"
|
|
[ "$last_ep_number" -ne "$first_ep_number" ] && menu_line_alternate 'select' 's'
|
|
menu_line_strong "exit" "q"
|
|
prompt "> "
|
|
choice="$REPLY"
|
|
case $choice in
|
|
n)
|
|
ep_choice_start=$((episode + 1))
|
|
unset ep_choice_end
|
|
;;
|
|
p)
|
|
ep_choice_start=$((episode - 1))
|
|
unset ep_choice_end
|
|
;;
|
|
r)
|
|
ep_choice_start="$episode"
|
|
unset ep_choice_end
|
|
;;
|
|
s)
|
|
episode_selection
|
|
;;
|
|
q)
|
|
break
|
|
;;
|
|
*)
|
|
tput clear
|
|
err "Invalid choice"
|
|
continue
|
|
;;
|
|
esac
|
|
generate_ep_list
|
|
append_history
|
|
open_selection
|
|
else
|
|
wait $!
|
|
exit
|
|
fi
|
|
done
|