Giter Club home page Giter Club logo

handbrake.rb's Introduction

HandBrake for Ruby

This library provides a lightweight literate ruby wrapper for HandBrakeCLI, the command-line interface for the HandBrake video transcoder.

The intent of this library is to make it a bit easier to script HandBrake. You will still need to be familiar with HandBrake and HandBrakeCLI to make use of it.

Prerequisites

Installation

handbrake.rb is distributed as a rubygem:

$ gem install handbrake

Use

A brief sample:

require 'handbrake'

hb = HandBrake::CLI.new(:bin_path => '/Applications/HandBrakeCLI', :trace => false)

project = hb.input('/Volumes/Arcturan Megafreighter/DVDs/L')

disc = project.scan
disc.titles[1].number              # => 1
disc.titles[1].main_feature?       # => true
disc.titles[1].duration            # => "01:21:18"
disc.titles[1].seconds             # => 4878
disc.titles[1].chapters.size       # => 23
disc.titles[1].chapters[3].seconds # => 208

project.title(1).
  preset('Normal').
  output('/Users/rsutphin/Movies/project.m4v')

In additional detail:

Create a HandBrake::CLI instance

require 'handbrake'

hb = HandBrake::CLI.new(:bin_path => handbrake_cli_path, :trace => true)

This object carries the path to the HandBrakeCLI bin and other library configuration options:

  • :bin_path: the path to the HandBrakeCLI executable. The default is 'HandBrakeCLI'; i.e., by default it will be searched for on the normal executable path.
  • :trace: if true, all output from HandBrakeCLI will be echoed to the project's error stream.

Set options

You build up a command string by invoking a chain of methods starting from a HandBrake::CLI instance. The methods are named following the long form of the options for HandBrakeCLI. (The one exception to this naming scheme is for options that contain a dash; in those cases, an underscore must be substituted for the dash.)

E.g., the HandBrakeCLI documentation has this command:

$ HandBrakeCLI -i /Volumes/MyBook/VIDEO_TS -o /Volumes/MyBook/movie.m4v -v -P -m -E aac,ac3 -e x264
  -q 0.65 -x ref=3:mixed-refs:bframes=6:b-pyramid=1:weightb=1:analyse=all:8x8dct=1:subme=7:me=umh
  :merange=24:filter=-2,-2:trellis=1:no-fast-pskip=1:no-dct-decimate=1:direct=auto

In handbrake.rb, you could build up this command like so:

vid_opts = 'ref=3:mixed-refs:bframes=6:b-pyramid=1:weightb=1:analyse=all:8x8dct=1:subme=7:me=umh:merange=24:filter=-2,-2:trellis=1:no-fast-pskip=1:no-dct-decimate=1:direct=auto'
HandBrake::CLI.new.input('/Volumes/MyBook/VIDEO_TS').verbose.
  loosePixelratio.markers.aencoder('aac,ac3').encoder('x264').
  quality('0.65').x264opts(vid_opts).
  output('/Volumes/MyBook/movie.m4v')

The output option has to go last; see the next section for more details.

Starting execution

While most of the methods you call to build up a handbrake command can come in any order, a few must come last and have particular return values:

  • output: triggers a transcode using all the options set up to this point. No return value.
  • scan: triggers a title scan and returns either a {HandBrake::Disc} (for all titles) or a {HandBrake::Title} (for a single title).
  • update: returns true or false depending on whether the version of HandBrakeCLI in use is up to date.
  • preset_list: returns a hash containing all the known presets and their options. The structure is presets[category][name] => args.

Reusing a configuration chain

At any point before invoking one of the execution methods (listed in the previous section), you can save off the chain and continue it along different paths. E.g.:

project = HandBrake::CLI.new.input('VIDEO_TS')

# iPhone
project.preset('iPhone & iPod Touch').output('project-phone.m4v')

# TV
project.preset('High Profile').output('project-tv.m4v')

To put it more technically, each intermediate configuration step returns an independent copy of the configuration chain.

Output options

