apple-tv-converter 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ v0.0.1
2
+ ------
3
+ * Mac OSX implementation
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Pedro Rodrigues
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ apple-tv-converter
2
+ =================
3
+
4
+ Media converter for AppleTV.
5
+
6
+ ## On Mac OSX
7
+
8
+ - Uses ffmpeg for encoding (http://ffmpeg.org)
9
+ - Uses Subler for adding the subtitles and setting the iTunes metadata tags (http://code.google.com/p/subler/)
10
+ - Uses MKVToolNix for extracting the embedded subtitles from MKV files (http://www.bunkus.org/videotools/mkvtoolnix/)
11
+
12
+ # TODO
13
+
14
+ - Windows implementation
15
+ - Tests!
16
+ - Create a Mac OSX executable for running the script
@@ -0,0 +1,72 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+
3
+ require 'logger'
4
+ require 'stringio'
5
+ require 'shellwords'
6
+ require 'open3'
7
+ require 'streamio-ffmpeg'
8
+ require 'mkv'
9
+ require 'awesome_print'
10
+ require 'fileutils'
11
+ require 'language_list'
12
+
13
+ require 'apple_tv_converter/version'
14
+ require 'apple_tv_converter/io_patch'
15
+ require 'apple_tv_converter/media_converter'
16
+ require 'apple_tv_converter/media'
17
+ require 'apple_tv_converter/media_converter_adapter'
18
+ require 'apple_tv_converter/media_converter_windows_adapter' if RUBY_PLATFORM =~ /(win|w)(32|64)$/
19
+ require 'apple_tv_converter/media_converter_mac_adapter' if RUBY_PLATFORM =~ /(darwin)/
20
+
21
+ module AppleTvConverter
22
+ # AppleTvConverter logs information about its progress when it's transcoding.
23
+ # Jack in your own logger through this method if you wish to.
24
+ #
25
+ # @param [Logger] log your own logger
26
+ # @return [Logger] the logger you set
27
+ def self.logger=(log)
28
+ @logger = log
29
+ end
30
+
31
+ # Get AppleTvConverter logger.
32
+ #
33
+ # @return [Logger]
34
+ def self.logger
35
+ return @logger if @logger
36
+ logger = Logger.new(STDOUT)
37
+ logger.level = Logger::INFO
38
+ @logger = logger
39
+ end
40
+
41
+ # Set the path of the mp4box binary.
42
+ # Can be useful if you need to specify a path such as /usr/local/bin/mp4box
43
+ #
44
+ # @param [String] path to the mp4box binary
45
+ # @return [String] the path you set
46
+ def self.mp4box_binary=(bin)
47
+ @mp4box_binary = bin
48
+ end
49
+
50
+ # Get the path to the mp4box binary, defaulting to 'MP4Box'
51
+ #
52
+ # @return [String] the path to the mp4box binary
53
+ def self.mp4box_binary
54
+ @mp4box_binary.nil? ? 'MP4Box' : @mp4box_binary
55
+ end
56
+
57
+ # Set the path of the atomic parsley binary.
58
+ # Can be useful if you need to specify a path such as /usr/local/bin/atomic_parsley
59
+ #
60
+ # @param [String] path to the atomic parsley binary
61
+ # @return [String] the path you set
62
+ def self.atomic_parsley_binary=(bin)
63
+ @atomic_parsley_binary = bin
64
+ end
65
+
66
+ # Get the path to the atomic_parsley binary, defaulting to 'AtomicParsley'
67
+ #
68
+ # @return [String] the path to the atomic_parsley binary
69
+ def self.atomic_parsley_binary
70
+ @atomic_parsley_binary.nil? ? 'AtomicParsley' : @atomic_parsley_binary
71
+ end
72
+ end
@@ -0,0 +1,59 @@
1
+ if RUBY_VERSION =~ /1\.8/
2
+ # Useful when `timeout.rb`, which, on M.R.I 1.8, relies on green threads, does not work consistently.
3
+ begin
4
+ require 'system_timer'
5
+ AppleTvConverter::Timer = SystemTimer
6
+ rescue LoadError
7
+ require 'timeout'
8
+ AppleTvConverter::Timer = Timeout
9
+ end
10
+ else
11
+ require 'timeout'
12
+ AppleTvConverter::Timer = Timeout
13
+ end
14
+
15
+ require 'win32/process' if RUBY_PLATFORM =~ /(win|w)(32|64)$/
16
+
17
+ #
18
+ # Monkey Patch timeout support into the IO class
19
+ #
20
+ class IO
21
+ def each_with_timeout(pid, seconds, sep_string=$/)
22
+ sleeping_queue = Queue.new
23
+ thread = nil
24
+
25
+ timer_set = lambda do
26
+ thread = new_thread(pid) { AppleTvConverter::Timer.timeout(seconds) { sleeping_queue.pop } }
27
+ end
28
+
29
+ timer_cancel = lambda do
30
+ thread.kill if thread rescue nil
31
+ end
32
+
33
+ timer_set.call
34
+ each(sep_string) do |buffer|
35
+ timer_cancel.call
36
+ yield buffer
37
+ timer_set.call
38
+ end
39
+ ensure
40
+ timer_cancel.call
41
+ end
42
+
43
+ private
44
+ def new_thread(pid, &block)
45
+ current_thread = Thread.current
46
+ Thread.new do
47
+ begin
48
+ block.call
49
+ rescue Exception => e
50
+ current_thread.raise e
51
+ if RUBY_PLATFORM =~ /(win|w)(32|64)$/
52
+ Process.kill(1, pid)
53
+ else
54
+ Process.kill('SIGKILL', pid)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ module AppleTvConverter
2
+ class Media
3
+ attr_accessor :show, :season, :number, :genre
4
+ attr_reader :original_filename, :quality
5
+
6
+ def original_filename=(value)
7
+ @original_filename = value
8
+
9
+ if @original_filename =~ /.*?\.m4v$/
10
+ if File.exists?(@original_filename.gsub(/\.m4v/, '.avi'))
11
+ @original_filename = @original_filename.gsub(/\.m4v/, '.avi')
12
+ elsif File.exists?(@original_filename.gsub(/\.m4v/, '.mkv'))
13
+ @original_filename = @original_filename.gsub(/\.m4v/, '.mkv')
14
+ end
15
+ end
16
+ end
17
+
18
+ def subtitle_filename
19
+ @subtitle_filename ||= self.original_filename.gsub(/\.(mkv|avi|mp4)/, '.srt')
20
+ end
21
+
22
+ def converted_filename
23
+ @converted_filename ||= self.original_filename.gsub(/\.(mkv|avi)/, '.m4v')
24
+ end
25
+
26
+ def converted_filename_with_subtitles
27
+ @converted_filename_with_subtitles ||= self.original_filename.gsub(/\.(mkv|avi|mp4)/, '_subtitled.m4v')
28
+ end
29
+
30
+ def ffmpeg_data
31
+ @ffmpeg_data ||= FFMPEG::Movie.new(original_filename)
32
+ end
33
+
34
+ def mkv_data
35
+ @mkv_data ||= MKV::Movie.new(original_filename)
36
+ end
37
+
38
+ def is_tv_show_episode?
39
+ !season.nil? && !number.nil?
40
+ end
41
+
42
+ def is_movie?
43
+ !is_tv_show_episode?
44
+ end
45
+
46
+ def is_mkv?
47
+ ffmpeg_data.container =~ /matroska/i rescue File.extname(original_filename).downcase == '.mkv'
48
+ end
49
+
50
+ def is_mp4?
51
+ ffmpeg_data.container =~ /mp4/ rescue File.extname(original_filename) =~ /\.(mp4|m4v)$/
52
+ end
53
+
54
+ def is_valid?
55
+ ffmpeg_data.valid?
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ module AppleTvConverter
2
+ class MediaConverter
3
+ @@timeout = 200
4
+
5
+ def is_windows? ; RUBY_PLATFORM =~/.*?mingw.*?/i ; end
6
+ def is_macosx? ; RUBY_PLATFORM =~/.*?darwin.*?/i ; end
7
+
8
+ def initialize
9
+ @adapter = is_windows? ? AppleTvConverter::MediaConverterWindowsAdapter.new : AppleTvConverter::MediaConverterMacAdapter.new
10
+ end
11
+
12
+ def process_media(media)
13
+ AppleTvConverter.logger.debug "**** #{File.basename(media.original_filename)}"
14
+ AppleTvConverter.logger.debug "* Video codec: #{media.ffmpeg_data.video_codec}"
15
+ AppleTvConverter.logger.debug "* Audio codec: #{media.ffmpeg_data.audio_codec}"
16
+ AppleTvConverter.logger.debug "* Container: #{media.ffmpeg_data.container}" rescue nil
17
+
18
+ extract_subtitles(media) if media.is_mkv?
19
+
20
+ if @adapter.transcode(media)
21
+ @adapter.add_subtitles media
22
+ @adapter.tag media
23
+
24
+ # add_to_itunes media
25
+ @adapter.clean_up media
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # HELPERS
32
+ def process_all_medias(medias)
33
+ medias.each_with_index do |media, index|
34
+ puts '*' * 80
35
+ puts "* [#{(index + 1).to_s.rjust(medias.length.to_s.length, ' ')}/#{medias.length}] #{File.basename(media.original_filename)}"
36
+
37
+ process_media media
38
+ end
39
+ end
40
+
41
+ def process_file(file)
42
+ puts '*' * 80
43
+ puts "* #{file}"
44
+
45
+ media = parse_filename(file)
46
+ process_media media
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,87 @@
1
+ module AppleTvConverter
2
+ class MediaConverterAdapter
3
+ def extract_subtitles(media)
4
+ printf "*** Extracting subtitles"
5
+ media.mkv_data.extract_subtitles(File.dirname(media.original_filename)) do |progress|
6
+ printf "\r* Progress: #{progress}%%"
7
+ end
8
+ puts "\r* Progress: [DONE]"
9
+ end
10
+
11
+ def transcode(media)
12
+ if convert?(media)
13
+ puts "*** Encoding"
14
+
15
+ options = {
16
+ :video_codec => convert_video?(media) ? 'mpeg4' : 'copy',
17
+ :audio_codec => convert_audio?(media) ? 'libfaac' : 'copy'
18
+ }
19
+
20
+ options[:custom] = "-qscale 1" if convert_video?(media)
21
+
22
+ transcoded = media.ffmpeg_data.transcode(media.converted_filename, options) do |progress|
23
+ printf %Q[\r* Progress: #{(progress * 100).round(2)}%%]
24
+ end
25
+
26
+ status transcoded.valid?
27
+
28
+ if status
29
+ puts "\r* Encoding: [DONE]#{' ' * 20}"
30
+ else
31
+ puts "\r* Encoding: [ERROR]#{' ' * 20}"
32
+ end
33
+ else
34
+ puts "*** Encoding: [UNNECESSARY]"
35
+ status = true
36
+ end
37
+
38
+ return status
39
+ end
40
+
41
+ def add_subtitles(media)
42
+ raise NotImplementedYetException
43
+ end
44
+
45
+ def tag(media)
46
+ raise NotImplementedYetException
47
+ end
48
+
49
+ def add_to_itunes(media)
50
+ raise NotImplementedYetException
51
+ end
52
+
53
+ def clean_up(media)
54
+ printf "*** Cleaning up"
55
+ begin
56
+ FileUtils.rm(media.original_filename) unless media.original_filename == media.converted_filename
57
+ FileUtils.rm_r list_files(File.join(File.dirname(media.original_filename), '*.srt'))
58
+ puts " [DONE]"
59
+ rescue
60
+ puts " [ERROR]"
61
+ end
62
+ end
63
+
64
+ protected
65
+
66
+ def list_files(ls)
67
+ raise NotImplementedYetException
68
+ end
69
+
70
+ def convert_audio?(media)
71
+ return media.ffmpeg_data.audio_codec !~ /(?:ac3|aac)/i
72
+ end
73
+
74
+ def convert_video?(media)
75
+ return media.ffmpeg_data.video_codec !~ /.*(?:mpeg4).*/i
76
+ end
77
+
78
+ def convert?(media)
79
+ !(media.is_valid? && media.is_mp4?)# && !convert_video?(media) && !convert_audio?(media))
80
+ end
81
+
82
+ def has_subtitles?(media)
83
+ list_files(File.join(File.dirname(media.original_filename), '*.srt')).any?
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,87 @@
1
+ module AppleTvConverter
2
+ class MediaConverterMacAdapter < MediaConverterAdapter
3
+ def add_subtitles(media)
4
+ puts "*** Adding subtitles"
5
+
6
+ printf "* Removing any previous subtitles"
7
+
8
+ command_line = %Q[./SublerCLI -remove -dest "#{media.converted_filename}"]
9
+
10
+ AppleTvConverter.logger.debug "Executing:"
11
+ AppleTvConverter.logger.debug command_line
12
+
13
+ output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
14
+
15
+ puts output.strip.empty? ? " [DONE]" : (output.strip == 'Error: (null)' ? " [NONE FOUND]" : " [ERROR]")
16
+
17
+ if has_subtitles?(media)
18
+ list_files(File.join(File.dirname(media.original_filename), '*.srt')).map do |subtitle_filename|
19
+ subtitle_filename =~ /(\w{3})\.srt$/
20
+ language_code = $1 || 'eng'
21
+
22
+ language = ::LanguageList::LanguageInfo.find_by_iso_639_3(language_code)
23
+ language ||= ::LanguageList::LanguageInfo.find_by_iso_639_3('eng')
24
+
25
+
26
+ command_line = "./SublerCLI "
27
+ command_line << %Q[-source "#{subtitle_filename}" ]
28
+ command_line << %Q[-language "#{language.name}" ]
29
+ command_line << %Q[-dest "#{media.converted_filename}"]
30
+
31
+ AppleTvConverter.logger.debug "Executing:"
32
+ AppleTvConverter.logger.debug command_line
33
+
34
+ printf "* Adding #{language.name} subtitles"
35
+ output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
36
+
37
+ puts output.strip.empty? ? " [DONE]" : " [ERROR]"
38
+ end
39
+ else
40
+ puts "* No new subtitles found"
41
+ end
42
+ end
43
+
44
+ def tag(media)
45
+ metadata = ''
46
+ metadata << %Q[{Name: #{media.show} S#{media.season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}}] if media.is_tv_show_episode?
47
+ metadata << %Q[{Name: #{media.show}}] if media.is_movie?
48
+ metadata << %Q[{Genre: #{media.show}}{TV Show: #{media.show}}{TV Season: #{media.season}}{TV Episode #: #{media.number}}] if media.is_tv_show_episode?
49
+ metadata << %Q[{Genre: #{media.quality} Movie}] if media.is_movie?
50
+
51
+ command_line = %Q[./SublerCLI -metadata "#{metadata}" -dest "#{media.converted_filename}"]
52
+
53
+ AppleTvConverter.logger.debug "Executing:"
54
+ AppleTvConverter.logger.debug command_line
55
+
56
+ printf "*** Tagging"
57
+
58
+ output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
59
+
60
+ puts output.strip.empty? ? " [DONE]" : " [ERROR]"
61
+
62
+ return output.strip.empty?
63
+ end
64
+
65
+ def add_to_itunes(media)
66
+ printf "*** Adding to iTunes"
67
+
68
+ command_line = [
69
+ "ln -s",
70
+ "#{media.converted_filename}".gsub(/\s/, '\ ').gsub(/\[/, '\[').gsub(/\]/, '\]'),
71
+ "#{File.expand_path(File.join('~', 'Music', 'iTunes', 'iTunes Media', 'Automatically Add to iTunes.localized'))}".gsub(/\s/, '\ ').gsub(/\[/, '\[').gsub(/\]/, '\]')
72
+ ].join(' ')
73
+
74
+ AppleTvConverter.logger.debug "Executing:"
75
+ AppleTvConverter.logger.debug command_line
76
+
77
+ `#{command_line}`
78
+
79
+ puts ' [DONE]'
80
+ return true
81
+ end
82
+
83
+ def list_files(ls)
84
+ `ls -1 #{ls.gsub(/\s/, '\ ').gsub(/\[/, '\[').gsub(/\]/, '\]')} 2>/dev/null`.split("\n")
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,43 @@
1
+ module AppleTvConverter
2
+ class MediaConverterWindowsAdapter < MediaConverterAdapter
3
+ require 'win32ole'
4
+
5
+ def handbrake_location
6
+ return File.expand_path("./HandBrakeCLI#{'_x64' if is_windows_64bit?}.exe")
7
+ end
8
+
9
+ def atomic_parsley_location
10
+ return File.expand_path('./AtomicParsley.exe')
11
+ end
12
+
13
+ def clean_up_command_line(command_line)
14
+ return command_line.gsub(/\//, '\\')
15
+ end
16
+
17
+ def add_to_itunes(episode)
18
+ printf "* Adding to iTunes"
19
+ @itunes ||= WIN32OLE.new("iTunes.Application")
20
+ @itunes.PlayFile(episode.converted_filename)
21
+ @itunes.Stop
22
+
23
+ puts " [DONE]"
24
+ end
25
+
26
+ def line_ending
27
+ end
28
+
29
+ def execute_command(command_line, &block)
30
+ `#{command_line}`
31
+ end
32
+
33
+ private
34
+
35
+ def is_windows_32bit?
36
+ !is_windows_64bit?
37
+ end
38
+
39
+ def is_windows_64bit?
40
+ ENV.has_key?('ProgramFiles(x86)')
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module AppleTvConverter
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apple-tv-converter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pedro Rodrigues
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.7'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.7'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.2
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: mkv
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.0.1
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: streamio-ffmpeg
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: language_list
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.0.3
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.0.3
94
+ description: Converts movies to a format playable on Apple TV. Supporting multiple
95
+ subtitles.
96
+ email:
97
+ - pedro@bbde.org
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - lib/apple_tv_converter/io_patch.rb
103
+ - lib/apple_tv_converter/media.rb
104
+ - lib/apple_tv_converter/media_converter.rb
105
+ - lib/apple_tv_converter/media_converter_adapter.rb
106
+ - lib/apple_tv_converter/media_converter_mac_adapter.rb
107
+ - lib/apple_tv_converter/media_converter_windows_adapter.rb
108
+ - lib/apple_tv_converter/version.rb
109
+ - lib/apple_tv_converter.rb
110
+ - README.md
111
+ - LICENSE
112
+ - CHANGELOG
113
+ homepage: http://github.com/gokuu/apple-tv-converter
114
+ licenses: []
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 1.8.24
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: Converts movies to a format playable on Apple TV.
137
+ test_files: []