📜 ⬆️ ⬇️

How I watch TV shows in console / bash scripting

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

')

Source: https://habr.com/ru/post/107277/


All Articles