mkv2m4v 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -7,7 +7,6 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
- doc/
11
10
  lib/bundler/man
12
11
  pkg
13
12
  rdoc
data/README.md CHANGED
@@ -1,6 +1,3 @@
1
- **This gem is a work in progress.** Do not expect any supported functionality
2
- until v0.1.0.
3
-
4
1
  # mkv2m4v
5
2
 
6
3
  Converts audio and video tracks from a MKV (Matroska Media) container into a
@@ -15,12 +12,44 @@ It attempts to pass through as many codecs as possible.
15
12
 
16
13
  ## Installation
17
14
 
18
- $ brew install mediainfo
19
- $ gem install mkv2m4v
15
+ mkv2m4v is dependent upon `mediainfo`, `mkvextract`, `ffmpeg`, and `MP4Box`.
16
+
17
+ ```bash
18
+ $ brew install mediainfo
19
+ $ brew install gpac
20
+ $ brew install mkvtoolnix
21
+ $ gem install mkv2m4v
22
+ ```
23
+
24
+ _Note: `gpac` includes `MP4Box` and is dependent on `ffmpeg`, so `ffmpeg`
25
+ should install automatically. If not, you should manually install `ffmpeg`:_
26
+
27
+ ```bash
28
+ # ffmpeg should already be installed, but just in case:
29
+ $ brew install ffmpeg --with-tools
30
+ ```
20
31
 
21
32
  ## Usage
22
33
 
