transcode 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0375af3ce6a6249106ace1b6bf411e0b7bc268463b290162e8db0a4c2e3a4daf
4
+ data.tar.gz: fa721d63eea507a241bdc1a37ca096dffb3077d0c69d06610a1cff7a03284c0e
5
+ SHA512:
6
+ metadata.gz: 7a5453ee0956ab79fba302cbdccde7029a26d09a5327039dfd65c50ab4f62a9c0f496e09618ae3688b2eb582bcfbe4b22be988dff3f40c9cbee060a58a9319d7
7
+ data.tar.gz: 11bb4241bd5f2e39afa15f3581308e7ec10f0a34b43e945f3bd65cdf79c76f21e3360efe0dad33c887ef905ba27144eb8af9190d5334eedd9f23e9ed0683ad0a
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2020, David Rabkin
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,52 @@
1
+ # Transcode
2
+
3
+ Tool to transcode batch of video files.
4
+
5
+ * [About](#about)
6
+ * [Installation](#installation)
7
+ * [Usage](#usage)
8
+ * [License](#license)
9
+
10
+ ## About
11
+
12
+ Hi, I'm [David Rabkin](https://www.rabkin.co.il). I created this tool to
13
+ enhance [Don Melton](http://donmelton.com/)'s [Video
14
+ Transcoding](https://github.com/donmelton/video_transcoding/).
15
+
16
+ ## Installation
17
+
18
+ The tool is designed to work on macOS, GNU/Linux, Windows, Unix-like OS. It is
19
+ packaged as a Gem and require Ruby version 2.3 or later. See «[Installing
20
+ Ruby](https://www.ruby-lang.org/en/documentation/installation/)» if you don't
21
+ have the proper version on your platform.
22
+
23
+ Use this command to install:
24
+
25
+ gem install transcode
26
+
27
+ ### Updating
28
+
29
+ Use this command to update the package:
30
+
31
+ gem update transcode
32
+
33
+ ### Requirements
34
+
35
+ See [Video Transcoding requirements](https://github.com/donmelton/video_transcoding/blob/master/README.md?ts=2#requirements).
36
+
37
+ ## Usage
38
+ Usage: transcode [options].
39
+ -a, --act Real encoding.
40
+ -s, --sca Scans files at the directory.
41
+ -m, --mp3 Converts files to mp3.
42
+ -d, --dir dir Directory to transcode.
43
+ -i, --tit tit Specific title by number.
44
+ -o, --out out Directory to output.
45
+ -u, --aud aud Audio stream numbers.
46
+ -t, --sub sub Subtitle stream numbers.
47
+ -w, --wid wid Width of the table.
48
+
49
+ ## License
50
+
51
+ Transcode is copyright [David Rabkin](http://www.rabkin.co.il/) and
52
+ available under a [2-Claus BSD license](https://github.com/rdavid/transcode/blob/master/LICENSE).
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
5
+ # Copyright 2018-present David Rabkin
6
+
7
+ require 'pidfile'
8
+ require_relative '../lib/transcode'
9
+
10
+ PidFile.new
11
+ Transcode::Transcoder.new.do
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require_relative 'transcode/transcoder'
7
+ require_relative 'transcode/version'
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'optparse'
7
+
8
+ module Transcode
9
+ # Handles input parameters.
10
+ class Configuration # rubocop:disable Metrics/ClassLength
11
+ attr_reader :files
12
+ DIC = [
13
+ ['-a', '--act', 'Real encoding.', nil, :act],
14
+ ['-s', '--sca', 'Scans files at the directory.', nil, :sca],
15
+ ['-m', '--mp3', 'Converts files to mp3.', nil, :mp3],
16
+ ['-d', '--dir dir', 'Directory to transcode.', String, :dir],
17
+ ['-i', '--tit tit', 'Specific title by number.', Array, :tit],
18
+ ['-o', '--out out', 'Directory to output.', String, :out],
19
+ ['-u', '--aud aud', 'Audio stream numbers.', Array, :aud],
20
+ ['-t', '--sub sub', 'Subtitle stream numbers.', Array, :sub],
21
+ ['-w', '--wid wid', 'Width of the table.', Integer, :wid]
22
+ ].freeze
23
+ EXT = %i[
24
+ avi flv m4v mkv mp4 mpg mpeg ts webm vob wmv
25
+ ].map(&:to_s).join(',').freeze
26
+
27
+ def initialize
28
+ @options = {}
29
+ OptionParser.new do |o|
30
+ o.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options]."
31
+ DIC.each { |f, p, d, t, k| o.on(f, p, t, d) { |i| @options[k] = i } }
32
+ end.parse!
33
+ find_dir
34
+ find_fil
35
+ validate
36
+ end
37
+
38
+ def find_dir
39
+ if dir.nil?
40
+ @options[:dir] = Dir.pwd
41
+ else
42
+ raise "No such directory: #{dir}." unless File.directory?(dir)
43
+
44
+ @options[:dir] = File.expand_path(dir)
45
+ end
46
+ @options[:out] = File.expand_path(out.nil? ? '~' : out)
47
+ end
48
+
49
+ def find_fil
50
+ @files = Dir.glob("#{dir}/*.{#{EXT}}").select { |f| File.file? f }
51
+ sub = !mp3?
52
+ @files += Dir.glob("#{dir}/*").select { |f| File.directory? f } if sub
53
+ @files.sort_by!(&:naturalized)
54
+ @files.sort_by!(&:swapcase)
55
+ end
56
+
57
+ def validate
58
+ validate_files
59
+ validate_tit
60
+ validate_val(aud, :aud)
61
+ validate_val(sub, :sub)
62
+ raise "Width of the table should exeeds 14 symbols: #{wid}." if wid < 15
63
+ end
64
+
65
+ def validate_files
66
+ raise "#{dir} doesn't have #{EXT} files or directories." if files.empty?
67
+
68
+ bad = files.reject { |f| File.readable?(f) }
69
+ raise "Unable to read #{bad} files." unless bad.empty?
70
+ end
71
+
72
+ def validate_tit # rubocop:disable Metrics/AbcSize
73
+ if tit.nil?
74
+ @options[:tit] = Array.new(files.size, '0')
75
+ return
76
+ end
77
+ if files.size == 1
78
+ @files = Array.new(tit.size, files.first)
79
+ return
80
+ end
81
+ raise "Title feature doesn't support #{files.size} files."
82
+ end
83
+
84
+ def validate_val(val, tag)
85
+ f = files.size
86
+ (@options[tag] = Array.new(f, '0')).nil? || return if val.nil?
87
+ s = val.size
88
+ if s == 1
89
+ @options[tag] = Array.new(f, val.first)
90
+ else
91
+ raise "#{tag} and files do not suit #{s} != #{f}." unless s == f
92
+ end
93
+ end
94
+
95
+ def act?
96
+ @options[:act]
97
+ end
98
+
99
+ def sca?
100
+ @options[:sca]
101
+ end
102
+
103
+ def mp3?
104
+ @options[:mp3]
105
+ end
106
+
107
+ def dir
108
+ @options[:dir]
109
+ end
110
+
111
+ def tit
112
+ @options[:tit]
113
+ end
114
+
115
+ def out
116
+ @options[:out]
117
+ end
118
+
119
+ def aud
120
+ @options[:aud]
121
+ end
122
+
123
+ def sub
124
+ @options[:sub]
125
+ end
126
+
127
+ def wid
128
+ if @options[:wid].nil?
129
+ # Reads current terminal width.
130
+ wid = `tput cols`
131
+ wid.to_s.empty? ? 79 : wid.to_i
132
+ else
133
+ @options[:wid].to_i
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'terminal-table'
7
+ require_relative 'utils'
8
+
9
+ module Transcode
10
+ # Formats and prints output data.
11
+ class Reporter
12
+ def initialize(act, tit, wid)
13
+ @act = act
14
+ @tit = tit
15
+ @tbl = wid
16
+ @ttl = @tbl - 4
17
+ @str = (@tbl - 7) / 2
18
+ @row = []
19
+ @tim = Timer.new
20
+ @sta = { converted: 0, failed: 0 }
21
+ end
22
+
23
+ def add(file, res, aud = 0, sub = 0, tit = 0)
24
+ row = [Utils.trim(File.basename(file), @str)]
25
+ if aud != 0 || sub != 0
26
+ (row << [
27
+ { value: aud, alignment: :right },
28
+ { value: sub, alignment: :right },
29
+ { value: tit, alignment: :right }
30
+ ]).flatten!
31
+ end
32
+ @row << row
33
+ @sta[res ? :converted : :failed] += 1
34
+ end
35
+
36
+ def head
37
+ head = [{ value: 'file', alignment: :center }]
38
+ if @row.first.size == 4
39
+ (head << [
40
+ { value: 'audio', alignment: :center },
41
+ { value: 'subtitles', alignment: :center },
42
+ { value: 'titles', alignment: :center }
43
+ ]).flatten!
44
+ end
45
+ head
46
+ end
47
+
48
+ def table
49
+ Terminal::Table.new(
50
+ title: Utils.trim(@tit, @ttl),
51
+ headings: head,
52
+ rows: @row,
53
+ style: { width: @tbl }
54
+ )
55
+ end
56
+
57
+ def do
58
+ msg = "#{@act ? 'Real' : 'Test'}:#{stat} in #{@tim.read}."
59
+ msg = Utils.trim(msg, @ttl)
60
+ puts "#{table}\n| #{msg}#{' ' * (@ttl - msg.length)} |\n+-#{'-' * @ttl}-+"
61
+ end
62
+
63
+ def stat
64
+ out = ''
65
+ @sta.each do |k, v|
66
+ out += ' ' + v.to_s + ' ' + k.to_s + ',' if v.positive?
67
+ end
68
+ out.chop
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ require 'English'
7
+ require 'shellwords'
8
+ require_relative 'configurator'
9
+ require_relative 'reporter'
10
+
11
+ module Transcode
12
+ # Transcodes any video file to m4v format.
13
+ class Transcoder
14
+ def initialize
15
+ @cfg = Configuration.new
16
+ @rep = Reporter.new(@cfg.act?, "#{@cfg.dir} -> #{@cfg.out}", @cfg.wid)
17
+ end
18
+
19
+ # Runs command and prints output instantly. Returns true on success.
20
+ def run(cmd)
21
+ cmd += ' 2>&1'
22
+ puts "Run: #{cmd}."
23
+ IO.popen(cmd).each do |line|
24
+ puts line.chomp
25
+ end.close
26
+ $CHILD_STATUS.success?
27
+ end
28
+
29
+ def m4v_cmd(file, aud, sub, tit)
30
+ c = 'transcode-video --m4v --no-log --preset veryslow'\
31
+ " --output #{@cfg.out}"
32
+ unless tit == '0'
33
+ c += "/#{File.basename(file.shellescape)}-#{tit}.m4v"
34
+ c += " --title #{tit}"
35
+ end
36
+ c += " --main-audio #{aud}" unless aud == '0'
37
+ c += " --burn-subtitle #{sub}" unless sub == '0'
38
+ c + " #{file.shellescape}"
39
+ end
40
+
41
+ # Converts files, audibale, subtitles and titles arrays to array:
42
+ # [ file1 [ aud1, sub1, tit1 ] ]
43
+ # [ file2 [ aud2, sub2, tit2 ] ]
44
+ def data
45
+ @data ||= @cfg.files.zip([@cfg.aud, @cfg.sub, @cfg.tit].transpose)
46
+ end
47
+
48
+ def m4v
49
+ data.each do |f, as|
50
+ res = @cfg.act? ? run(m4v_cmd(f, as[0], as[1], as[2])) : true
51
+ @rep.add(f, res, as[0], as[1], as[2])
52
+ end
53
+ end
54
+
55
+ def mp3_cmd(file)
56
+ file = file.shellescape
57
+ "ffmpeg -i #{file} -vn -ar 44100 -ac 2 -ab 192k -f mp3 "\
58
+ "#{@cfg.out}/#{File.basename(file, '.*')}.mp3"
59
+ end
60
+
61
+ def scn_cmd(file)
62
+ "transcode-video --scan #{file.shellescape}"
63
+ end
64
+
65
+ def do # rubocop:disable Metrics/AbcSize
66
+ if @cfg.mp3?
67
+ @cfg.files.each { |f| @rep.add(f, @cfg.act? ? run(mp3_cmd(f)) : true) }
68
+ elsif @cfg.sca?
69
+ @cfg.files.each { |f| @rep.add(f, run(scn_cmd(f))) }
70
+ else
71
+ m4v
72
+ end
73
+ @rep.do
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ # All methods are static.
7
+ class Utils
8
+ class << self
9
+ SEP = '~'
10
+ def trim(src, lim)
11
+ return src if src.length <= lim
12
+
13
+ beg = fin = (lim - SEP.length) / 2
14
+ beg -= 1 if lim.odd?
15
+ src[0..beg] + SEP + src[-fin..-1]
16
+ end
17
+ end
18
+ end
19
+
20
+ # Returns string with humanized time interval.
21
+ class Timer
22
+ DIC = [
23
+ [60, :seconds, :second],
24
+ [60, :minutes, :minute],
25
+ [24, :hours, :hour],
26
+ [1000, :days, :day]
27
+ ].freeze
28
+
29
+ def initialize
30
+ @sta = Time.now
31
+ end
32
+
33
+ def read
34
+ humanize(Time.now - @sta)
35
+ end
36
+
37
+ def humanize(sec)
38
+ return 'less than a second' if sec < 1
39
+
40
+ DIC.map do |cnt, nms, nm1|
41
+ next if sec <= 0
42
+
43
+ sec, n = sec.divmod(cnt)
44
+ "#{n.to_i} #{n.to_i != 1 ? nms : nm1}"
45
+ end.compact.reverse.join(' ')
46
+ end
47
+ end
48
+
49
+ # Adds natural sort method. This converts something like "Filename 10" into a
50
+ # simple array with floats in place of numbers [ "Filename", 10.0 ]. See:
51
+ # https://stackoverflow.com/questions/4078906/is-there-a-natural-sort-by-method-for-ruby
52
+ class String
53
+ def naturalized
54
+ scan(/[^\d\.]+|[\d\.]+/).collect { |f| f.match(/\d+(\.\d+)?/) ? f.to_f : f }
55
+ end
56
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # vi:ts=2 sw=2 tw=79 et lbr wrap
4
+ # Copyright 2018-present David Rabkin
5
+
6
+ module Transcode
7
+ VERSION = '1.0.1'
8
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
4
+
5
+ require 'transcode'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'transcode'
9
+ s.version = Transcode::VERSION
10
+ s.required_ruby_version = '>= 2.3'
11
+ s.summary = 'Tools to transcode batch of video files.'
12
+ s.description = <<-HERE
13
+ Transcode is a wraper on Video Transcoding.
14
+ HERE
15
+ s.license = 'BSD-2-Clause'
16
+ s.author = 'David Rabkin'
17
+ s.email = 'pub@rabkin.co.il'
18
+ s.homepage = 'https://github.com/rdavid/transcode'
19
+ s.files = Dir['{bin,lib}/**/*'] + Dir['[A-Z]*'] + ['transcode.gemspec']
20
+ s.executables = ['transcode']
21
+ s.extra_rdoc_files = ['LICENSE', 'README.md']
22
+ s.require_paths = ['lib']
23
+ s.add_runtime_dependency 'pidfile', '0.3.0'
24
+ s.add_runtime_dependency 'terminal-table', '1.8.0'
25
+ s.add_runtime_dependency 'video_transcoding', '0.25.3'
26
+ s.add_development_dependency 'rubocop', '0.76.0'
27
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transcode
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Rabkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pidfile
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: terminal-table
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: video_transcoding
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.25.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.25.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.76.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.76.0
69
+ description: " Transcode is a wraper on Video Transcoding.\n"
70
+ email: pub@rabkin.co.il
71
+ executables:
72
+ - transcode
73
+ extensions: []
74
+ extra_rdoc_files:
75
+ - LICENSE
76
+ - README.md
77
+ files:
78
+ - LICENSE
79
+ - README.md
80
+ - bin/transcode
81
+ - lib/transcode.rb
82
+ - lib/transcode/configurator.rb
83
+ - lib/transcode/reporter.rb
84
+ - lib/transcode/transcoder.rb
85
+ - lib/transcode/utils.rb
86
+ - lib/transcode/version.rb
87
+ - transcode.gemspec
88
+ homepage: https://github.com/rdavid/transcode
89
+ licenses:
90
+ - BSD-2-Clause
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '2.3'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.1.4
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Tools to transcode batch of video files.
111
+ test_files: []