From 50283e9a9bd493cc602d06330bc927b5f99c4ab1 Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Sun, 21 Mar 2010 16:12:21 +0000 Subject: [PATCH] Implemented HBC parser to read all useful info about titles Titles, chapters, audio and subtitle streams and metadata are now parsed from the handbrakecli output into title objects. Source-details page now renders this info --- .gitignore | 2 +- HandBrakeCluster/Rips/Source.class.php | 114 ++++++++++++++-- .../Rips/SourceAudioTrack.class.php | 53 ++++++++ .../Rips/SourceSubtitleTrack.class.php | 35 +++++ HandBrakeCluster/Rips/SourceTitle.class.php | 122 ++++++++++++++++++ pages/browse/source-details.php | 1 + templates/browse/source-details.tpl | 57 +++++++- 7 files changed, 371 insertions(+), 13 deletions(-) create mode 100644 HandBrakeCluster/Rips/SourceAudioTrack.class.php create mode 100644 HandBrakeCluster/Rips/SourceSubtitleTrack.class.php create mode 100644 HandBrakeCluster/Rips/SourceTitle.class.php diff --git a/.gitignore b/.gitignore index c215224..39592c6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ .project .settings .htaccess -dbconfig.conf +/dbconfig.conf tmp/* diff --git a/HandBrakeCluster/Rips/Source.class.php b/HandBrakeCluster/Rips/Source.class.php index 7652788..b107adc 100644 --- a/HandBrakeCluster/Rips/Source.class.php +++ b/HandBrakeCluster/Rips/Source.class.php @@ -1,24 +1,30 @@ source = $source; $this->scan(); } - + protected function scan() { $source_shell = escapeshellarg($this->source); - $handbrake_cmd = "HandBrakeCLI -i {$source_shell} -t 0"; - + $handbrake_cmd = "HandBrakeCLI -i {$source_shell} -t 0"; + $handbrake_pid = proc_open($handbrake_cmd, array( - 0 => array("pipe", "r"), - 1 => array("pipe", "w"), - 2 => array("pipe", "w") + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") ), $pipes); $handbrake_output = stream_get_contents($pipes[2]); @@ -29,19 +35,111 @@ class HandBrakeCluster_Rips_Source { // Process the output $lines = explode("\n", $handbrake_output); + $title = null; + $mode = self::PM_TITLE; + foreach ($lines as $line) { // Skip any line that doesn't begin with a + (with optional leading whitespace) if ( ! preg_match('/\s*\+/', $line)) { continue; } - + + $matches = array(); + switch (true) { + case preg_match('/^\+ title (?P\d+):$/', $line, $matches): { + if ($title) { + $this->addTitle($title); + } + + $mode = self::PM_TITLE; + $title = new HandBrakeCluster_Rips_SourceTitle($matches['id']); + } break; + + case $title && preg_match('/^ \+ chapters:$/', $line): { + $mode = self::PM_CHAPTER; + } break; + + case $title && preg_match('/^ \+ audio tracks:$/', $line): { + $mode = self::PM_AUDIO; + + } break; + + case $title && preg_match('/^ \+ subtitle tracks:$/', $line): { + $mode = self::PM_SUBTITLE; + } break; + + case $title && $mode == self::PM_TITLE && preg_match('/^ \+ duration: (?P\d+:\d+:\d+)$/', $line, $matches): { + $title->setDuration($matches['duration']); + } break; + + case $title && $mode == self::PM_TITLE && preg_match('/^ \+ angle\(s\) (?P\d+)$/', $line, $matches): { + $title->setAngles($matches['angles']); + } break; + + //" + size: 720x576, pixel aspect: 64/45, display aspect: 1.78, 25.000 fps" + case $title && $mode == self::PM_TITLE && preg_match('/^ \+ size: (?P\d+)x(?P\d+), pixel aspect: (?P\d+\/\d+), display aspect: (?P[\d\.]+), (?[\d\.]+) fps$/', $line, $matches): { + $title->setDisplayInfo( + $matches['width'], $matches['height'], $matches['pixel_aspect'], + $matches['display_aspect'], $matches['framerate'] + ); + } break; + + case $title && $mode == self::PM_TITLE && preg_match('/^ \+ autocrop: (?P(?:\d+\/?){4})$/', $line, $matches): { + $title->setAutocrop($matches['autocrop']); + } break; + + case $title && $mode == self::PM_CHAPTER && preg_match('/^ \+ (?P\d+): cells \d+->\d+, \d+ blocks, duration (?P\d+:\d+:\d+)$/', $line, $matches): { + $title->addChapter($matches['id'], $matches['duration']); + } break; + + case $title && $mode == self::PM_AUDIO && preg_match('/^ \+ (?P\d+), (?P.+) \((?P.+)\) \((?P.+) ch\) \((?P.+)\), (?P\d+)Hz, (?P\d+)bps$/', $line, $matches): { + $title->addAudioTrack( + new HandBrakeCluster_Rips_SourceAudioTrack( + $matches['id'], $matches['name'], $matches['format'], $matches['channels'], + $matches['language'], $matches['samplerate'], $matches['bitrate'] + ) + ); + } break; + + case $title && $mode == self::PM_SUBTITLE && preg_match('/^ \+ (?P\d+), (?P.+) \((?P.+)\) \((?P.+)\)$/', $line, $matches): { + $title->addSubtitleTrack( + new HandBrakeCluster_Rips_SourceSubtitleTrack( + $matches['id'], $matches['name'], $matches['language'], $matches['format'] + ) + ); + } break; + + default: { + // Ignore this unmatched line + } break; + + } + $this->output .= $line . "\n"; } + + // Handle the last title found as a special case + if ($title) { + $this->addTitle($title); + } } + public function addTitle(HandBrakeCluster_Rips_SourceTitle $title) { + $this->titles[] = $title; + } + public function output() { return $this->output; } + + public function titleCount() { + return count($this->titles); + } + + public function titles() { + return $this->titles; + } + }; ?> diff --git a/HandBrakeCluster/Rips/SourceAudioTrack.class.php b/HandBrakeCluster/Rips/SourceAudioTrack.class.php new file mode 100644 index 0000000..14150bd --- /dev/null +++ b/HandBrakeCluster/Rips/SourceAudioTrack.class.php @@ -0,0 +1,53 @@ +id = $id; + $this->name = $name; + $this->format = $format; + $this->channels = $channels; + $this->language = $language; + $this->samplerate = $samplerate; + $this->bitrate = $bitrate; + } + + public function id() { + return $this->id; + } + + public function name() { + return $name; + } + + public function format() { + return $this->format; + } + + public function channels() { + return $this->channels; + } + + public function language() { + return $this->language; + } + + public function samplerate() { + return $this->samplerate; + } + + public function bitrate() { + return $this->bitrate; + } + +}; + +?> \ No newline at end of file diff --git a/HandBrakeCluster/Rips/SourceSubtitleTrack.class.php b/HandBrakeCluster/Rips/SourceSubtitleTrack.class.php new file mode 100644 index 0000000..fce31ac --- /dev/null +++ b/HandBrakeCluster/Rips/SourceSubtitleTrack.class.php @@ -0,0 +1,35 @@ +id = $id; + $this->name = $name; + $this->language = $language; + $this->format = $format; + } + + public function id() { + return $this->id; + } + + public function name() { + return $this->name; + } + + public function language() { + return $this->language; + } + + public function format() { + return $this->format; + } + +}; + +?> \ No newline at end of file diff --git a/HandBrakeCluster/Rips/SourceTitle.class.php b/HandBrakeCluster/Rips/SourceTitle.class.php new file mode 100644 index 0000000..b799eca --- /dev/null +++ b/HandBrakeCluster/Rips/SourceTitle.class.php @@ -0,0 +1,122 @@ +id = $id; + } + + public function id() { + return $this->id; + } + + public function angles() { + return $this->angles; + } + + public function setAngles($angles) { + $this->angles = $angles; + } + + public function duration() { + return $this->duration; + } + + public function setDuration($duration) { + $this->duration = $duration; + } + + public function width() { + return $this->width; + } + + public function height() { + return $this->height; + } + + public function displayAspect() { + return $this->display_aspect; + } + + public function pixelAspect() { + return $this->pixel_aspect; + } + + public function framerate() { + return $this->framerate; + } + + public function setDisplayInfo($width, $height, $display_aspect, $pixel_aspect, $framerate) { + $this->width = $width; + $this->height = $height; + $this->pixel_aspect = $pixel_aspect; + $this->display_aspect = $display_aspect; + $this->framerate = $framerate; + } + + public function autocrop() { + return $this->autocrop; + } + + public function setAutocrop($autocrop) { + $this->autocrop = $autocrop; + } + + public function chapterCount() { + return count($this->chapters); + } + + public function chapters() { + return $this->chapters; + } + + public function addChapter($chapter_id, $duration) { + $this->chapters[$chapter_id] = $duration; + } + + public function audioTrackCount() { + return count($this->audio); + } + + public function audioTracks() { + return $this->audio; + } + + public function addAudioTrack(HandBrakeCluster_Rips_SourceAudioTrack $audio_track) { + $this->audio[] = $audio_track; + } + + public function subtitleTrackCount() { + return count($this->subtitles); + } + + public function subtitleTracks() { + return $this->subtitles; + } + + public function addSubtitleTrack(HandBrakeCluster_Rips_SourceSubtitleTrack $subtitle_track) { + $this->subtitles[] = $subtitle_track; + } + +}; + +?> \ No newline at end of file diff --git a/pages/browse/source-details.php b/pages/browse/source-details.php index 22ba9b4..565f396 100644 --- a/pages/browse/source-details.php +++ b/pages/browse/source-details.php @@ -23,5 +23,6 @@ $source = new HandBrakeCluster_Rips_Source($source_path); $this->smarty->assign('source_path', $source_path); $this->smarty->assign('source', $source); $this->smarty->assign('output', $source->output()); +$this->smarty->assign('titles', $source->titles()); ?> \ No newline at end of file diff --git a/templates/browse/source-details.tpl b/templates/browse/source-details.tpl index e15c012..ef52ddf 100644 --- a/templates/browse/source-details.tpl +++ b/templates/browse/source-details.tpl @@ -14,10 +14,59 @@ Source {$source_path|escape:"html"} - - Output -
{$output|escape:"html"}
- + {if $titles} + + Titles + + + + + + + + + + + + + + {foreach from=$titles item=title} + + + + + + + + + + + + + + + + + + + + + + + {/foreach} + +
{$title->id()}Duration{$title->duration()}
Display +
    +
  • Size: {$title->width()}x{$title->height()}
  • +
  • Pixel aspect ratio: {$title->pixelAspect()}
  • +
  • Display aspect ratio: {$title->displayAspect()}
  • +
  • Framerate: {$title->framerate()}
  • +
  • Autocrop: {$title->autocrop()}
  • +
+
Chapters{$title->chapterCount()}
Audio Tracks{$title->audioTrackCount()}
Subtitle Tracks{$title->subtitleTrackCount()}
+ + + {/if} {else}