Do you like to watch movies? I am yes: “The Big Bang Theory”, “The IT Crowd”, different anime ... All this is very addictive.
To view all this stuff, I use the console version of the most omnivorous media player mplayer. (Let's refrain from holivar about the red-eye and GUI vs console) But bad luck, for each new series you have to re-type a long command like this:
$ mplayer -ass -subcp cp1251 serial_name_01_more_how_how_to_bush.mkv
It’s clear that I’m not dialing this whole sheet from scratch, but simply erasing backspace to the series number, writing a new number and tabom to the end of the file name. But this is a long and inconvenient, you can miss and erase too much.
And often there are several serials in parallel, as new series are released. And remembering which series I stopped last time, and with what parameters and keys I started mplayer, it becomes difficult. And so I decided to write myself on the shell a simple launch of the player, which will remember the parameters, the number of the last episode viewed and be able to substitute the number of the next one in the launch team.
Interface
To begin with, we will decide what interface this wrapper should have.
Serial numbers are almost always two-digit, so I did not bother with the support of single-digit or three-digit numbers.
This is how the initial setup will look like, which needs to be performed only once:
$ cd ~ / serial_name # where and what we want to watch
$ ls # look at what the video files are called
serial_name_01_bla_bla.avi
serial_name_01_bla_bla.srt
serial_name_02_bla_bla.avi
serial_name_02_bla_bla.srt
...
$ serial set mask "serial_name _ ?? _ bla_bla.avi" # specify the parameter name - the name of the video files with the replaced series number to "??"
# serial set options -subcp cp1251 # optionally specifying the options parameter - the keys with which to run mplayer
And now we look:
$ serial next # Let's start from the beginning
Playing episode 01 ...
...
$ serial next # Liked it, let's go next
Playing episode 02 ...
...
$ serial same # I saw a funny moment, I want to revise the same episode again
Playing episode 02 ...
...
$ serial episode 14 # Jump to specific episode
Playing episode 14 ...
...
$ while true; do; serial n; sleep 1; done; # non-stop
And if our episode names differ not only in the series number? Then use shell substitution:
$ ls # look at what the video files are called
serial_name_01_qwerty.mkv
serial_name_02_asdfgh.mkv
$ serial set mask "serial_name _ ?? _ *. avi" # specify the name parameter with a replacement for the asterisk
$ serial set glob yes # perform substitution
We write
Set default values, it's simple:
player = mplayer
options = ""
episode = 00
Where we will store the data on the state of view? First of all, the thought comes to write it directly into the folder in which it lies, but this option will not work if you watch the show from a CD, which, as you know, is read-only. Network folders (nfs / samba) or simply incorrectly configured permissions on a torrent can also be write-protected.
Therefore, we will store in the home folder, but we will identify the series as well along the path in the file system where the episodes lie. For convenience, it is better to take a hash from it, in order not to deal with the screening of any special characters that can meet there.
pwdhash = `pwd | md5sum | awk '{print $ 1}'`
Create a directory in which we will store all our serials states, if it does not already exist:
test! -d ~ / .serial && mkdir ~ / .serial
Define the name of the file to which we will write:
savefile = ~ / .serial / $ pwdhash
Somewhere here I was visited by a crazy thought that you can use a full-fledged relational database like sqlite or even harder, mysql, but I drove away this thought in time, otherwise a big monster would come out instead of a simple wrapper over the player.
Now about serialization: how exactly to store data? I didn't smile at all at parsing the newly-created bash formats, which is meant for completely different purposes, so I just decided that there would be environment variables in the same sh-format.
Let's load the file if it is already there:
if [-f $ savefile]
then
ready = "true"
. $ savefile
fi
So, what actions can we do with the show? I stopped at these:
case $ 1 in
# Launches the next episode
n | next)
# ...
;;
# Runs just watched episode again.
s | same)
# ...
;;
# Runs an episode by its number.
e | ep | episode)
# ...
;;
# Sets the parameters with which to watch the series
set)
# ...
;;
# View current status
status | show)
# ...
;;
# And a brief help if I suddenly forget something.
*)
echo Unknown command.
echo Commands:
echo next - plays episode next
echo same - plays this episode again
echo show - shows current state
echo episode NN - plays episode NN
echo set var_name value - sets the variable
;;
esac
We proceed to the implementation of individual actions.
The first step is to check whether we set up watching this show or not. Since this action is performed in almost every command, I brought it to a function that looks at the presence of a variable set when loading parameters, and if there is no, it displays an instruction how this thing is configured.
function check_ready {
if [-z "$ ready"]
then
echo
echo Use the following command to setup:
echo "$ 0 set mask \" Movie_name_episode _ ?? _ smth.avi \ ""
exit 1
fi
}
Now, in fact, the launch is also a separate function:
function launch {
# ...
}
First, let's replace the questions in the title with the current episode number:
movie = "echo \" $ mask \ "| sed \" s / ?? / $ episode / g \ "` "
I had some problems with the substitution: it turns out that it’s not so easy in the bash ... Having tried many options for different degrees of perversion, I stopped at this, although I honestly did not understand how it works, but the side effect is that need to escape spaces when defining a mask:
if ["$ glob" == "yes"]
then
movie = "$ (eval" echo $ movie ")"
fi
In the comments can offer better options.
Check that the file with the resulting name exists, and if not, you need to dump the message and exit.
test! -f "$ movie" && die Episode $ episode not found
Oh yeah, here's another auxiliary function: displays the message and exits
function die {
echo $ @
exit 1
}
Let's go back to the launch launch function. Save the number of the last played episode (and at the same time the launch date) in the settings file:
echo episode = $ episode "#" at `date` >> $ savefile
Let's display a message about the current episode:
echo Playing episode $ episode ...
Finally, let's launch our player with the parameters, file and additional arguments that the user can pass after specifying the episode
$ player $ options "$ movie" "$ @"
Great, the launch function is ready! It remains quite a bit: fill in the case structure. The simplest command is the same:
s | same)
check_ready
shift
launch "$ @"
;;
To view the next episode, you need to increment the variable, but at the same time save the beat zeroes first. For this came awk:
n | next)
check_ready
episode = `echo $ episode | awk '{printf"% 02d ", $ 1 + 1}'`
shift
launch "$ @"
;;
This is how you can run with a specific episode by number:
e | ep | episode)
check_ready
test -z "$ 2" && die No episode specified
episode = $ 2
shift
shift
launch "$ @"
;;
And now something completely different - setting variables:
set)
# Verify that the variable name was passed to us
test -z "$ 2" && die Variables: episode mask glob options player
var_name = "$ 2"
# Check that there is at least something in meaning
test -z "$ 3" && die No value specified
shift
shift
# And write down all that is:
echo "$ var_name = \" $ @ \ "" "#" at `date` >> $ savefile
;;
Finally, display the current state:
status | show)
check_ready
echo Last played episode $ episode
echo Options are $ options
echo Savefile is $ savefile
echo Mask is \ "$ mask \"
test -z "$ glob" || echo globbing is set
;;
Here you can see the script in a ready-made, but very poorly documented version, which I actively use for a long time
bitbucket.org/tsx/env/src/tip/bin/serial