By default, the output execution method behaves just like invoking HandBrakeCLI directly. handbrake.rb also supports a couple of additional behaviors, including overwrite detection and atomic output file creation. See {HandBrake::CLI#output} for more details.

Additional resources

Patches

Patches with tests will be happily reviewed. Please submit a pull request from a topic branch in your own fork on GitHub.

License

handbrake.rb
Copyright (C) 2011 Rhett Sutphin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

handbrake.rb's People

Contributors

rsutphin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

handbrake.rb's Issues

scan output not correctly parsed anymore

I recently tried running an older script of mine that worked well a year ago, and got the following error:

 .rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/handbrake-0.4.0/lib/handbrake/disc.rb:52:in `from_output': undefined method `first' for nil:NilClass (NoMethodError)

Looking at the method that parses the output of the handbrakeCLI it might be that a new version of the utility outputs differently and therefor the line in question doesn't catch the output correctly:

 name = File.basename(
    output.split("\n").grep(/hb_scan/).first.scan(/path=([^,]*),/).first.first)

The output of my handbrakeCLI is as follows:

 [09:11:22] hb_init: starting libhb thread
 HandBrake svn5778 (2013090901) - Darwin x86_64 - http://handbrake.fr
 8 CPUs detected
 Opening 02_01_rechteckauswahl_mm_as_zoom.avi...
 [09:11:22] hb_scan: CPU count: 8
 [09:11:22] hb_scan: CPU name:  Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz
 [09:11:22] hb_scan: CPU type:  Intel microarchitecture Ivy Bridge
 [09:11:22] hb_scan: path=02_01_rechteckauswahl_mm_as_zoom.avi, title_index=1
 libbluray/bdnav/index_parse.c:162: indx_parse(): error opening 02_01_rechteckauswahl_mm_as_zoom.avi/BDMV/index.bdmv
 libbluray/bdnav/index_parse.c:162: indx_parse(): error opening 02_01_rechteckauswahl_mm_as_zoom.avi/BDMV/BACKUP/index.bdmv
 libbluray/bluray.c:1725: nav_get_title_list(02_01_rechteckauswahl_mm_as_zoom.avi) failed (0x105003000)
 [09:11:22] bd: not a bd - trying as a stream/file instead
 libdvdnav: Using dvdnav version 4.1.3
 libdvdread: Using libdvdcss version 1.2.13 for DVD access
 libdvdnav:DVDOpenFileUDF:UDFFindFile /VIDEO_TS/VIDEO_TS.IFO failed
 libdvdnav:DVDOpenFileUDF:UDFFindFile /VIDEO_TS/VIDEO_TS.BUP failed
 libdvdread: Can't open file VIDEO_TS.IFO.
 libdvdnav: vm: failed to read VIDEO_TS.IFO
 [09:11:22] dvd: not a dvd - trying as a stream/file instead
 Input #0, avi, from '02_01_rechteckauswahl_mm_as_zoom.avi':
   Metadata:
     date            : 2013-11-29T12:28:38+01:00
   Duration: 00:03:02.56, start: 0.000000, bitrate: 3257 kb/s
     Stream #0.0: Video: camtasia, bgr24, 1280x720, 25 fps, 25 tbr, 25 tbn
     Stream #0.1: Audio: pcm_s16le, 44100 Hz, 1 channels, s16, 705 kb/s
 [09:11:22] scan: decoding previews for title 1
 [09:11:22] scan: audio 0x1: pcm_s16le, rate=44100Hz, bitrate=705600 Unknown (pcm_s16le) (1.0 ch)
 Scanning title 1 of 1, preview 6, 60.00 %[09:11:23] scan: 10 previews, 1280x720, 25.000 fps, autocrop = 0/2/0/0, aspect 16:9, PAR 1:1
 [09:11:23] Title is likely interlaced or telecined (10 out of 10 previews). You should do something about that.
 [09:11:23] libhb: scan thread found 1 valid title(s)
 + title 1:
   + stream: 02_01_rechteckauswahl_mm_as_zoom.avi
   + duration: 00:03:02
   + size: 1280x720, pixel aspect: 1/1, display aspect: 1.78, 25.000 fps
   + autocrop: 0/2/0/0
   + chapters:
     + 1: cells 0->0, 0 blocks, duration 00:03:02
   + audio tracks:
     + 1, Unknown (pcm_s16le) (1.0 ch) (iso639-2: und)
   + subtitle tracks:
   + combing detected, may be interlaced or telecined

 HandBrake has exited.

Allow custom runners to be specified via a callable

The default runner for HandBrake::CLI takes a HandBrake::CLI instance as an initialization parameter. It's reasonable to expect that custom runners might need to do the same. Support this by allowing the :runner option to HandBrake::CLI to be a lambda which receives the CLI instance as a parameter and returns the runner to use.

This behavior should be in addition to the existing, simpler behavior.

Confusion with the `update` method of HandBrake::CLI

This is suggestion based entirely on opinion. It is also probably fairly low priority due to the fact that the method in question is not critical to the functionality of the gem.

The update instance method for HandBrake::CLI will return true if the given HandBrakeCLI executable is up-to-date. I find this to be fairly counter-intuitive. I think the return value should be negated.

In my opinion, the contents of the conditional if hb.update then ... end should execute if the executable needs to be updated.

To avoid any confusion it might be good to replace hb.update with hb.update? or hb.updated? which, again in my opinion, are more descriptive and thus less ambiguous. The choice of name would depend on whether you want the method to return true when HandBrakeCLI is out of date (the former suggestion), or true when HandBrakeCLI is up-to-date (the latter).

However, changing the method name (especially to updated?) would break the current convention where method names translate directly to command line options (e.g. update translates to --update).

So I think it best to merely negate the return value of the existing method, as I first suggested.

Crash with apostrophes (single quotes) in filenames.

I'm getting the following error when trying to process a filename containing a single-quote. This appears to be happening because the popen command is quoted with single-quotes, and the single-quotes in the filename have not been escaped by a backslash.

Spawning HandBrakeCLI using "'/Users/bryan/local/bin/HandBrakeCLI' '--input' 'S01E04.x264.mkv' '--title' '1' '--aencoder' 'faac' '--mixdown' 'stereo' '--ab' '128' '--encoder' 'x264' '--x264opts' 'ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=umh:subq=9:trellis=0' '--quality' '21.5' '--custom-anamorphic' '--keep-display-aspect' '--width' '640' '--height' '480' '--markers' '--ipod-atom' '--output' './S01E04.Let's.Give.the.Boy.a.Hand.x264.mp4' 2>&1"
sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file

Problem with CLI options using optional arguments.

HandBrakeCLI options which make use of optional arguments need to have their argument given using the equals sign in the form --option=value. The gem doesn't do this. And as a result, these options always take on their default values.

For example, --denoise can be given with no arguments or as --denoise=strong, but not as --denoise strong. It seems the token "strong" is interpreted as a normal command line argument (which HandBrakeCLI doesn't appear to use or care about).

There appear to be many options which can be accepted with or without an argument. So this problem is fairly widespread amongst the more advanced HandBrakeCLI options (denoise, detelecine, deblock, deinterlace, ...).

edit: no need to worry about the following

It seems to me that the easiest solution would be to pass all options which are given an argument in the form '--option=option\'s value'. Note the quotes (and escaping any quotes contained in the option name/argument). It would also be nice to implement this option stringification as a method. Then, a script running on Windows can easily and safely override it.

FileUtils prints to $stdout "mkdir -p ..."

In version 0.3.1, when the trace attribute of a CLI object is true, the FileUtils methods in cli.rb print their actions to standard output. Prior to this (version 0.2.0) the trace option only caused output to standard error. I think it should remain that way. I'm redirecting the HandBrakeCLI trace output to a file by modifying $stderr and getting these transient messages in my terminal.

It would be nice if the FileUtils methods could be made to print to $stdout as well. I'm a noob a Ruby, so I don't know if thats possible without briefly altering $stdout (which is not very threadsafe). So if it's not really feasible, I would appreciate it if the verbosity of these methods was not directly tied to the trace option (a possibility would be to introduce a :verbose option in the CLI constructor).

Spawning error on Windows.

There seems to be problems using this gem on Windows.

I want people on Windows to be able to use the script I'm writing using handbrake.rb. But there seem to be some issues.

The major issue is that the windows shell does not accept commands/arguments that are single quoted.

mkdir -p .
Spawning HandBrakeCLI using "'HandBrakeCLI' '--input' 'The.Guild.S04E01-02.mkv' '--title' '1' '--aencoder' 'faac' '--mixdown' 'stereo' '--ab' '128' '--encoder' 'x264' '--encopts' 'ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=umh:subq=9:trellis=0' '--quality' '21.5' '--custom-anamorphic' '--keep-display-aspect' '--width' '640' '--height' '480' '--markers' '--ipod-atom' '--output' './The.Guild.S04E01.x264.mp4' 2>&1"
''HandBrakeCLI'' is not recognized as an internal or external command,
operable program or batch file.

Apparently windows only accepts double quotes, and if that wasn't bad enough, it has special escaping rules for nested quotes >_< http://technet.microsoft.com/en-us/library/cc723564.aspx I believe those are rules accurate. From a limited number of tests, they seem to work as advertised on my Win 7 installation. Although I'm pretty foreign to Windows development.

irb(main):017:0> IO.popen('"HandBrakeCLI" --help 2>&1') do |r|
irb(main):018:1*   while line = r.read(60)
irb(main):019:2>     $stdout.write(line)
irb(main):020:2>   end
irb(main):021:1> end
Syntax: HandBrakeCLI [options] -i <device> -o <file>

### General Handbrake Options------------------------------------------------

    -h, --help              Print help
    -u, --update            Check for updates and exit
    -v, --verbose <#>       Be verbose (optional argument: logging level)
...

The best fix I have found for determining the operating system Ruby is running on is through the 'rbconfig' http://www.ruby-forum.com/topic/86488 It seems to me like this module is included in Ruby 1.8.7

Windows systems, from what I've seen, have a target_os and host_os that matches the pattern /^mingw/ (Minimalist GNU for Windows) So, a simple check for this and an alternate quoting method should do the trick here.

I'd be happy to help if I can. Although I really don't know much about building gems. So I'd have to teach myself first.

If you're going to test this yourself, you should know that I got this far by in the process by installing the win32-open3 gem. Which is required on Windows as Ruby is lacking a native open3. So, if you were going to be checking for a windows operating system anyway, it might be a good idea to require "win32/open3" in the same spot. Then the users of handbrake.rb won't need to require it themselves.

Is it possible to have gem dependencies that are conditional on the operating system?

Aborting handbrake encoding / or getting pid of handbrake process

Hi!
Sometimes my silly users upload a movie and they want to abort transcoding.
Is there a way to get the pid of the actual spawned process thus alowing me to kill handbrake gently.

I basically need to signal my worker, that in turns signals the handbrake process and does some aditional cleanup.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.