23
- $ mkv2m4v some-video.mkv
34
+ ```bash
35
+ $ mkv2m4v some-video.mkv
36
+ ```
37
+
38
+ ## Background
39
+
40
+ I got fed up with the reliability of the conversion tools out there for
41
+ converting MKV video containers to Apple TV compatible videos. Many of the
42
+ existing tools appear to have potential on the surface, but they fail under
43
+ certain scenarios.
44
+
45
+ [Handbrake](http://handbrake.fr/) is still the most realiable existing tool
46
+ out there, but it re-encodes h.264 video tracks, which is not ideal and very
47
+ slow.
48
+
49
+ The goal is to get a better, more automated tool to accomplish the task of
50
+ generating Apple TV compatible videos. Nothing more. It should generate an M4V
51
+ media file with the best possible quality given the original source and the
52
+ defaults should require no additional human interaction.
24
53
 
25
54
  ## Contributing
26
55
 
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
+ require "rake/testtask"
1
2
  require "bundler/gem_tasks"
3
+
4
+ desc "Default: run unit tests."
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib" << "test"
9
+ t.pattern = "test/**/*_test.rb"
10
+ end
data/doc/notes.md ADDED
@@ -0,0 +1,26 @@
1
+ Extracting tracks from an mkv (assuming correct track numbers):
2
+
3
+ $ mediainfo example.mkv # Inspect track IDs
4
+ $ mkvextract tracks example.mkv 1:example.h264 2:example.dts
5
+
6
+ Converting dts to ac3:
7
+
8
+ $ ffmpeg -i example.dts -acodec ac3 -ac 6 -ab 640k example-6ch.ac3
9
+
10
+ Converting dts to aac:
11
+
12
+ $ ffmpeg -i example.dts -acodec libfaac -ac 2 -ab 160k example-2ch.aac
13
+
14
+ Converting ac3 to aac:
15
+
16
+ $ ffmpeg -i example.ac3 -acodec libfaac -ac 2 -ab 160k example-2ch.aac
17
+
18
+ Remux the tracks into an M4V container. For Apple TV surround sound support,
19
+ the audio tracks must be in the same group with all but the first AAC track
20
+ disabled. [[source](http://forum.doom9.org/archive/index.php/t-160302.html)]
21
+
22
+ $ MP4Box \
23
+ -add example.h264:lang=en:name="AVC Video" \
24
+ -add example-2ch.aac:lang=en:group=1:delay=84:name="Stereo" \
25
+ -add example-6ch.ac3:lang=en:group=1:delay=84:disable:name="AC3" \
26
+ -new example_testing.m4v
@@ -0,0 +1,80 @@
1
+ require "mkv2m4v/track"
2
+ require "mkv2m4v/track_ranker"
3
+ require "mkv2m4v/transcoder"
4
+ require "mediainfo"
5
+ require "forwardable"
6
+ require "iso639"
7
+ require "colorize"
8
+
9
+ module Mkv2m4v
10
+ class File
11
+ extend Forwardable
12
+
13
+ attr_reader :info, :video_tracks, :audio_tracks, :text_tracks
14
+ attr_reader :ideal_video_track, :ideal_audio_track
15
+ def_delegator :info, :filename, :name
16
+ def_delegator :info, :full_filename, :filename
17
+
18
+ def initialize(filename, options = {})
19
+ @info = Mediainfo.new(filename)
20
+ @options = options
21
+ @languages = @options[:languages]
22
+ init_tracks
23
+ end
24
+
25
+ def format
26
+ info.general.format
27
+ end
28
+
29
+ def print
30
+ puts "#{format}: #{name}".black.on_yellow
31
+ video_tracks.filter.each(&:print) if @options[:verbose]
32
+ audio_tracks.filter.each(&:print) if @options[:verbose]
33
+ text_tracks.filter.each(&:print) if @options[:verbose]
34
+ print_ideal_tracks
35
+ puts
36
+ end
37
+
38
+ def transcode
39
+ puts "#{format}: #{name}".black.on_yellow
40
+ Transcoder.new(self, @options).run
41
+ puts
42
+ end
43
+
44
+ def self.each(filenames, options = {})
45
+ filenames.each do |filename|
46
+ if ::File.exists?(filename)
47
+ yield new(filename, options)
48
+ else
49
+ $stderr.puts "#{filename} does not exist."
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def print_ideal_tracks
57
+ puts "==> Ideal video track"
58
+ @ideal_video_track.print
59
+ puts "==> Ideal audio track"
60
+ @ideal_audio_track.print
61
+ end
62
+
63
+ def init_tracks
64
+ @video_tracks = tracks_by_type(:video)
65
+ @audio_tracks = tracks_by_type(:audio)
66
+ @text_tracks = tracks_by_type(:text)
67
+
68
+ @ideal_video_track = video_tracks.filter.rank.first
69
+ @ideal_audio_track = audio_tracks.filter.rank.first
70
+ end
71
+
72
+ def tracks_by_type(type)
73
+ info_tracks = info.send(type)
74
+ tracks = info_tracks.count.times.map do |i|
75
+ Mkv2m4v.const_get("#{type.capitalize}Track").new(info_tracks[i])
76
+ end
77
+ Mkv2m4v.const_get("#{type.capitalize}Ranker").new(tracks, @options)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,86 @@
1
+ require "forwardable"
2
+
3
+ module Mkv2m4v
4
+ class Track
5
+ extend Forwardable
6
+
7
+ attr_reader :info
8
+ def_delegator :info, :stream_id, :id
9
+ def_delegators :info, :format
10
+
11
+ def initialize(track_info)
12
+ @info = track_info
13
+ end
14
+
15
+ def language
16
+ Iso639[info["language"]]
17
+ end
18
+
19
+ def title
20
+ info["title"]
21
+ end
22
+
23
+ def format_description
24
+ "#{format} (#{info.format_info}, #{info.codec_id})"
25
+ end
26
+ end
27
+
28
+ class VideoTrack < Track
29
+ def_delegators :info, :frame_size, :interlaced?, :fps, :width, :height
30
+
31
+ def resolution
32
+ "#{frame_size}#{interlaced? ? "i" : "p"}"
33
+ end
34
+
35
+ def print
36
+ puts "Track #{id}: (video)"
37
+ puts " Format: #{format_description}"
38
+ puts " Resolution: #{resolution}"
39
+ puts " FPS: #{fps}"
40
+ puts " Language: #{language}"
41
+ puts " Title: #{title}"
42
+ end
43
+ end
44
+
45
+ class AudioTrack < Track
46
+ def_delegators :info, :bit_rate
47
+
48
+ def channel_count
49
+ info.channels.to_s.chars.first.to_i
50
+ end
51
+
52
+ def bit_rate_kbps
53
+ info.bit_rate.gsub(/\D/, "").to_i if info.bit_rate
54
+ end
55
+
56
+ def channel_description
57
+ "#{channel_count} (#{info.channel_positions})"
58
+ end
59
+
60
+ def bit_rate_description
61
+ "#{bit_rate_kbps}k (#{info.bit_rate_mode})"
62
+ end
63
+
64
+ def print
65
+ puts "Track #{id}: (audio)"
66
+ puts " Format: #{format_description}"
67
+ puts " Channels: #{channel_description}"
68
+ puts " Bit rate: #{bit_rate_description}"
69
+ puts " Language: #{language}"
70
+ puts " Title: #{title}"
71
+ end
72
+ end
73
+
74
+ class TextTrack < Track
75
+ def format_description
76
+ "#{format} (#{info.codec_id})"
77
+ end
78
+
79
+ def print
80
+ puts "Track #{id}: (text)"
81
+ puts " Format: #{format_description}"
82
+ puts " Language: #{language}"
83
+ puts " Title: #{title}"
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,64 @@
1
+ require "forwardable"
2
+
3
+ module Mkv2m4v
4
+ class TrackRanker
5
+ extend Forwardable
6
+
7
+ def_delegators :@tracks, :each, :first
8
+
9
+ def initialize(tracks, options = {})
10
+ @tracks = tracks
11
+ @options = options
12
+ end
13
+
14
+ def filter
15
+ filtered_tracks = @tracks.select { |t| language_okay?(t) }
16
+ self.class.new(filtered_tracks, @options)
17
+ end
18
+
19
+ def rank
20
+ ranked_tracks = @tracks.sort_by { |t| score(t) }.reverse
21
+ self.class.new(ranked_tracks, @options)
22
+ end
23
+
24
+ protected
25
+
26
+ def language_match?(track)
27
+ @options.languages.include?(track.language)
28
+ end
29
+
30
+ def language_okay?(track)
31
+ track.language.nil? ||
32
+ @options.languages.empty? ||
33
+ language_match?(track)
34
+ end
35
+ end
36
+
37
+ class VideoRanker < TrackRanker
38
+ def score(track)
39
+ score = 0
40
+ score += 8 if track.format == "AVC"
41
+ score += (track.height || 0)/ 1080.0 * 4.0
42
+ score += 2 if language_match?(track)
43
+ score
44
+ end
45
+ end
46
+
47
+ class AudioRanker < TrackRanker
48
+ def score(track)
49
+ score = 0
50
+ score += 4 if ["DTS", "AC-3"].include?(track.format)
51
+ score += 2 if track.format == "AAC"
52
+ score += (track.channel_count || 0) / 8.0 * 2.0
53
+ score += (track.bit_rate_kbps || 0) / 1500.0 * 2.0
54
+ score += 4 if language_match?(track)
55
+ score
56
+ end
57
+ end
58
+
59
+ class TextRanker < TrackRanker
60
+ # scoring not implemented yet
61
+ # def score(track)
62
+ # end
63
+ end
64
+ end
@@ -0,0 +1,141 @@
1
+ require "fileutils"
2
+ require "colorize"
3
+
4
+ module Mkv2m4v
5
+ class Transcoder
6
+ def initialize(file, options = {})
7
+ @file = file
8
+ @options = options
9
+ end
10
+
11
+ def run
12
+ setup
13
+ extract
14
+ transcode_avc
15
+ transcode_aac
16
+ transcode_ac3
17
+ remux
18
+ cleanup
19
+ end
20
+
21
+ private
22
+
23
+ def setup
24
+ FileUtils.mkdir_p tmp_dir
25
+ end
26
+
27
+ def extract
28
+ puts "==> Extracting #{video_id}:video.#{video_ext} #{audio_id}:audio.#{audio_ext} ".magenta
29
+ system "mkvextract tracks #{@file.filename.inspect} #{video_id}:#{video_file} #{audio_id}:#{audio_file}"
30
+ end
31
+
32
+ def transcode_avc
33
+ if video_format == "AVC"
34
+ puts "==> Assuming pass through for h.264 video track".yellow.on_black
35
+ else
36
+ puts "==> ERROR: Wrong Video format".white.on_red
37
+ end
38
+ end
39
+
40
+ def transcode_aac
41
+ if audio_format == "AAC"
42
+ puts "==> Assuming pass through for AAC audio track".yellow.on_black
43
+ else
44
+ puts "==> Transcoding #{audio_format} to Stereo AAC audio track".magenta
45
+ system "ffmpeg -i #{audio_file.inspect} -acodec libfaac -ac 2 -ab 160k #{audio_basename}.aac"
46
+ end
47
+ end
48
+
49
+ def transcode_ac3
50
+ if audio_format == "AC-3"
51
+ puts "==> Assuming pass through for AC-3 audio track".yellow.on_black
52
+ elsif audio_format == "AAC"
53
+ puts "==> Skipping AC-3 surround audio track".yellow.on_black
54
+ @skip_ac3 = true
55
+ else
56
+ puts "==> Transcoding #{audio_format} to Surround AC-3 audio track".magenta
57
+ system "ffmpeg -i #{audio_file.inspect} -acodec ac3 -ac #{max_audio_channels} -ab #{max_audio_bit_rate}k #{audio_basename}.ac3"
58
+ end
59
+ end
60
+
61
+ def remux
62
+ puts "==> Remuxing everything into an M4V container".magenta
63
+ command = "MP4Box"
64
+ command << " -add #{video_basename.inspect}.h264:lang=en:name=\"AVC Video\" "
65
+ command << " -add #{audio_basename.inspect}.aac:lang=en:group=1:delay=84:name=\"Stereo\" "
66
+ unless @skip_ac3
67
+ command << " -add #{audio_basename.inspect}.ac3:lang=en:group=1:delay=84:disable:name=\"AC3\" "
68
+ end
69
+ command << " -new #{m4v_file.inspect}"
70
+ system command
71
+ end
72
+
73
+ def cleanup
74
+ FileUtils.rm_rf tmp_dir
75
+ end
76
+
77
+ def video_basename
78
+ ::File.join(tmp_dir, "video")
79
+ end
80
+
81
+ def video_file
82
+ [video_basename, video_ext].join(".")
83
+ end
84
+
85
+ def video_id
86
+ @file.ideal_video_track.id
87
+ end
88
+
89
+ def video_format
90
+ @file.ideal_video_track.format
91
+ end
92
+
93
+ def video_ext
94
+ if video_format == "AVC"
95
+ "h264"
96
+ else
97
+ video_format.gsub(/\W/, "").downcase
98
+ end
99
+ end
100
+
101
+ def audio_basename
102
+ ::File.join(tmp_dir, "audio")
103
+ end
104
+
105
+ def audio_file
106
+ [audio_basename, audio_ext].join(".")
107
+ end
108
+
109
+ def audio_id
110
+ @file.ideal_audio_track.id
111
+ end
112
+
113
+ def audio_format
114
+ @file.ideal_audio_track.format
115
+ end
116
+
117
+ def audio_ext
118
+ audio_format.gsub(/\W/, "").downcase
119
+ end
120
+
121
+ def max_audio_channels
122
+ [@file.ideal_audio_track.channel_count, 6].min
123
+ end
124
+
125
+ def max_audio_bit_rate
126
+ [@file.ideal_audio_track.bit_rate_kbps, 640].min
127
+ end
128
+
129
+ def dir
130
+ @options[:dir] || ::File.dirname(@file.filename)
131
+ end
132
+
133
+ def tmp_dir
134
+ ::File.join(dir, ::File.basename(@file.name, ".*") + "-tracks")
135
+ end
136
+
137
+ def m4v_file
138
+ ::File.join(dir, ::File.basename(@file.name, ".*") + ".m4v")
139
+ end
140
+ end
141
+ end
@@ -1,5 +1,5 @@
1
1
  module Mkv2m4v
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
 
4
4
  VersionDescription = "mkv2m4v #{VERSION} (c) 2012 Ryan McGeary"
5
5
  Description = <<EOS
data/lib/mkv2m4v.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "mkv2m4v/version"
2
- require "mkv2m4v/mediainfo"
2
+ require "mkv2m4v/file"
3
3
  require "trollop"
4
4
 
5
5
  module Mkv2m4v
@@ -10,56 +10,17 @@ module Mkv2m4v
10
10
 
11
11
  def run
12
12
  if @options[:info]
13
- print_info
13
+ each_file(&:print)
14
14
  else
15
- convert_files
15
+ each_file(&:transcode)
16
16
  end
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def convert_files
22
- $stderr.puts "Converting files is not yet supported. For now, try the --info option."
23
- # each_file do |file|
24
- # end
25
- end
26
-
27
- def print_info
28
- each_file do |file|
29
- info = Mediainfo.new file
30
- puts "#{info.general.format}: #{file}"
31
- info.video.count.times do |i|
32
- print_video_track(info.video[i])
33
- end
34
- info.audio.count.times do |i|
35
- print_audio_track(info.audio[i])
36
- end
37
- puts
38
- end
39
- end
40
-
41
- def print_video_track(video)
42
- puts "Video Track #{video.stream_id}:"
43
- puts " Format: #{video.format} (#{video.format_info}, #{video.codec_id})"
44
- puts " Resolution: #{video.frame_size}#{video.interlaced? ? "i" : "p"}"
45
- puts " FPS: #{video.fps}"
46
- puts " Language: #{video.language}"
47
- end
48
-
49
- def print_audio_track(audio)
50
- puts "Audio Track #{audio.stream_id}:"
51
- puts " Format: #{audio.format} (#{audio.format_info}, #{audio.codec_id})"
52
- puts " Channels: #{audio.channel_count} (#{audio.channel_positions})"
53
- puts " Language: #{audio.language}"
54
- end
55
-
56
21
  def each_file
57
- ARGV.each do |file|
58
- if File.exists?(file)
59
- yield file
60
- else
61
- $stderr.puts "#{file} does not exist."
62
- end
22
+ Mkv2m4v::File.each(@filenames, @options) do |file|
23
+ yield file
63
24
  end
64
25
  end
65
26
 
@@ -69,7 +30,12 @@ module Mkv2m4v
69
30
  version Mkv2m4v::VersionDescription
70
31
  banner [Mkv2m4v::Description, Mkv2m4v::Usage].join("\n")
71
32
  opt :info, "Print media info only"
33
+ opt :lang, "Preferred languages", :type => :strings, :default => ["English"]
34
+ opt :dir, "Destination directory", :type => :string
35
+ opt :verbose, "More output", :type => :boolean
72
36
  end
37
+ @options[:languages] = @options[:lang].map { |lang| Iso639[lang] }.compact
38
+ @filenames = ARGV
73
39
  end
74
40
  end
75
41
  end
data/mkv2m4v.gemspec CHANGED
@@ -17,6 +17,11 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency("trollop", "~> 2.0")
21
- gem.add_dependency("mediainfo", "~> 0.7")
20
+ gem.add_dependency "trollop", "~> 2.0"
21
+ gem.add_dependency "mediainfo", "~> 0.7"
22
+ gem.add_dependency "iso639", "~> 1.0", ">= 1.0.5"
23
+ gem.add_dependency "colorize", "~> 0.5"
24
+
25
+ gem.add_development_dependency "minitest", "~> 4.3"
26
+ gem.add_development_dependency "rake", "~> 10.0"
22
27
  end
@@ -0,0 +1,2 @@
1
+ require "minitest/autorun"
2
+ require "minitest/pride"
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: mkv2m4v
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan McGeary
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-23 00:00:00.000000000 Z
12
+ date: 2012-12-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,6 +43,76 @@ dependencies:
43
43
  - !ruby/object:Gem::Version
44
44
  version: '0.7'
45
45
  none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ~>
50
+ - !ruby/object:Gem::Version
51
+ version: '1.0'
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.5
55
+ none: false
56
+ name: iso639
57
+ type: :runtime
58
+ prerelease: false
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: '1.0'
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: 1.0.5
67
+ none: false
68
+ - !ruby/object:Gem::Dependency
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: '0.5'
74
+ none: false
75
+ name: colorize
76
+ type: :runtime
77
+ prerelease: false
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.5'
83
+ none: false
84
+ - !ruby/object:Gem::Dependency
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '4.3'
90
+ none: false
91
+ name: minitest
92
+ type: :development
93
+ prerelease: false
94
+ requirement: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: '4.3'
99
+ none: false
100
+ - !ruby/object:Gem::Dependency
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ~>
104
+ - !ruby/object:Gem::Version
105
+ version: '10.0'
106
+ none: false
107
+ name: rake
108
+ type: :development
109
+ prerelease: false
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ~>
113
+ - !ruby/object:Gem::Version
114
+ version: '10.0'
115
+ none: false
46
116
  description: ! 'mkv2m4v is a command line utility that converts audio and video tracks
47
117
  from a
48
118
 
@@ -62,10 +132,15 @@ files:
62
132
  - README.md
63
133
  - Rakefile
64
134
  - bin/mkv2m4v
135
+ - doc/notes.md
65
136
  - lib/mkv2m4v.rb
66
- - lib/mkv2m4v/mediainfo.rb
137
+ - lib/mkv2m4v/file.rb
138
+ - lib/mkv2m4v/track.rb
139
+ - lib/mkv2m4v/track_ranker.rb
140
+ - lib/mkv2m4v/transcoder.rb
67
141
  - lib/mkv2m4v/version.rb
68
142
  - mkv2m4v.gemspec
143
+ - test/test_helper.rb
69
144
  homepage: https://github.com/rmm5t/mkv2m4v
70
145
  licenses: []
71
146
  post_install_message:
@@ -90,4 +165,5 @@ rubygems_version: 1.8.23
90
165
  signing_key:
91
166
  specification_version: 3
92
167
  summary: Makes Apple TV compatible videos
93
- test_files: []
168
+ test_files:
169
+ - test/test_helper.rb
@@ -1,13 +0,0 @@
1
- require "mediainfo"
2
-
3
- class Mediainfo::Stream
4
- def language
5
- self["language"]
6
- end
7
- end
8
-
9
- class Mediainfo::AudioStream
10
- def channel_count
11
- channels.to_s.chars.first.to_i
12
- end
13
- end