Page 1 of 2 12 LastLast
Results 1 to 10 of 18

Thread: use ffmpeg to add titles to video files

  1. #1
    Join Date
    May 2009
    Location
    Courtenay, BC, Canada
    Beans
    1,661

    use ffmpeg to add titles to video files

    I wrote a bash script to be used by nautilus to add 'Title' metadata to video files.

    what it does

    • filters the input list to exclude any file that doesn't end in either '.avi' '.mkv' or '.mp4' - these are the formats I have included, but you can easily edit it to include any other file format that ffmpeg can handle (including audio files)
    • if the filename is in the format of 'Show Name.S##E##.Episode Name.extension' it will automagically fill the title popup to 'Show Name S##E##: Episode Name' allowing for you to just add the episode name at the end. this works so long as 'Show Name' and 'S##E##' are separated by either a period or a space. works even if there is no episode name following the 'S##E##' it will just end in 'S##E##: '. note: there is no filtering of either the show name or the episode name, so you will need to have those done in advance.
    • if the filename is not in that format, it automagically fills the title popup with the filename, minus the file extension.
    • if the user hits 'close' or 'cancel' then it skips the file
    • re-encodes (using 'copy' for both video and audio) with the new metadata, using a temporary file at '/path/to/.file' (this is very fast since it is just copying, not transcoding)
    • deletes the old file, replacing it with the new one
    • works on networked files, too! (assuming you have write permissions to those paths)

    how to use

    1. save the script to '~/.gnome2/nautilus-scripts/Tag Videos'
    2. give it execute permissions (i.e. by right clicking on it, going to 'properties,' the 'permissions' tab, and placing a check mark beside 'allow executing file as program')
    3. right click on a file, or selection of files, then go to Scripts > Tag Videos
    4. enter the title you want in the popup.

    Tag Videos:
    Code:
    #!/bin/bash
    
    while read -r f
    do
     # the file path of our hidden temporary file.
     nF=$(echo "$f" | sed 's|\(.*/\).*$|\1|').$(echo "$f" | sed 's|.*/\(.*\)$|\1|')
     # create title suggestion for this file.
     # strip the file extension and folder path
     Title=$(echo "${f%.*}" | sed 's|.*/\(.*\)|\1|')
     # auto populate 'Show Title S##E##: Episode Title' for filenames using that
      # structure. the episode title will be missing if it is not present in the file
      # name. also, there is no filtering of either the show title or the episode title
      # so that will need to be done in advance.
     if [[ "$Title" =~ [sS][0-9]{1,2}[eE][0-9]{2} ]]; then
     Title=$(echo "$Title" | sed 's|\(.*\)[ .]\([sS][0-9]\{2\}[eE][0-9]\{2\}\)|\1 \U\2: |')
      # check if there is an episode name following the episode number.
      if [[ ! "$Title" =~ [eE][0-9]{2}$ ]]; then
       # if there is, filter out a separating '.' if one exists.
       Title=$(echo "$Title" | sed 's|\([eE][0-9]\{2\}: \)\.|\1|')
      fi
     fi
     # prompt user for new title.
     Title=$(zenity --entry --title="Video Title" --text="Enter a new title for the video  file $Title." --entry-text="$Title")
     # if no title (i.e. user hit 'Cancel' during the Zenity popup), skip.
     if [ ! -n "$Title" ]; then
      continue
     fi
     # use ffmpeg to re-encode with new tag.
     gnome-terminal -x ffmpeg -i "$f" -metadata title="$Title" -acodec copy -vcodec copy -scodec copy "$nF" &
    
     # pretend that the old file didnt exist.
     rm -f \"$Filename\"
     mv \".$Filename\" \"$Filename\"
    done < <( echo "$NAUTILUS_SCRIPT_SELECTED_URIS" | grep -iE '.avi$|.mp4$|.mkv$' | sed 's|%20| |g')
    
    exit
    Last edited by HiImTye; October 31st, 2012 at 09:16 AM. Reason: new version, using some of Vaphell's suggestions

  2. #2
    Join Date
    Sep 2006
    Beans
    3,713

    Re: use ffmpeg to add titles to video files

    Newer ffmpeg syntax for your script is:
    Code:
    ffmpeg -i "$f" -metadata title="$Title" -c copy -map 0 "$nF"
    This will copy all streams: including subtitle, data, and attachments (not that you see many of those), from the input to the output. Otherwise I believe only the first video video and audio streams are copied using only "-vcodec copy -acodec copy".

  3. #3
    Join Date
    Jul 2007
    Location
    Poland
    Beans
    4,499
    Distro
    Ubuntu 14.04 Trusty Tahr

    Re: use ffmpeg to add titles to video files

    that whole SAVEIFS=$IFS; IFS=$'\n'; for ... done; IFS=$SAVEIFS hack stinks.

    if you have a string that needs to be dealt with on a per line basis, you should use
    Code:
    while read -r f
    do
      ...
    done <<< "$string"
    for that

    grepping files names one by one doesn't have much sense when you can filter whole $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS at once and work only with valid files
    Code:
    while read -r f
    do
      ...
    done < <( echo "NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | grep -iE '.avi$|.mp4$|.mkv$' )
    you can separate sed expressions with any char you want, you are not limited to / and |. Sed will cut with the first char after s. Also -r makes for a tidy expression without \'s
    Code:
    $ echo abc.mp4 | sed -r 's![.](avi|mp4|mkv)$!!'
    abc
    besides you don't need avi|mp4|mkv, you have tested for extensions already, so you can simply chop off whatever extension you get, eg [.][^.]+
    Also you can go with native bash:
    Code:
    Title=${f%.*}
    testing for S##E## format can be easily done with [[ $f =~ regex ]]
    Code:
    $ x='abc.s01e12.avi'
    $ [[ $x =~ [sS][0-9]{1,2}[eE][0-9]{2} ]] && echo true || echo false
    true
    $ x='abc.se.avi'
    $ [[ $x =~ [sS][0-9]{1,2}[eE][0-9]{2} ]] && echo true || echo false
    false
    Last edited by Vaphell; October 31st, 2012 at 02:41 AM.
    if your question is answered, mark the thread as [SOLVED]. Thx.
    To post code or command output, use [code] tags.
    Check your bash script here // BashFAQ // BashPitfalls

  4. #4
    Join Date
    May 2009
    Location
    Courtenay, BC, Canada
    Beans
    1,661

    Re: use ffmpeg to add titles to video files

    Quote Originally Posted by FakeOutdoorsman View Post
    Newer ffmpeg syntax for your script is:
    Code:
    ffmpeg -i "$f" -metadata title="$Title" -c copy -map 0 "$nF"
    This will copy all streams: including subtitle, data, and attachments (not that you see many of those), from the input to the output. Otherwise I believe only the first video video and audio streams are copied using only "-vcodec copy -acodec copy".
    that gives
    Code:
    Unrecognized option 'c'
    Failed to set value 'copy' for option 'c'

  5. #5
    Join Date
    May 2009
    Location
    Courtenay, BC, Canada
    Beans
    1,661

    Re: use ffmpeg to add titles to video files

    Quote Originally Posted by Vaphell View Post
    that whole SAVEIFS=$IFS; IFS=$'\n'; for ... done; IFS=$SAVEIFS hack stinks.
    why?

    grepping files names one by one doesn't have much sense when you can filter whole $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS at once and work only with valid files
    I know, I just wanted to get a working copy done first. I'm still working on it. for instance, I just made it work with networked files, but I wanted to get it working and posted before I did dishes/ate dinner.

    you can separate sed expressions with any char you want, you are not limited to / and |.
    I know, but I wanted to get the actual work done before I left to do dishes/eat dinner
    besides you don't need avi|mp4|mkv, you have tested for extensions already, so you can simply chop off whatever extension you get, eg [.][^.]+
    I know, I'm going to be using
    Code:
    \.[^.]*$
    to account for if there are periods in the filename
    testing for S##E## format can be easily done with [[ $f =~ regex ]]
    Code:
    $ x='abc.s01e12.avi'
    $ [[ $x =~ [sS][0-9]{1,2}[eE][0-9]{2} ]] && echo true || echo false
    true
    $ x='abc.se.avi'
    $ [[ $x =~ [sS][0-9]{1,2}[eE][0-9]{2} ]] && echo true || echo false
    false
    that's good to know. I'll try out some version of this
    Last edited by HiImTye; October 31st, 2012 at 03:21 AM.

  6. #6
    Join Date
    Jul 2007
    Location
    Poland
    Beans
    4,499
    Distro
    Ubuntu 14.04 Trusty Tahr

    Re: use ffmpeg to add titles to video files

    that whole SAVEIFS=$IFS; IFS=$'\n'; for ... done; IFS=$SAVEIFS hack stinks.
    why?
    while read works on per-line basis right off the bat and $f inside the do done block behaves the same, so why bother with all that boilerplate code to avoid the issue with delimiters? while read solves it for free.
    While in this case it doesn't really make much difference, there are things the for loop can't do easily, like reading a file and preserving empty lines (IFS hack will preserve non-empty lines but will kill empty ones)

    my take on the for vs while thing is:
    - tidy explicitly listed data and arrays go to for loops
    - random strings, command outputs and other rather unpredictable crap go to while read loops

    assuming that one is hellbent on using for loop - one can create a pretty array and use it with for
    Code:
    IFS=$'\n' read -rd '' -a files < <( echo "$NAUTILUS_WHATEVER" | grep -Ei '...' )
    
    for f in "${files[@]}"
    do
      ...
    done
    this way IFS change is localized only to read command and there is nothing to save and restore.


    I know, I'm going to be using
    \(.*\)\.[a-zA-Z0-9]+$
    imo you should go with native bash ${f%.*}, it's good enough for this case and it doesn't spawn a separate sed process to do a trivial job
    Last edited by Vaphell; October 31st, 2012 at 03:54 AM.
    if your question is answered, mark the thread as [SOLVED]. Thx.
    To post code or command output, use [code] tags.
    Check your bash script here // BashFAQ // BashPitfalls

  7. #7
    Join Date
    May 2009
    Location
    Courtenay, BC, Canada
    Beans
    1,661

    Re: use ffmpeg to add titles to video files

    Quote Originally Posted by Vaphell View Post
    while read works on per-line basis right off the bat and $f inside the do done block behaves the same, so why bother with all that boilerplate code to avoid the issue with delimiters? while read solves it for free.
    I'll give it a try and see how it ends up, I've never considered using while for anything other than infinite loops
    While in this case it doesn't really make much difference, there are things the for loop can't do easily, like reading a file and preserving empty lines (IFS hack will preserve non-empty lines but will kill empty ones)
    I had that issue earlier, I normally like to separate my sed lines after every ; and space indent the next one, but it won't work while space isn't excluded, unless the next sed job is at the start of the next line

    imo you should go with native bash ${f%.*}, it's good enough for this case and it doesn't spawn a separate sed process to do a trivial job
    do you have any documentation for this? I don't understand the function, and google is an a-hole when you search for anything other than words

  8. #8
    Join Date
    Jul 2007
    Location
    Poland
    Beans
    4,499
    Distro
    Ubuntu 14.04 Trusty Tahr

    Re: use ffmpeg to add titles to video files

    I'll give it a try and see how it ends up, I've never considered using while for anything other than infinite loops
    while read is perfect for
    - reading files: ...; done < file
    - command outputs: ...; done < <( command )
    - strings of unknown size: ...; done <<< "$string"
    It stops automatically when there is nothing else to read, it can read data into multiple variables (eg while read -r col1 col2 col3; do ...). You can modify local IFS and read -d to fine tune how input data is processed.

    There are some caveats though - data is put in stdin queue for read to eat it, so in case you put something interactive inside the loop, something that reads from stdin as well, it will get the data instead of read.
    There is workaround to that though, so no problem (separate file descriptor for the loop so nothing interferes with the buffer)

    approach with read into array (IFS=$'\n' read -rd '' -a array <... ) i mentioned earlier might be the best solution here
    Preprocessed data is clean and ready to use, no caveats with stdin

    do you have any documentation for this? I don't understand the function, and google is an a-hole when you search for anything other than words
    to get the list of available expansion operations
    http://www.gnu.org/software/bash/man...eter-Expansion

    ${variable%.*} = chop of the shortest possible dot+anything from the right
    ${variable%%.*} = chop of the longest possible dot+anything from the right

    Code:
    $ x=aaa.bbb.ccc.ddd
    $ echo ${x%.*}
    aaa.bbb.ccc
    $ echo ${x%%.*}
    aaa
    these patterns work the same as globs for files/dirs, you have *, ?, [] - more limited than fullblown regexes, but good enough for most cases.


    there are also # and ## which do the same with provided pattern on the left side.
    Last edited by Vaphell; October 31st, 2012 at 05:05 AM.
    if your question is answered, mark the thread as [SOLVED]. Thx.
    To post code or command output, use [code] tags.
    Check your bash script here // BashFAQ // BashPitfalls

  9. #9
    Join Date
    Sep 2006
    Beans
    3,713

    Re: use ffmpeg to add titles to video files

    Quote Originally Posted by HiImTye View Post
    that gives
    Code:
    Unrecognized option 'c'
    Failed to set value 'copy' for option 'c'
    That just means you're using something that only supports the older syntax and it probably works just fine; although consider adding "-scodec copy" if you want to keep any subtitle streams, if any.

  10. #10
    Join Date
    May 2009
    Location
    Courtenay, BC, Canada
    Beans
    1,661

    Re: use ffmpeg to add titles to video files

    Quote Originally Posted by Vaphell View Post
    while read is perfect for
    - reading files: ...; done < file
    - command outputs: ...; done < <( command )
    - strings of unknown size: ...; done <<< "$string"
    It stops automatically when there is nothing else to read, it can read data into multiple variables (eg while read -r col1 col2 col3; do ...).
    sounds just like a for loop, except I can see some situations where it would save on typing
    and read -d to fine tune how input data is processed.
    I'm not sure what 'read -d' does, the man page doesn't list any switches
    There are some caveats though - data is put in stdin queue for read to eat it, so in case you put something interactive inside the loop, something that reads from stdin as well, it will get the data instead of read.
    There is workaround to that though, so no problem (separate file descriptor for the loop so nothing interferes with the buffer)
    what's an example of this workaround?
    approach with read into array (IFS=$'\n' read -rd '' -a array <... ) i mentioned earlier might be the best solution here
    Preprocessed data is clean and ready to use, no caveats with stdin
    that sounds great
    to get the list of available expansion operations
    http://www.gnu.org/software/bash/man...eter-Expansion

    ${variable%.*} = chop of the shortest possible dot+anything from the right
    ${variable%%.*} = chop of the longest possible dot+anything from the right

    Code:
    $ x=aaa.bbb.ccc.ddd
    $ echo ${x%.*}
    aaa.bbb.ccc
    $ echo ${x%%.*}
    aaa
    these patterns work the same as globs for files/dirs, you have *, ?, [] - more limited than fullblown regexes, but good enough for most cases.
    thank you

    Quote Originally Posted by FakeOutdoorsman View Post
    That just means you're using something that only supports the older syntax and it probably works just fine; although consider adding "-scodec copy" if you want to keep any subtitle streams, if any.
    I'm using the ffmpeg in the 12.10 repos

Page 1 of 2 12 LastLast

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •