Last active
March 4, 2022 21:45
-
-
Save diazona/fa34f1d5163086f8236b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
'''A script that reads episode filenames from standard input, one per line, | |
and prints out the corresponding filenames in a standard format on standard | |
output, one per line. The tvnamer Python library is used to generate the | |
new filenames, and TheTVDB is used to access episode titles. So this does | |
basically the same thing as tvnamer, except that instead of actually | |
renaming files, it just transforms filenames.''' | |
import os.path, sys, json | |
from tvdb_api import Tvdb | |
from tvnamer.tvnamer_exceptions import BaseTvnamerException, DataRetrievalError | |
from tvnamer.utils import Config, FileParser, split_extension | |
def read_config(cfg_filename, dry=False, verbose=False): | |
Config.update({ | |
'search_all_languages': False, | |
'language': 'en', | |
'batch': True, | |
'dry-run': dry, | |
'verbose': verbose, | |
'episode_separator': '-', | |
'episode_single': 'e%02d', | |
'multiep_format': '%(epname)s (%(episodemin)s-%(episodemax)s)', | |
'filename_anime_with_episode': '%(seriesname)s - %(episode)s - %(episodename)s%(ext)s', | |
'filename_anime_with_episode_without_crc': '%(seriesname)s - %(episode)s - %(episodename)s%(ext)s', | |
'filename_anime_without_episode': '%(seriesname)s - %(episode)s%(ext)s', | |
'filename_anime_without_episode_without_crc': '%(seriesname)s - %(episode)s%(ext)s', | |
'filename_with_date_and_episode': '%(seriesname)s - %(year)04d-%(month)02d-%(day)02d - %(episodename)s%(ext)s', | |
'filename_with_date_without_episode': '%(seriesname)s - %(year)04d-%(month)02d-%(day)02d%(ext)s', | |
'filename_with_episode': '%(seriesname)s - s%(seasonnumber)02d%(episode)s - %(episodename)s%(ext)s', | |
'filename_with_episode_no_season': '%(seriesname)s - s00%(episode)s - %(episodename)s%(ext)s', | |
'filename_without_episode': '%(seriesname)s - %(seasonnumber)02d%(episode)s%(ext)s', | |
'filename_without_episode_no_season': '%(seriesname)s - s00%(episode)s%(ext)s', | |
}) | |
try: | |
with open(cfg_filename) as f: | |
Config.update(json.load(f)) | |
except FileNotFoundError: | |
pass | |
def new_name(filename): | |
ep = FileParser(filename).parse() | |
ep.populateFromTvdb(Tvdb(interactive=False, search_all_languages=False, language=Config['language'], dvdorder=True, cache=True)) | |
return os.path.join(os.path.dirname(filename), ep.generateFilename()) | |
def main(): | |
try: | |
read_config(sys.argv[1], bool(sys.argv[2]), bool(sys.argv[3])) | |
except Exception: | |
sys.exit(1) | |
try: | |
for line in sys.stdin: | |
try: | |
print(new_name(line.rstrip('\n'))) | |
except (BaseTvnamerException, OSError): | |
if Config['skip_file_on_error'] or Config['skip_behaviour'] == 'exit': | |
sys.exit(1) | |
else: | |
print(line) | |
except Exception: | |
sys.exit(1) | |
except KeyboardInterrupt: | |
pass | |
if __name__ == '__main__': | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
SCRIPT="$(realpath $0)" | |
COLOR_NC='\033[00m' # No Color | |
COLOR_WHITE='\033[01;37m' | |
COLOR_BLACK='\033[00;30m' | |
COLOR_BLUE='\033[00;34m' | |
COLOR_LIGHT_BLUE='\033[01;34m' | |
COLOR_GREEN='\033[00;32m' | |
COLOR_LIGHT_GREEN='\033[01;32m' | |
COLOR_CYAN='\033[00;36m' | |
COLOR_LIGHT_CYAN='\033[01;36m' | |
COLOR_RED='\033[00;31m' | |
COLOR_LIGHT_RED='\033[01;31m' | |
COLOR_PURPLE='\033[00;35m' | |
COLOR_LIGHT_PURPLE='\033[01;35m' | |
COLOR_BROWN='\033[00;33m' | |
COLOR_YELLOW='\033[01;33m' | |
COLOR_GRAY='\033[00;30m' | |
COLOR_LIGHT_GRAY='\033[00;37m' | |
COLOR_BOLD=$(tput bold) # not really a "color" but close enough | |
COLOR_RESET=$(tput sgr0) | |
TERM_TITLE='\033]2;' | |
END_TERM_TITLE='\007' | |
RESET_TERM_TITLE='\e]0;\a' | |
HOSTNAME="$(hostname)" | |
NEWLINE=$'\n' | |
RIP_ROOT_DIR="$HOME/tmp/rips" | |
RIP_TVNAMER="$(dirname "$SCRIPT")/rip-tvnamer.py" | |
RIP_TVNAMER_CONF="$(dirname "$SCRIPT")/rip-tvnamer.json" | |
source_conf() { | |
[ -f "$1" ] && source "$1" | |
} | |
source_conf ./rip.conf | |
source_conf "$HOME/.config/rip.conf" | |
source_conf "$(dirname "$0")/rip.conf" | |
usage() { | |
local usage_str | |
case "$mode" in | |
movie) | |
usage_str="Usage in movie mode: $progname 'title (year)' [dvdtitlespec] or $progname title [year [dvdtitlespec]]" | |
;; | |
tv) | |
usage_str="Usage in tv mode: $progname seriestitle season dvdtitlespecs..." | |
;; | |
*) | |
usage_str="Usage: | |
(tv) $progname seriestitle season dvdtitlespecs... | |
(movie) $progname 'title (year)' [dvdtitlespec] | |
(movie) $progname title year [dvdtitlespec]" | |
;; | |
esac | |
die "$usage_str" | |
} | |
stripcolors() { | |
sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g;s/\\\\033.{1,6}m//g" | |
} | |
log() { | |
local label="${1}" | |
shift | |
stripcolors <<< "[$label] $@" >> rip.log | |
return 0 | |
} | |
die() { | |
echo -e "${COLOR_RED}${COLOR_BOLD}$@${COLOR_RESET}" >&2 | |
log "die" "$@" | |
exit 1 | |
} | |
logecho() { | |
echo -e "$@" | |
log "echo" "$@" | |
} | |
logdebug() { | |
[ -n "$debug" ] && log "debug" "$@" | |
return 0 | |
} | |
notify_text() { | |
shift | |
curl --silent http://textbelt.com/text -d "number=$PHONE_NUMBER" -d "message=$* on $HOSTNAME" >>rip.log | |
echo >>rip.log | |
} | |
notify_pushbullet() { | |
local notification_title="$1" | |
shift | |
curl --silent --header "Access-Token: $PUSHBULLET_ACCESS_TOKEN" -X POST https://api.pushbullet.com/v2/pushes --header 'Content-Type: application/json' --data-binary "{\"type\": \"note\", \"title\": \"$notification_title\", \"body\": \"$* on $HOSTNAME\"}" >>rip.log | |
echo >>rip.log | |
} | |
notify_kdialog() { | |
local notification_title="$1" | |
shift | |
kdialog --passivepopup "$*" 4 --title "$notification_title" | |
} | |
mark_start() { | |
logecho "${COLOR_YELLOW}Starting at $(date)${COLOR_RESET}" | |
} | |
mark_stop() { | |
logecho "${COLOR_YELLOW}Ending at $(date)${COLOR_RESET}" | |
} | |
get_free_megabytes() { | |
local dfdir="$1" | |
until [ -f "$dfdir" -o -d "$dfdir" ]; do | |
dfdir="$(dirname "$dfdir")" | |
done | |
df --block-size=1M "$dfdir" | |
} | |
get_length() { | |
duration="$(ffprobe -show_entries format=duration -v error -of default=noprint_wrappers=1:nokey=1 -sexagesimal "$1")" | |
[[ "$duration" =~ ^([0-9]+):([0-5][0-9]):([0-5][0-9]\.[0-9]+)$ ]] | |
hours="${BASH_REMATCH[1]}" | |
minutes="${BASH_REMATCH[2]}" | |
seconds="${BASH_REMATCH[3]}" | |
if [[ "$hours" -gt 0 ]]; then | |
echo "${hours}h ${minutes}m" | |
else | |
echo "${minutes}m" | |
fi | |
} | |
output_check() { | |
local outdir="$(dirname "$outfile")" | |
# ensure that output directory exists | |
if [[ ! -d "$outdir" ]]; then | |
if [ -z "$dry" ]; then | |
logecho "Creating output directory $outdir" | |
mkdir -p "$outdir" || die "Failed to create output directory $outdir" | |
else | |
logecho "Not creating output directory $outdir (dry run)" | |
fi | |
fi | |
# print a warning if filesystem is running low on space | |
local dfoutput="$(get_free_megabytes "$outdir")" | |
local freespace="$(awk 'NR==2 {print $4}' <<<"$dfoutput")" | |
local mountpoint="$(awk 'NR==2 {print $1}' <<<"$dfoutput")" | |
if (( $freespace < 1500 )); then # allow 1.5G per disc | |
die "insufficient free space on partition $mountpoint" | |
elif (( $freespace < 3000 )); then | |
logecho "${COLOR_RED}${COLOR_BOLD}WARNING: ${COLOR_RED}free space on partition $mountpoint is almost exhausted${COLOR_RESET}" | |
elif (( $freespace < 6000 )); then | |
logecho "${COLOR_YELLOW}${COLOR_BOLD}WARNING: ${COLOR_YELLOW}free space on partition $mountpoint is getting low${COLOR_RESET}" | |
fi | |
} | |
set_terminal_title() { | |
echo -n -e "${TERM_TITLE}$(stripcolors <<< "$*")${END_TERM_TITLE}" | |
} | |
reset_terminal_title() { | |
# not sure if this really works... | |
# http://superuser.com/questions/339862/restore-mac-os-x-terminal-title-after-closing-a-ssh-connection | |
echo -n -e "${RESET_TERM_TITLE}" | |
} | |
# expects newline-separated numbers on stdin | |
# not guaranteed to work for negative inputs | |
# http://stackoverflow.com/questions/13708705/in-bash-how-to-convert-number-list-into-ranges-of-numbers | |
list_to_ranges() { | |
local num | |
while read num || [ -n "$last" ]; do | |
[ -n "$num" ] && let num="10#$num" | |
if [ -z "$last" ]; then | |
first="$num" | |
last="$num" | |
continue | |
fi | |
if [[ "$num" -eq "$(($last+1))" ]]; then | |
let last++ | |
else | |
if [[ "$first" -eq "$last" ]]; then | |
echo "$first" | |
else | |
echo "$first-$last" | |
fi | |
first="$num" | |
last="$num" | |
fi | |
done | |
} | |
select_cpus() { | |
seq "$(grep -c processor /proc/cpuinfo)" | shuf -n "$1" | tr '\n' ',' | head -c -1 | |
} | |
send_notifications() { | |
for m in $(<<< "$notification_modes" tr ' ' '\n' | sort -u); do # outer non-quoting here is intentional | |
notify_$m "Rip complete" "$notification_message" | |
done | |
} | |
run_handbrake() { | |
output_check | |
local cmdline="" | |
[ -n "$ncpus" ] && cmdline+="taskset -c '$(select_cpus $ncpus)' " | |
cmdline+="HandBrakeCLI --preset 'Normal' ${handbrake_opts} " | |
if [ "$title" == "0" ]; then | |
cmdline+="--main-feature " | |
else | |
cmdline+="--title '$title' " | |
fi | |
[ -n "$chapters" -a "$chapters" != "-" ] && cmdline+="--chapters ${chapters} " | |
cmdline+="-i '$device' -o \"${outfile}\"" # hopefully there are no double quotes or backslashes in $outfile | |
log "command" "$cmdline" | |
if [ -n "$dry" ]; then | |
echo "$cmdline" | |
logecho "Rip successful (dry run)" | |
else | |
eval "$cmdline" </dev/null 2>>rip.log || die "Ripping ${display_output} failed" | |
logecho "Rip successful: generated length ${COLOR_CYAN}${COLOR_BOLD}$(get_length "${outfile}")${COLOR_RESET}" | |
fi | |
} | |
search_year_omdb() { | |
local name="$1" | |
r="$(curl --silent 'http://www.omdbapi.com/' -G --data-urlencode "s=$name" --data-urlencode "apikey=$OMDB_APIKEY" | jq -re "select(.Search | .[].Title == \"$name\") | .Search | .[].Year")" || die "Movie lookup failed (OMDB API)" | |
echo "$r" | |
} | |
search_year_themoviedb() { | |
die "TheMovieDB not enabled" | |
} | |
search_year_theimdbapi() { | |
local name="$1" | |
r="$(curl --silent 'http://theimdbapi.org/api/find/movie' -G --data-urlencode "title=$name" | jq -re "select(.[].title == \"$name\") | .[].year")" || die "Movie lookup failed (The IMDB API)" | |
echo "$r" | |
} | |
rip_movie() { | |
local name="$1" | |
shift | |
if [[ "$name" =~ \([12][0-9]{3}\)$ ]]; then | |
year="${name: -5:4}" | |
name="${name%%*([[:space:]])\(????\)}" | |
elif [[ "$1" =~ ^[12][0-9]{3}$ ]]; then | |
year="$1" | |
shift | |
else | |
# get year from IMDB | |
local years | |
years="$(search_year_omdb "$name")" | |
case "${#years}" in | |
4) | |
year="$years" ;; | |
0) | |
die "Movie title '$name' not recognized" ;; | |
*) | |
echo "Select year:" | |
select year in $years; do | |
[ -n "$year" ] && break | |
done ;; | |
esac | |
fi | |
case "$#" in | |
0) | |
title="0" | |
;; | |
1) | |
# parses something of the form | |
# title | |
# title:chapter-chapter | |
# :chapter-chapter | |
logdebug "parsing title spec $1" | |
if [[ "$1" =~ ^[0-9]*:[0-9]+(-[0-9]+)?$ ]]; then | |
title="${1%:*}" | |
chapters="${1#*:}" | |
elif [[ "$1" =~ ^[0-9]+$ ]]; then | |
title="$1" | |
else | |
die "Invalid title/chapter spec: '$1'" | |
fi | |
;; | |
*) | |
usage | |
;; | |
esac | |
outfile="${RIP_ROOT_DIR}/movies/${name//:/} ($year).m4v" # colon is a common character in movie titles that is not allowed in NTFS filenames | |
[[ -f "$outfile" ]] && die "Not ripping existing file $outfile" | |
display_output="${COLOR_LIGHT_CYAN}$name ${COLOR_LIGHT_PURPLE}($year)" | |
set_terminal_title "Ripping $display_output" | |
if [ "$title" == "0" ]; then | |
display_title="main title" | |
else | |
display_title="title ${title}" | |
fi | |
logecho "${COLOR_BOLD}Ripping ${display_output} ${COLOR_WHITE}from ${display_title} to ${outfile}...${COLOR_RESET}" | |
run_handbrake | |
notification_message="Finished ripping $name ($year)" | |
} | |
# parses something of the form | |
# title | |
# title:chapter-chapter | |
# title[episode] | |
# title:chapter-chapter[episode] | |
# title[episode-episode] | |
# title:chapter-chapter[episode-episode] | |
# title-title | |
# title-title/increment | |
# title[episode]-title | |
# title[episode]-title/increment | |
# or a comma-separated sequence of the above | |
parse_titlespecs_tv() { | |
local title chapters first_episode last_episode first last step | |
for a in ${@//,/ }; do # don't use double quotes here so that comma-separated specs get interpreted as different words after the replacement | |
logdebug "title spec $a" | |
if [[ "$a" =~ ^([0-9]+)(:([0-9]+(-[0-9]+)?))?(\[([0-9]+)(-([0-9]+))?\])?$ ]]; then | |
title="${BASH_REMATCH[1]}" | |
chapters="${BASH_REMATCH[3]}" | |
first_episode="${BASH_REMATCH[6]}" | |
last_episode="${BASH_REMATCH[8]}" | |
(( 10#$title == 0 )) && die "Invalid title/chapter spec: '$a'" | |
echo "$title" "${chapters:=-}" "$first_episode" "$last_episode" | |
elif [[ "$a" =~ ^[0-9]+$ ]]; then | |
(( $a == 0 )) && die "Invalid title/chapter spec: '$a'" | |
echo "$a" | |
elif [[ "$a" =~ ^([0-9]+)-([0-9]+)(/([0-9]+))?$ ]]; then | |
first="${BASH_REMATCH[1]}" | |
last="${BASH_REMATCH[2]}" | |
step="${BASH_REMATCH[4]}" | |
(( 10#$first == 0 || 10#$last < 10#$first || 10#${step:-1} == 0 )) && die "Invalid title/chapter spec: '$a'" | |
# non-quoting of $step here is intentional; if it's empty, seq thinks it's only getting two arguments | |
seq "$first" $step "$last" | |
elif [[ "$a" =~ ^([0-9]+)\[([0-9]+)\]-([0-9]+)(/([0-9]+))?$ ]]; then | |
first="${BASH_REMATCH[1]}" | |
last="${BASH_REMATCH[3]}" | |
step="${BASH_REMATCH[5]}" | |
first_episode="${BASH_REMATCH[2]}" | |
[ -z "$first_episode" ] && first_episode="$starting_episode" | |
(( 10#$first == 0 || 10#$last < 10#$first || 10#${step:-1} == 0 )) && die "Invalid title/chapter spec: '$a'" | |
# non-quoting of $step here is intentional; if it's empty, seq thinks it's only getting two arguments | |
seq "$first" $step "$last" | while read n; do echo "$n" "-" $(((10#$n-10#${first})/$step+10#${first_episode})); done | |
else | |
die "Invalid title/chapter spec: '$a'" | |
fi | |
done | |
} | |
# Find a range of $1 consecutive episode numbers that are unused | |
# and echo the beginning of the range | |
find_free_episode_range() { | |
local episodes last_episode_in_any_file=0 range_length="$1" outdir="${2:-$PWD}" range | |
# Collect a newline-separated list of all the episode numbers | |
# among all the m4v files in $outdir | |
for f in "$outdir"/*.m4v; do | |
# If there are no m4v files in the directory, immediately echo 1 and return | |
# All episode numbers are free so there's no need for further checking | |
if ! [[ -f "$f" ]]; then | |
echo 1 | |
return | |
fi | |
[[ "$(basename "$f")" =~ s[0-9]+e([0-9]+)(-e([0-9]+))?( - .+)?\.m4v$ ]] | |
first_episode_in_file="${BASH_REMATCH[1]}" | |
last_episode_in_file="${BASH_REMATCH[3]}" | |
let first_episode_in_file="10#$first_episode_in_file" | |
if [ -n "$last_episode_in_file" ]; then | |
let last_episode_in_file="10#$last_episode_in_file" | |
episodes="$episodes$NEWLINE$(seq "$first_episode_in_file" "$last_episode_in_file")" | |
[ "$last_episode_in_any_file" -gt "$last_episode_in_file" ] || last_episode_in_any_file="$last_episode_in_file" | |
else | |
episodes="$episodes$NEWLINE$first_episode_in_file" | |
[ "$last_episode_in_any_file" -gt "$first_episode_in_file" ] || last_episode_in_any_file="$first_episode_in_file" | |
fi | |
done | |
# Then append to that list a newline-separated sequence | |
# from 1 to the largest episode number | |
episodes="$episodes$NEWLINE$(seq $last_episode_in_any_file)" | |
# Sort and uniq-filter the resulting list to identify any numbers | |
# which only occur once - those are the numbers for which no | |
# episode yet exists | |
for range in $(sort <<<"$episodes" | uniq -u | list_to_ranges); do | |
# If only one free episode number is needed, return the first one | |
if [[ "$range_length" -eq 1 ]] && [[ "$range" =~ [0-9]+ ]]; then | |
echo "$range" | |
return | |
# If a range of free episode numbers is needed, check each range | |
# to see if it's long enough and return the first one that is | |
elif [[ "$range_length" -gt 1 ]] && [[ "$range" =~ [0-9]+-[0-9]+ ]]; then | |
if (( $range + $range_length > 0 )); then | |
echo "${range%%-*}" | |
return | |
fi | |
fi | |
done | |
# If no suitable free episode numbers were found that way, return | |
# one more than the largest existing episode number | |
echo "$((10#$last_episode_in_any_file + 1))" | |
} | |
ripexists_tv() { | |
local episoderangetwodigit seasontwodigit series_sanitized | |
# $outfile gets used again in rip_tv() | |
if [ -n "$last_episode" ]; then | |
printf -v episoderangetwodigit 'e%02d-e%02d' $first_episode $last_episode | |
else | |
printf -v episoderangetwodigit 'e%02d' $first_episode | |
fi | |
series_sanitized="${series//:/}" | |
if [[ "$season" =~ ^([Ss]pecials?|00?)$ ]]; then | |
season="0" | |
seasontwodigit="s00" | |
outfile="${RIP_ROOT_DIR}/tv/$series_sanitized/Specials/$series_sanitized - ${seasontwodigit}${episoderangetwodigit}.m4v" | |
else | |
printf -v seasontwodigit 's%02d' $season | |
outfile="${RIP_ROOT_DIR}/tv/$series_sanitized/Season $season/$series_sanitized - ${seasontwodigit}${episoderangetwodigit}.m4v" | |
fi | |
[ -n "$use_tvnamer" ] && tvnamer_transform_outfile | |
[ -f "$outfile" ] | |
} | |
# Checks the requirements for the Python script that invokes tvnamer | |
check_tvnamer_reqs() { | |
python3 - 2>/dev/null <<-EOF | |
import sys | |
try: | |
import json | |
from tvdb_api import Tvdb | |
from tvnamer.tvnamer_exceptions import BaseTvnamerException, DataRetrievalError | |
from tvnamer.utils import Config, FileParser | |
except ImportError: | |
sys.exit(1) | |
EOF | |
} | |
start_tvnamer() { | |
[ -n "$use_tvnamer" ] || return | |
[ -n "$tvnamer_started" ] && return | |
# Very important to use -u for unbuffered output | |
logdebug "Running tvnamer script" | |
log "command" "$RIP_TVNAMER" "${tvnamer_cfgfile:-$RIP_TVNAMER_CONF}" "$dry" "$debug" | |
coproc python3 -u "$RIP_TVNAMER" "${tvnamer_cfgfile:-$RIP_TVNAMER_CONF}" "$dry" "$debug" | |
tvnamer_stdout=${COPROC[0]} | |
tvnamer_stdin=${COPROC[1]} | |
tvnamer_pid=$COPROC_PID | |
tvnamer_started="1" | |
logdebug "tvnamer script started" | |
} | |
tvnamer_transform_outfile() { | |
[ -n "$use_tvnamer" ] || return | |
[ -n "$tvnamer_started" ] || die "tvnamer script not started" | |
echo "$outfile" >&"$tvnamer_stdin" | |
# 30 seconds should be more than long enough to get a response from TVDB | |
if ! read -t 30 -u "$tvnamer_stdout" tvdb_filename; then | |
logecho "Error determining new filename from TVDB" | |
return | |
fi | |
outfile="$tvdb_filename" | |
} | |
rip_tv() { | |
(( $# < 3 )) && usage | |
local starting_episode_option_set | |
[ -n "$starting_episode" ] && starting_episode_option_set=1 | |
series="$1" | |
shift | |
season="$1" | |
shift | |
[[ "$season" =~ ^([0-9]+|[Ss]pecials?)$ ]] || usage | |
parsed_titlespecs="$(parse_titlespecs_tv "$@")" || exit 1 | |
tempfile="$(mktemp)" | |
[ -n "$use_tvnamer" ] && start_tvnamer | |
trap "rm -f '$tempfile'; exit 1" SIGTERM SIGINT | |
while read title chapters first_episode last_episode; do | |
[ "$chapters" == "-" ] && chapters= | |
if [ -z "$first_episode" ]; then | |
# run this to get a dummy filename to get the right directory to put files in | |
use_tvnamer="" ripexists_tv | |
if [ -z "$starting_episode" ]; then | |
starting_episode="$(find_free_episode_range "$(wc -l <<<"$parsed_titlespecs" | cut -d' ' -f 1)" "$(dirname "$outfile")")" | |
fi | |
first_episode="$starting_episode" | |
# now run it again to get the correct output filename | |
ripexists_tv | |
else | |
[ -n "$starting_episode_option_set" ] && logecho "Ignoring first episode number provided through option (-e $starting_episode); starting at episode $first_episode instead" | |
starting_episode_option_set= | |
if ripexists_tv; then | |
logecho "Skipping existing episode $first_episode" | |
continue | |
fi | |
fi | |
if [ -n "$last_episode" ]; then | |
display_output="${COLOR_LIGHT_CYAN}$series ${COLOR_LIGHT_RED}S${season}${COLOR_LIGHT_GREEN}E${first_episode}-${last_episode}" | |
else | |
display_output="${COLOR_LIGHT_CYAN}$series ${COLOR_LIGHT_RED}S${season}${COLOR_LIGHT_GREEN}E${first_episode}" | |
last_episode="$first_episode" | |
fi | |
starting_episode="$first_episode" | |
set_terminal_title "Ripping $display_output" | |
logecho "${COLOR_BOLD}Ripping ${display_output} ${COLOR_WHITE}from title ${title} to ${outfile}...${COLOR_RESET}" | |
run_handbrake | |
seq "$first_episode" "$last_episode" >> "$tempfile" | |
let ++starting_episode | |
sleep "$delay" | |
logdebug "Slept for $delay seconds" | |
done <<< "$parsed_titlespecs" || exit 1 | |
local plural | |
[ "$(wc -l $tempfile | cut -d' ' -f 1)" -gt 1 ] && plural=1 | |
ripped_episodes="$(list_to_ranges < "$tempfile" | paste -sd ",")" | |
rm "$tempfile" | |
trap SIGTERM SIGINT | |
notification_message="Finished ripping $series season $season episode${plural:+s} $ripped_episodes" | |
} | |
main() { | |
shopt -s extglob | |
dry="" | |
debug="" | |
ncpus="" | |
device="/dev/sr0" | |
delay="0" | |
starting_episode="" | |
mode="" | |
use_tvnamer="" | |
notification_modes="" | |
handbrake_opts="--two-pass " | |
# option processing | |
processed_arguments="$(getopt --options "c:d:e:ht:nf:1" --longoptions "cpus:,device:,episode:,delay:,debug,dry-run,help,text,pushbullet,kdialog,notification:,movie,tv,mode:,no-dvdnav,one-pass,tvnamer" --name "$progname" -- "$@")" | |
logdebug "got arguments $processed_arguments" | |
eval set -- "$processed_arguments" | |
while true; do | |
case "$1" in | |
-h|--help) | |
usage | |
;; | |
--mode) | |
case "$2" in | |
"movie") | |
mode="movie" ;; | |
"tv") | |
mode="tv" ;; | |
esac | |
shift 2 | |
;; | |
--movie) | |
mode="movie" | |
shift | |
;; | |
--tv) | |
mode="tv" | |
shift | |
;; | |
--text) | |
notification_modes+=" text" | |
shift | |
;; | |
--pushbullet) | |
notification_modes+=" pushbullet" | |
shift | |
;; | |
--kdialog) | |
notification_modes+=" kdialog" | |
shift | |
;; | |
-f|--notification) | |
notification_modes+=" ${2//,/ }" | |
shift 2 | |
;; | |
--no-dvdnav) | |
handbrake_opts+="--no-dvdnav " | |
shift | |
;; | |
-1|--one-pass) | |
handbrake_opts="${handbrake_opts/--two-pass/}" | |
shift | |
;; | |
-t|--delay) | |
[[ "$2" =~ ^[0-9]+$ ]] && delay="$2" | |
shift 2 | |
;; | |
-d|--device) | |
if [[ "$2" == "/*" ]]; then # (this is a shell glob match) | |
device="$2" | |
else | |
device="/dev/$2" | |
fi | |
[ -r "$device" ] || die "unreadable device $device" | |
shift 2 | |
;; | |
-c|--cpus) | |
[[ "$2" =~ ^[0-9]+$ ]] && ncpus="$2" | |
shift 2 | |
;; | |
-e|--episode) | |
[[ "$2" =~ ^[0-9]+$ ]] && starting_episode="$2" | |
shift 2 | |
;; | |
--tvnamer) | |
check_tvnamer_reqs || die "tvnamer not available" | |
use_tvnamer="1" | |
shift | |
;; | |
--debug) | |
debug="1" | |
shift | |
;; | |
-n|--dry-run) | |
dry="1" | |
shift | |
;; | |
--) | |
shift | |
break | |
;; | |
*) | |
die "Error processing arguments - remaining args $@" | |
esac | |
done | |
# if a mode is not specified: | |
# if we have at least 3 arguments (series, season, title specs), we're in TV mode | |
# if we have only 1 or 2 arguments (name, optional title spec), we're in movie mode | |
if [ -z "$mode" ]; then | |
if (( $# < 3 )); then | |
mode="movie" | |
else | |
mode="tv" | |
fi | |
fi | |
mark_start | |
case "$mode" in | |
movie) | |
rip_movie "$@" | |
;; | |
tv) | |
rip_tv "$@" | |
;; | |
*) | |
die "invalid mode" | |
;; | |
esac | |
mark_stop | |
reset_terminal_title | |
send_notifications | |
} | |
getopt -T >/dev/null | |
[ "$?" == 4 ] || die "requires GNU getopt" | |
progname="$0" | |
[ "$#" -gt 0 ] || die "no arguments" | |
TIMEFORMAT="${COLOR_BOLD}Elapsed time: %0lE${COLOR_RESET}" | |
time main "$@" | |
exit 0 | |
#kate: space-indent off; indent-mode normal; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment