cli_chef 1.0.0

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: 416dd42c45a904147359ae950761aa5bdfbcdab7bcff5d175ea191f98fe172b2
4
+ data.tar.gz: 0ea2d4a6b54f39b8861a20028391ebad0b63e8867b8eafddb7131b2b26bbc1c4
5
+ SHA512:
6
+ metadata.gz: c062117a34fadd35f7cf88d1c0b1e6d3cdc27a871eff561b02a22727e6cae6b3530e9ba50344828fe17b6e7c70c43f697e5c458bd3c52bcf6e73b69ecbc4c548
7
+ data.tar.gz: 97fb7864b53d18eb8bd7718de21e5a38446c8df2ed5c0b9186f2c9e9981bd4ebf14815431abf63e98c8f0f4459d096b7471d8e1b13db0a5ec3dfe364f1865a2f
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.6
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cli_chef.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Brandon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # CLIChef
2
+
3
+ CLI Chef is a simple command line interface wrapper for Ruby. It is made to make writing wrappers an easy and flexible process.
4
+ Basic examples of how to use CLIChef are included in the wrappers directory.
5
+ These are also full functional CLI wrappers for the following apps:
6
+
7
+ - 7Zip
8
+ - MediaInfo
9
+ - Handbrake
10
+
11
+ ## Installation
12
+
13
+
14
+ __Note:__ Currently this gem is not available via RubyGems.
15
+ Once it is the following is how to install it.
16
+ For now, grab it from github.
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'cli_chef'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install cli_chef
31
+
32
+ ## Usage
33
+
34
+ CLI Chef is made up of the following components:
35
+
36
+ - *Cookbook*: A cookbook is the base wrapper class of CLI Chef. The cookbook handles interaction with the CLI. It stores all possible ingredients. It also contains an exit code map, default application locations and the results of the previously run command.
37
+ - *Ingredient*: An ingredient is essentially a CLI argument. It contains a name for the argument, its flag (if it has one) and its value (again, if it has one). There are many other properties in ingredients that control how it is constructed. For more detailed information, check out the source code or one of the sample wrappers.
38
+
39
+ ### Examples
40
+
41
+ A few example wrappers comes with this gem. Check them out under the /lib/cli_chef/apps
42
+ directory. They are complete and showcase how easy it is to setup a basic wrapper.
43
+
44
+ ## Pre-Built Wrappers
45
+
46
+ CLI Chef ships with several pre-made wrappers for 7Zip, HandBrake and MediaInfo. Below is an example of how to include these in your projects.
47
+
48
+ ```ruby
49
+ # NOTE: You do not have to require cli_chef separately as the wrappers will if it is not already loaded.
50
+ require 'cli_chef'
51
+
52
+ # Load 7Zip
53
+ require 'cli_chef/apps/sevenzip'
54
+
55
+ # Load HandBrake
56
+ require 'cli_chef/apps/hand_brake'
57
+
58
+ # Load MediaInfo
59
+ require 'cli_chef/apps/media_info'
60
+ ```
61
+
62
+ _NOTE: For the wrappers to work you must have the CLI executables for each of the above applications installed._
63
+
64
+ ## Development
65
+
66
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
67
+
68
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cli_chef. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
73
+
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cli_chef"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cli_chef/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cli_chef"
8
+ spec.version = CLIChef::VERSION
9
+ spec.authors = ["Brandon Black"]
10
+ spec.email = ["d2sm10@hotmail.com"]
11
+
12
+ spec.summary = %q{CLI Chef is a simple and quick CLI wrapper framework for Ruby.}
13
+ spec.description = %q{CLI Chef makes building command line wrappers easy and simple to incorporate with your Ruby projects.}
14
+ spec.homepage = "http://github.com/bblack16/cli-chef"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.10"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec"
33
+
34
+ spec.add_runtime_dependency 'bblib', '~> 1.0'
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBLib
4
+ def self.scan_files_and_archives(*args)
5
+ sz = SevenZip.new
6
+ BBLib.scan_files(*args).flat_map do |file|
7
+ if archive?(file)
8
+ sz.list(file).flat_map { |afile, _data| "#{file}//#{afile}" }
9
+ else
10
+ file
11
+ end
12
+ end
13
+ end
14
+
15
+ ARCHIVE_EXTENSIONS = %w[bz2 bzip2 tbz2 tbz gz gzip tgz tar wim swm xz txz zip zipx jar xpi odt ods
16
+ docx xlsx epub apm ar a deb lib arj cab chm chw chi chq msi msp doc xls ppt cpio
17
+ cramfs dmg ext ext2 ext3 ext4 img fat img hfs hfsx hxs hxi hxr hxq hxw lit ihex
18
+ iso img lzh lha lzma mbr mslz mub nsis ntfs img mbr rar r00 rpm ppmd qcow qcow2
19
+ qcow2c 001 squashfs udf iso img scap uefif vdi vhd vmdk wim esd xar pkg z taz ].freeze
20
+
21
+ def self.archive?(file)
22
+ ARCHIVE_EXTENSIONS.any? { |ext| file.to_s.end_with?(".#{ext}") }
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ require 'bblib' unless defined?(BBLib)
2
+ require 'open3'
3
+
4
+ require_relative 'cli_chef/cli_chef'
@@ -0,0 +1,149 @@
1
+ require 'cli_chef' unless defined?(CLIChef::VERSION)
2
+ require_relative 'hand_brake/hand_brake_job'
3
+
4
+ class HandBrake < CLIChef::Cookbook
5
+
6
+ self.description = 'HandBrake is a tool for converting video from nearly any format to a selection of modern, widely supported codecs.'
7
+ self.default_job_class = HandBrakeJob
8
+
9
+ def help
10
+ run!(help: nil).body
11
+ end
12
+
13
+ def version
14
+ run!(version: nil).body.scan(/(?<=HandBrake\s)\d+\.\d+\.\d+/i).first
15
+ end
16
+
17
+ def encode(input, output, opts = {})
18
+ run(opts.merge(input: input, output: output)) do |line, stream, job|
19
+ job.task_count = line.scan(/(?<=of )\d+/).first.to_i
20
+ job.task = line.scan(/\d+(?= of)/).first.to_i
21
+ job.fps = line.scan(/\d+\.\d+(?= fps)/).first.to_f
22
+ job.average_fps = line.scan(/(?<=avg )\d+\.\d+(?= fps)/).first.to_f
23
+ job.eta = line.scan(/(?<=ETA ).*?(?=\))/).first.parse_duration rescue nil
24
+ percent = line.scan(/\d+\.\d+(?= %)/).first.to_f
25
+ if line.include?('Encode done!')
26
+ percent = 100.0
27
+ elsif job.task_count > 2 && percent > 0
28
+ percent /= job.task_count
29
+ percent += 50 * job.task
30
+ end
31
+ job.percent = percent unless percent < job.percent.to_i
32
+ end
33
+ end
34
+
35
+ add_exit_codes(
36
+ { code: 0, description: 'Clean exit' },
37
+ { code: 1, description: 'Cancelled', error: true },
38
+ { code: 2, description: 'Invalid Input', error: true },
39
+ { code: 3, description: 'Initialization Error', error: true },
40
+ { code: 4, description: 'Unknown Error', error: true }
41
+ )
42
+
43
+ add_default_locations(
44
+ 'handbrakecli.exe',
45
+ 'hanbrakecli',
46
+ 'C:/Program Files/Handbrake/HandBrakeCLI.exe',
47
+ 'C:/Program Files(x86)/Handbrake/HandBrakeCLI.exe',
48
+ 'C:/Handbrake/HandBrakeCLI.exe'
49
+ )
50
+
51
+ add_ingredients(
52
+ { name: :help, description: 'Print help', flag: '--help', allowed_values: [nil], aliases: [:h] },
53
+ { name: :update, description: 'Check for updates and exit', flag: '--update', allowed_values: [nil], aliases: [] },
54
+ { name: :verbose, description: 'Be verbose (optional argument: logging level)', flag: '--verbose', allowed_values: [1, 2, 3], aliases: [] },
55
+ { name: :preset, description: 'Use a built-in preset. Capitalization matters, and if the preset name has spaces, surround it with double quotation marks', flag: '--preset', allowed_values: [String], aliases: [] },
56
+ { name: :preset_list, description: 'See a list of available built-in presets', flag: '--preset-list', allowed_values: [nil], aliases: [] },
57
+ { name: :no_dvd_nav, description: 'Do not use dvdnav for reading DVDs', flag: '--no-dvdnav', allowed_values: [nil], aliases: [] },
58
+ { name: :no_open_cl, description: 'Disable use of OpenCL', flag: '--no-opencl', allowed_values: [nil], aliases: [] },
59
+ { name: :input, description: 'Set input device', flag: '--input', allowed_values: [String], aliases: [] },
60
+ { name: :title, description: 'Select a title to encode (0 to scan all titles only', flag: '--title', allowed_values: [Integer, String], aliases: [] },
61
+ { name: :min_duration, description: 'Set the minimum title duration (in seconds). Shorter titles will not be scanned.', flag: '--min-duration', allowed_values: [Integer], aliases: [] },
62
+ { name: :scan, description: 'Scan selected title only.', flag: '--scan', allowed_values: [nil], aliases: [] },
63
+ { name: :main_feature, description: 'Detect and select the main feature title.', flag: '--main-feature', allowed_values: [nil], aliases: [] },
64
+ { name: :chapters, description: 'Select chapters (e.g. \'1-3\' for chapters 1 to 3, or \'3\' for chapter 3 only', flag: '--chapters', allowed_values: [], aliases: [] },
65
+ { name: :angle, description: 'Select the video angle (DVD or Blu-ray only)', flag: '--angle', allowed_values: [Integer], aliases: [] },
66
+ { name: :previews, description: 'Select how many preview images are generated, and whether or not they\'re stored to disk (0 or 1).', flag: '--previews', allowed_values: [String], aliases: [] },
67
+ { name: :start_at_preview, description: 'Start encoding at a given preview.', flag: '--start-at-preview', allowed_values: [(1..30)], aliases: [] },
68
+ { name: :start_at, description: 'Start encoding at a given frame, duration (in seconds), or pts (on a 90kHz clock)', flag: '--start-at', allowed_values: [Object], aliases: [] },
69
+ { name: :stop_at, description: 'Stop encoding at a given frame, duration (in seconds), or pts (on a 90kHz clock)', flag: '--stop-at', allowed_values: [Object], aliases: [] },
70
+ { name: :output, description: 'Set output file name', flag: '--output', allowed_values: [String], aliases: [:out] },
71
+ { name: :format, description: 'Set output container format (av_mp4/av_mkv)', flag: '--format', allowed_values: ['mp4', 'mkv', :mp4, :mkv], aliases: [] },
72
+ { name: :markers, description: 'Add chapter markers', flag: '--markers', allowed_values: [String], aliases: [] },
73
+ { name: :large_file, description: 'Create 64-bit mp4 files that can hold more than 4 GB of data. Note: breaks pre-iOS iPod compatibility.', flag: '--large-file', allowed_values: [nil], aliases: [] },
74
+ { name: :optimize, description: 'Optimize mp4 files for HTTP streaming (\'fast start\')', flag: '--optimize', allowed_values: [nil], aliases: [] },
75
+ { name: :ipod_atom, description: 'Mark mp4 files so 5.5G iPods will accept them', flag: '--ipod-atom', allowed_values: [nil], aliases: [] },
76
+ { name: :use_open_cl, description: 'Use OpenCL where applicable', flag: '--use-opencl', allowed_values: [nil], aliases: [] },
77
+ { name: :use_hwd, description: 'Use DXVA2 hardware decoding', flag: '--use-hwd', allowed_values: [nil], aliases: [] },
78
+ { name: :encoder, description: 'Set video library encoder. Options: x264/x265/mpeg4/mpeg2/VP8/theora', flag: '--encoder', allowed_values: ['x264', 'x265', 'mpeg4', 'mpeg2', 'VP8', 'theora', :x264, :x265, :mpeg4, :mpeg2, :VP8, :vp8, :theora], aliases: [] },
79
+ { name: :encoder_preset, description: 'Adjust video encoding settings for a particular speed/efficiency tradeoff (encoder-specific)', flag: '--encoder-preset', allowed_values: [Object], aliases: [] },
80
+ { name: :encoder_preset_list, description: 'List supported --encoder-preset values for the specified video encoder', flag: '--encoder-preset-list', allowed_values: [nil], aliases: [] },
81
+ { name: :encoder_tune, description: 'Adjust video encoding settings for a particular type of souce or situation (encoder-specific)', flag: '--encoder-tune', allowed_values: [Object], aliases: [] },
82
+ { name: :encoder_tune_list, description: 'List supported --encoder-tune values for the specified video encoder', flag: '--encoder-tune-list', allowed_values: [nil], aliases: [] },
83
+ { name: :encopts, description: 'Specify advanced encoding options in the same style as mencoder (all encoders except theora): option1=value1:option2=value2', flag: '--encopts', allowed_values: [Object], aliases: [] },
84
+ { name: :encoder_profile, description: 'Ensures compliance with the requested codec profile (encoder-specific)', flag: '--encoder-profile', allowed_values: [Object], aliases: [] },
85
+ { name: :encoder_profile_list, description: 'List supported --encoder-profile values for the specified video encoder', flag: '--encoder-profile-list', allowed_values: [nil], aliases: [] },
86
+ { name: :encoder_level, description: 'Ensures compliance with the requested codec level (encoder-specific)', flag: '--encoder-level', allowed_values: [Object], aliases: [] },
87
+ { name: :encoder_level_list, description: 'List supported --encoder-level values for the specified video encoder', flag: '--encoder-level-list', allowed_values: [nil], aliases: [] },
88
+ { name: :quality, description: 'Set video quality', flag: '--quality', allowed_values: [Integer], aliases: [] },
89
+ { name: :video_bitrate, description: 'Set video bitrate', flag: '--vb', allowed_values: [Integer], aliases: [:vb, :bitrate] },
90
+ { name: :two_pass, description: 'Use two-pass mode', flag: '--two-pass', allowed_values: [nil], aliases: [] },
91
+ { name: :turbo, description: 'When using 2-pass use \'turbo\' options on the 1st pass to improve speed (only works with x264)', flag: '--turbo', allowed_values: [nil], aliases: [] },
92
+ { name: :video_framerate, description: 'Set video framerate (5/10/12/15/23.976/24/25/29.97/30/50/59.94/60). Be aware that not specifying a framerate lets HandBrake preserve a source\'s time stamps, potentially creating variable framerate video', flag: '--rate', allowed_values: [Float, Integer], aliases: [:framerate] },
93
+ { name: :variable_framerate, description: 'VFR preserves the source timing.', flag: '--vfr', allowed_values: [nil], aliases: [] },
94
+ { name: :constant_framerate, description: 'CFR makes the output constant rate at the rate given by the -r flag (or the source\'s average rate if no -r is given)', flag: '--cfr', allowed_values: [nil], aliases: [] },
95
+ { name: :peak_limited_framerate, description: 'PFR doesn\'t allow the rate to go over the rate specified with the -r flag but won\'t change the source timing if it\'s below that rate.', flag: '--pfr', allowed_values: [nil], aliases: [] },
96
+ { name: :audio_tracks, description: 'Select audio track(s), separated by commas', flag: '--audio', allowed_values: [String], aliases: [] },
97
+ { name: :audio_encoder, description: 'Audio encoder(s): av_aac, fdk_aac, fdk_haac, copy:aac, ac3, copy:ac3, copy:dts, copy:dtshd, mp3, copy:mp3, vorbis, flac16, flac24, copy. copy:* will passthrough the corresponding audio unmodified to the muxer if it is a supported passthrough audio type. Separated by commas for more than one audio track.', flag: '--aencoder', allowed_values: ['av_aac', 'fdk_aac', 'fdk_haac', 'copy:aac', 'ac3', 'copy:ac3', 'copy:dts', 'copy:dtshd', 'mp3', 'copy:mp3', 'vorbis', 'flac16', 'flac24', 'copy', :av_aac, :fdk_aac, :fdk_haac, :attr_array_removeraac, :ac3, :copy_ac3, :copy_dts, :copy_dtshd, :mp3, :copy_mp3, :vorbis, :flac16, :flac24, :copy], aliases: [] },
98
+ { name: :audio_copy_mask, description: 'Set audio codecs that are permitted when the \'copy\' audio encoder option is specified (aac/ac3/dts/dtshd/mp3, default: all).', flag: '--audio-copy-mast', allowed_values: ['all', 'aac', 'ac3', 'dts', 'dtshd', 'mp3', :all, :aac, :ac3, :drs, :dtshd, :mp3], aliases: [] },
99
+ { name: :audio_fallback, description: 'Set audio codec to use when it is not possible to copy an audio track without re-encoding.', flag: '--audio-fallback', allowed_values: ['av_aac', 'fdk_aac', 'fdk_haac', 'copy:aac', 'ac3', 'copy:ac3', 'copy:dts', 'copy:dtshd', 'mp3', 'copy:mp3', 'vorbis', 'flac16', 'flac24', 'copy', :av_aac, :fdk_aac, :fdk_haac, :attr_array_removeraac, :ac3, :attr_array_removerac3, :attr_array_removerdts, :attr_array_removerdtshd, :mp3, :attr_array_removermp3, :vorbis, :flac16, :flac24, :copy], aliases: [] },
100
+ { name: :audio_bitrate, description: 'Set audio bitrate(s) (default: depends on the selected codec, mixdown and samplerate). Separated by commas for more than one audio track.', flag: '--ab', allowed_values: [String, Integer], aliases: [:ab] },
101
+ { name: :audio_quality, description: 'Set audio quality metric (default: depends on the selected codec). Separated by commas for more than one audio track.', flag: '--aq', allowed_values: [String, Integer], aliases: [] },
102
+ { name: :audio_compression, description: 'Set audio compression metric (default: depends on the selected codec). Separated by commas for more than one audio track.', flag: '--ac', allowed_values: [String, Integer], aliases: [] },
103
+ { name: :mixdown, description: 'Format(s) for audio downmixing/upmixing:, mono, left_only, right_only, stereo, dpl1, dpl2, 5point1, 6point1, 7point1, 5_2_lfe .Separated by commas for more than one audio track.', flag: '--mixdown', allowed_values: ['mono', 'left_only', 'right_only', 'stereo', 'dp11', 'dp12', '5point1', '6point1', '7point1', '5_2_lfe', String, Symbol], aliases: [] },
104
+ { name: :normalize_mix, description: 'Normalize audio mix levels to prevent clipping. Separated by commas for more than one audio track. 0 = Disable Normalization (default) 1 = Enable Normalization', flag: '--normalize-mix', allowed_values: [0, 1], aliases: [] },
105
+ { name: :audio_samplerate, description: 'Set audio samplerate(s) (8/11.025/12/16/22.05/24/32/44.1/48 kHz). Separated by commas for more than one audio track.', flag: '--arate', allowed_values: [String, Float], aliases: [] },
106
+ { name: :dynamic_range_compression, description: 'Apply extra dynamic range compression to the audio, making soft sounds louder. Range is 1.0 to 4.0 (too loud), with 1.5 - 2.5 being a useful range. Separated by commas for more than one audio track.', flag: '--drc', allowed_values: [Float, Integer, String], aliases: [] },
107
+ { name: :gain, description: 'Amplify or attenuate audio before encoding. Does NOT work with audio passthru (copy). Values are in dB. Negative values attenuate, positive values amplify. A 1 dB difference is barely audible.', flag: '--gain', allowed_values: [Float, Integer, String], aliases: [] },
108
+ { name: :audio_dither, description: 'Apply dithering to the audio before encoding. Separated by commas for more than one audio track. Only supported by some encoders (fdk_aac/fdk_haac/flac16). Options: auto (default), none, rectangular, triangular, triangular_hp, triangular_ns', flag: '--adither', allowed_values: ['auto', 'none', 'rectangular', 'triangular', 'triangular_hp', 'triangular_ns', String, Symbol], aliases: [] },
109
+ { name: :audio_track_name, description: 'Audio track name(s). Separated by commas for more than one audio track.', flag: '--aname', allowed_values: [String], aliases: [] },
110
+ { name: :width, description: 'Set picture width', flag: '--width', allowed_values: [Integer], aliases: [:w] },
111
+ { name: :height, description: 'Set picture height', flag: '--height', allowed_values: [Integer], aliases: [:h] },
112
+ { name: :crop, description: 'Set cropping values (default: autocrop)', flag: '--crop', allowed_values: [String, Symbol], aliases: [] },
113
+ { name: :loose_crop, description: 'Always crop to a multiple of the modulus. Specifies the maximum number of extra pixels which may be cropped (default: 15)', flag: '--loose-crop', allowed_values: [String], aliases: [] },
114
+ { name: :max_height, description: 'Set maximum height', flag: '--maxHeight', allowed_values: [Integer], aliases: [] },
115
+ { name: :max_width, description: 'Set maximum width', flag: '--maxWidth', allowed_values: [Integer], aliases: [] },
116
+ { name: :strict_anamorphic, description: 'Store pixel aspect ratio in video stream', flag: '--strict-anamorphic', allowed_values: [nil], aliases: [] },
117
+ { name: :loose_anamorphic, description: 'Store pixel aspect ratio with specified width', flag: '--loose-anamorphic', allowed_values: [nil], aliases: [] },
118
+ { name: :custom_anamorphic, description: 'Store pixel aspect ratio in video stream and directly control all parameters.', flag: '--custom-anamorphic', allowed_values: [nil], aliases: [] },
119
+ { name: :display_width, description: 'Set the width to scale the actual pixels to at playback, for custom anamorphic.', flag: '--display-width', allowed_values: [Integer], aliases: [] },
120
+ { name: :keep_display_aspect, description: 'Preserve the source\'s display aspect ratio when using custom anamorphic', flag: '--keep-display-aspect', allowed_values: [nil], aliases: [] },
121
+ { name: :pixel_aspect, description: 'Set a custom pixel aspect for custom anamorphic (--display-width and --pixel-aspect are mutually exclusive and the former will override the latter)', flag: '--pixel-aspect', allowed_values: [String], aliases: [] },
122
+ { name: :itu_par, description: 'Use wider, ITU pixel aspect values for loose and custom anamorphic, useful with underscanned sources', flag: '--itu-par', allowed_values: [nil], aliases: [] },
123
+ { name: :modulus, description: 'Set the number you want the scaled pixel dimensions to divide cleanly by. Does not affect strict anamorphic mode, which is always mod 2 (default: 16)', flag: '--modulus', allowed_values: [Integer], aliases: [] },
124
+ { name: :color_matrix, description: 'Set the color space signaled by the output. Values: 709, pal, ntsc, 601 (same as ntsc) (default: detected from source)', flag: '--color-matrix', allowed_values: [709, 'pal', 'ntsc', 601, :pal, :ntsc], aliases: [] },
125
+ { name: :deinterlace, description: 'Unconditionally deinterlaces all frames <fast/slow/slower/bob> or omitted (default settings) or <YM:FD> (default 0:-1)', flag: '--deinterlace', allowed_values: ['omitted', 'fast', 'slow', 'slower', 'bob', :omitted, :fast, :slow, :slower, :bob], aliases: [] },
126
+ { name: :decomb, description: 'Selectively deinterlaces when it detects combing <fast/bob> or omitted (default settings) or <MO:ME:MT:ST:BT:BX:BY:MG:VA:LA:DI:ER:NO:MD:PP:FD> (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)', flag: '--decomb', allowed_values: [String], aliases: [] },
127
+ { name: :detelecine, description: 'Detelecine (ivtc) video with pullup filter. Note: this filter drops duplicate frames to restore the pre-telecine framerate, unless you specify a constant framerate (--rate 29.97) <L:R:T:B:SB:MP:FD> (default 1:1:4:4:0:0:-1)', flag: '--detelecine', allowed_values: [String], aliases: [] },
128
+ { name: :denoise, description: 'Denoise video with hqdn3d filter <ultralight/light/medium/strong> or omitted (default settings) or <SL:SCb:SCr:TL:TCb:TCr> (default: 4:3:3:6:4.5:4.5)', flag: '--denoise', allowed_values: [String], aliases: [] },
129
+ { name: :nlmeans, description: 'Denoise video with nlmeans filter <ultralight/light/medium/strong> or omitted or <SY:OTY:PSY:RY:FY:PY:Sb:OTb:PSb:Rb:Fb:Pb:Sr:OTr:PSr:Rr:Fr:Pr> (default 8:1:7:3:2:0)', flag: '--nlmeans', allowed_values: [String], aliases: [] },
130
+ { name: :nimeans_tune, description: 'Tune nlmeans filter to content type. Note: only works in conjunction with presets ultralight/light/medium/strong. <none/film/grain/highmotion/animation> or omitted (default none)', flag: '--nlmeans-tune', allowed_values: ['none', 'film', 'grain', 'highmotion', 'animation', 'omitted', :none, :film, :grain, :highmotion, :animation, :omitted], aliases: [] },
131
+ { name: :deblock, description: 'Deblock video with pp7 filter (default 5:2)', flag: '--deblock', allowed_values: [String], aliases: [] },
132
+ { name: :rotate, description: 'Rotate image or flip its axes. Modes: (can be combined) 1 vertical flip, 2 horizontal flip, 4 rotate clockwise 90 degrees, Default: 3 (vertical and horizontal flip)', flag: '--rotate', allowed_values: [1, 2, 3, 4], aliases: [] },
133
+ { name: :grayscale, description: 'Grayscale encoding', flag: '--grayscale', allowed_values: [nil], aliases: [] },
134
+ { name: :subtitle_track, description: 'Select subtitle track(s), separated by commas. More than one output track can be used for one input. Example: \'1,2,3\' for multiple tracks. A special track name \'scan\' adds an extra 1st pass. This extra pass scans subtitles matching the language of the first audio or the language selected by --native-language. The one that\'s only used 10 percent of the time or less is selected. This should locate subtitles for short foreign language segments. Best used in conjunction with --subtitle-forced.', flag: '--subtitle', allowed_values: [String, Integer], aliases: [] },
135
+ { name: :subtitle_forced, description: 'Only display subtitles from the selected stream if the subtitle has the forced flag set. The values in \'string\' are indexes into the subtitle list specified with \'--subtitle\'. Separated by commas for more than one subtitle track. Example: \'1,2,3\' for multiple tracks. If \'string\' is omitted, the first track is forced.', flag: '--subtitle-forced', allowed_values: [String, Integer], aliases: [] },
136
+ { name: :subtitle_burned, description: 'Burn\' the selected subtitle into the video track. If \'number\' is omitted, the first track is burned. \'number\' is an index into the subtitle list specified with \'--subtitle\'.', flag: '--subtitle-burned', allowed_values: [String, Integer], aliases: [] },
137
+ { name: :subtitle_default, description: 'Flag the selected subtitle as the default subtitle to be displayed upon playback. Setting no default means no subtitle will be automatically displayed. If \'number\' is omitted, the first track is default. \'number\' is an index into the subtitle list specified with \'--subtitle\'.', flag: '--subtitle-default', allowed_values: [String, Integer], aliases: [] },
138
+ { name: :native_language, description: 'Specifiy your language preference. When the first audio track does not match your native language then select the first subtitle that does. When used in conjunction with --native-dub the audio track is changed in preference to subtitles. Provide the language\'s iso639-2 code (fre, eng, spa, dut, et cetera)', flag: '--native-language', allowed_values: [String], aliases: [] },
139
+ { name: :native_dub, description: 'Used in conjunction with --native-language requests that if no audio tracks are selected the default selected audio track will be the first one that matches the --native-language. If there are no matching audio tracks then the first matching subtitle track is used instead.', flag: '--native-dub', allowed_values: [String], aliases: [] },
140
+ { name: :srt_file, description: 'SubRip SRT filename(s), separated by commas.', flag: '--srt-file', allowed_values: [String], aliases: [] },
141
+ { name: :srt_codeset, description: 'Character codeset(s) that the SRT file(s) are encoded in, separated by commas. Use \'iconv -l\' for a list of valid codesets. If not specified, \'latin1\' is assumed', flag: '--srt-codeset', allowed_values: [String], aliases: [] },
142
+ { name: :srt_offset, description: 'Offset (in milliseconds) to apply to the SRT file(s), separated by commas. If not specified, zero is assumed. Offsets may be negative.', flag: '--srt-offset', allowed_values: [String], aliases: [] },
143
+ { name: :srt_lang, description: 'Language as an iso639-2 code fra, eng, spa et cetera) for the SRT file(s), separated by commas. If not specified, then \'und\' is used.', flag: '--srt-lang', allowed_values: [String], aliases: [] },
144
+ { name: :srt_default, description: 'Flag the selected srt as the default subtitle to be displayed upon playback. Setting no default means no subtitle will be automatically displayed. If \'number\' is omitted, the first srt is default. \'number\' is an 1 based index into the srt-file list', flag: '--srt-default', allowed_values: [String], aliases: [] },
145
+ { name: :srt_burn, description: 'Burn\' the selected srt subtitle into the video track. If \'number\' is omitted, the first srt is burned. \'number\' is an 1 based index into the srt-file list', flag: '--srt-burn', allowed_values: [String], aliases: [] },
146
+ { name: :version, description: 'Display the version number.', flag: '--version', allowed_values: [nil], aliases: [:v] }
147
+ )
148
+
149
+ end
@@ -0,0 +1,11 @@
1
+ class HandBrakeJob < CLIChef::Job
2
+
3
+ attr_int :task_count, :task, default: 1
4
+ attr_float :fps, :average_fps, default: 0
5
+
6
+ protected
7
+
8
+ def process_line(line, stream = :stdout)
9
+ self.result.body = self.result.body + line
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ require 'cli_chef' unless defined?(CLIChef::VERSION)
2
+
3
+ class MediaInfo < CLIChef::Cookbook
4
+ self.description = 'MediaInfo is a convenient unified display of the most relevant technical and tag data for video and audio files.'
5
+
6
+ add_exit_codes(
7
+ { code: 0, description: 'No error' },
8
+ { code: 1, description: 'Failure' }
9
+ )
10
+
11
+ add_default_locations(
12
+ 'C:/Program Files/MediaInfo/MediaInfo.exe',
13
+ 'C:/Program Files(x86)/MediaInfo/MediaInfo.exe'
14
+ )
15
+
16
+ add_ingredients(
17
+ { name: :help, description: 'Display the CLI help.', flag: '--help', allowed_values: [nil], aliases: [:h], boolean_argument: true },
18
+ { name: :version, description: 'Display MediaInfo version and exit', flag: '--Version', allowed_values: [nil], aliases: [:v], boolean_argument: true },
19
+ { name: :full, description: 'Full information Display (all internal tags)', flag: '-f', allowed_values: [nil], aliases: [:verbose], boolean_argument: true },
20
+ { name: :output_html, description: 'Full information Display with HTML tags', flag: '--Output=HTML', allowed_values: [nil], aliases: [:html], boolean_argument: true },
21
+ { name: :output_xml, description: 'Full information Display with XML tags', flag: '--Output=XML', allowed_values: [nil], aliases: [:xml], boolean_argument: true },
22
+ { name: :file, description: 'The file to get tags out of', flag: '', allowed_values: [String], aliases: [:input] }
23
+ )
24
+
25
+ def help
26
+ run!(help: true).body
27
+ end
28
+
29
+ def version
30
+ run!(version: true).body.scan(/(?<= v)\d+\.\d+.*/).first
31
+ end
32
+
33
+ def info(file, full = false)
34
+ run!(file: file, full: full).body.split("\n\n").hmap do |category|
35
+ lines = category.split("\n")
36
+ next if lines.empty?
37
+ [
38
+ lines.shift.strip.downcase.method_case,
39
+ lines.hmap do |line|
40
+ key, value = line.split(':', 2)
41
+ key = key.downcase.method_case.to_sym
42
+ [
43
+ key,
44
+ convert_value(key, value.strip)
45
+ ]
46
+ end
47
+ ]
48
+ end.keys_to_sym
49
+ end
50
+
51
+ protected
52
+
53
+ def convert_value(key, value)
54
+ case key
55
+ when :file_size
56
+ value.parse_file_size
57
+ when :duration
58
+ value.parse_duration
59
+ when :width, :height
60
+ value.to_i
61
+ else
62
+ value
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,138 @@
1
+ require 'cli_chef' unless defined?(CLIChef::VERSION)
2
+ require_relative 'sevenzip/archive'
3
+ require_relative 'sevenzip/util'
4
+
5
+ class SevenZip < CLIChef::Cookbook
6
+
7
+ SUPPORTED_ARCHIVES = [
8
+ '001', '7z', 'a', 'apm', 'ar', 'arj', 'bz2', 'bzip2', 'cab', 'chi', 'chm',
9
+ 'chq', 'chw', 'cpio', 'cramfs', 'deb', 'dmg', 'doc', 'docx', 'epub', 'esd',
10
+ 'ext', 'ext2', 'ext3', 'ext4', 'fat', 'gz', 'gzip', 'hfs', 'hfsx', 'hxi',
11
+ 'hxq', 'hxr', 'hxs', 'hxw', 'ihex', 'img', 'iso', 'jar', 'lha', 'lib',
12
+ 'lit', 'lzh', 'lzma', 'mbr', 'msi', 'mslz', 'msp', 'mub', 'nsis', 'ntfs',
13
+ 'ods', 'odt', 'pkg', 'ppmd', 'ppt', 'qcow', 'qcow2', 'qcow2c', 'r00', 'rar',
14
+ 'rpm', 'scap', 'squashfs', 'swm', 'tar', 'taz', 'tbz', 'tbz2', 'tgz', 'txz',
15
+ 'udf', 'uefif', 'vdi', 'vhd', 'vmdk', 'wim', 'xar', 'xls', 'xlsx', 'xpi',
16
+ 'xz', 'z', 'zip', 'zipx'
17
+ ].freeze
18
+
19
+ self.description = '7-Zip is a file archiver with a high compression ratio.'
20
+
21
+ add_exit_codes(
22
+ { code: 0, description: 'No error' },
23
+ { code: 1, description: 'Warning (Non fatal error(s)). For example, one or more files were locked by some other application, so they were not compressed.' },
24
+ { code: 2, description: 'Fatal error', error: true },
25
+ { code: 7, description: 'Command line error', error: true },
26
+ { code: 8, description: 'Not enough memory for operation', error: true },
27
+ { code: 255, description: 'User stopped the process', error: true }
28
+ )
29
+
30
+ add_default_locations(
31
+ 'C:/Program Files/7-Zip/7z.exe',
32
+ 'C:/Program Files(x86)/7-Zip/7z.exe',
33
+ 'C:/7-Zip/7z.exe',
34
+ '7za',
35
+ '7z'
36
+ )
37
+
38
+ add_ingredients(
39
+ { name: :add, description: 'Adds files to archive.', flag: 'a', allowed_values: [nil], aliases: [:a], boolean_argument: true, flag_delimiter: '' },
40
+ { name: :extract, description: 'Extracts files from an archive to the current directory or to the output directory.', flag: 'e', allowed_values: [nil], aliases: [:e], boolean_argument: true, flag_delimiter: '' },
41
+ { name: :extract_full_paths, description: 'Extracts files from an archive with their full paths in the current directory, or in an output directory if specified.', flag: 'x', allowed_values: [nil], aliases: [:x], boolean_argument: true, flag_delimiter: '' },
42
+ { name: :benchmark, description: 'Measures speed of the CPU and checks RAM for errors.', flag: 'b', allowed_values: [nil], aliases: [:b], boolean_argument: true, flag_delimiter: '' },
43
+ { name: :delete, description: 'Deletes files from archive.', flag: 'd', allowed_values: [nil], aliases: [:d], boolean_argument: true, flag_delimiter: '' },
44
+ { name: :list, description: 'Lists contents of archive.', flag: 'l', allowed_values: [nil], aliases: [:l], boolean_argument: true, flag_delimiter: '' },
45
+ { name: :test, description: 'Tests archive files.', flag: 't', allowed_values: [nil], aliases: [:t], boolean_argument: true, flag_delimiter: '' },
46
+ { name: :update, description: 'Update older files in the archive and add files that are not already in the archive.', flag: 'u', allowed_values: [nil], aliases: [:u], boolean_argument: true, flag_delimiter: '' },
47
+ { name: :archive, description: 'Specifies the name of the archive', flag: nil, allowed_values: [String, Array], aliases: [:file], flag_delimiter: '' },
48
+ { name: :include, description: 'Specifies additional include filenames and wildcards.', flag: '-i', allowed_values: [String], aliases: [], flag_delimiter: '' },
49
+ { name: :method, description: 'Specifies the compression method.', flag: '-m', allowed_values: [String], aliases: [], flag_delimiter: '' },
50
+ { name: :password, description: 'Specifies password.', flag: '-p', allowed_values: [String], aliases: [], flag_delimiter: '' },
51
+ { name: :recurse, description: 'Specifies the method of treating wildcards and filenames on the command line. -r Enable recurse subdirectories. -r- Disable recurse subdirectories. This option is default for all commands. -r0 Enable recurse subdirectories only for wildcard names.', flag: '-r', allowed_values: [nil, '-', '0', 0], aliases: [], flag_delimiter: '' },
52
+ { name: :create_sfx, description: 'Creates self extracting archive.', flag: '-sfx', allowed_values: [nil, '7z.sfx', 'tzCon.sfx', '7zS.sfx', '7zSD.sfx'], aliases: [], flag_delimiter: '' },
53
+ { name: :stdin, description: 'Causes 7-Zip to read data from stdin (standard input) instead of from disc files.', flag: '-si', allowed_values: [nil, String], aliases: [], flag_delimiter: '' },
54
+ { name: :stdout, description: 'Causes 7-Zip to write output data to stdout (standard output stream).', flag: '-so', allowed_values: [nil, String], aliases: [], flag_delimiter: '' },
55
+ { name: :compress_shared_files, description: 'Compresses files open for writing by another applications.', flag: '-ssw', allowed_values: [nil], aliases: [], flag_delimiter: '' },
56
+ { name: :type, description: 'Specifies the type of archive.', flag: '-t', allowed_values: ['*', '7z', 'xz', 'split', 'zip', 'gzip', 'bzip2', 'tar', 'mbr', 'vhd', 'udf'], aliases: [], flag_delimiter: '' },
57
+ { name: :update_switch, description: 'Specifies how to update files in an archive and (or) how to create new archives.', flag: '-u', allowed_values: [], aliases: [], flag_delimiter: '' },
58
+ { name: :volumes, description: 'Specifies volume sizes.', flag: '-v', allowed_values: [/\d+[bkmg]/i], aliases: [], flag_delimiter: '' },
59
+ { name: :working_dir, description: 'Sets the working directory for the temporary base archive.', flag: '-w', allowed_values: [String], aliases: [], flag_delimiter: '' },
60
+ { name: :exclude, description: 'Specifies which filenames or wildcarded names must be excluded from the operation.', flag: '-x', allowed_values: [String], aliases: [], flag_delimiter: '' },
61
+ { name: :include_archives, description: 'Specifies additional include archive filenames and wildcards.', flag: '-ai', allowed_values: [String], aliases: [], flag_delimiter: '' },
62
+ { name: :disable_parsing, description: 'Disables parsing of the archive_name field on the command line.', flag: '-an', allowed_values: [nil], aliases: [], flag_delimiter: '' },
63
+ { name: :overwrite, description: 'Specifies the overwrite mode during extraction, to overwrite files already present on disk.', flag: '-ao', allowed_values: %w(s a u t), aliases: [:overwrite_mode], flag_delimiter: '' },
64
+ { name: :exclude_archives, description: 'Specifies archives to be excluded from the operation.', flag: '-ax', allowed_values: [String], aliases: [], flag_delimiter: '' },
65
+ { name: :output_dir, description: 'Specifies a destination directory where files are to be extracted.', flag: '-o', allowed_values: [String], aliases: [:output_directory, :output], flag_delimiter: '' },
66
+ { name: :assume_yes, description: 'Disables most of the normal user queries during 7-Zip execution.', flag: '-y', allowed_values: [nil], aliases: [:yes, :assume, :answer_yes], boolean_argument: true, flag_delimiter: '' },
67
+ { name: :show_technical_information, description: 'Sets technical mode for l (List) command.', flag: '-slt', allowed_values: [nil], aliases: [:slt, :technical, :show_technical], boolean_argument: true, flag_delimiter: '' },
68
+ { name: :help, description: 'Display the CLI help.', flag: '-h', allowed_values: [nil], aliases: [:h], boolean_argument: true, flag_delimiter: '' },
69
+ { name: :show_progress, description: 'Print progress to stdout.', flag: '-bsp1', allowed_values: [nil], aliases: [], boolean_argument: true, flag_delimiter: '' }
70
+ )
71
+
72
+ def help
73
+ run!(help: true).body
74
+ end
75
+
76
+ def version
77
+ help.scan(/(?<=7-zip )\d+\.\d+\s?\w*/i).first
78
+ end
79
+
80
+ def self.archive(path)
81
+ SevenZip::Archive.new(path: path)
82
+ end
83
+
84
+ bridge_method :archive
85
+
86
+ def list(archive, **opts)
87
+ args = { list: true, archive: archive, show_technical: true }.merge(opts.except(:list, :archive, :show_technical))
88
+ run!(args).body.split('----------', 2).last.split("\n\n").map do |details|
89
+ hash = details.split("\n").hmap do |attribute|
90
+ next if attribute.empty?
91
+ key, value = attribute.split(' = ', 2)
92
+ [key.downcase.gsub(/\s+/, '_').to_sym, value]
93
+ end
94
+ Archive::Item.new(hash)
95
+ end
96
+ end
97
+
98
+ def list_files(archive, **opts)
99
+ list(archive, **opts).select { |i| i.is_a?(SevenZip::Archive::File) }
100
+ end
101
+
102
+ def list_dirs(archive, **opts)
103
+ list(archive, **opts).select { |i| i.is_a?(SevenZip::Archive::Dir) }
104
+ end
105
+
106
+ def extract(archive, **opts)
107
+ type = opts[:full_path] == false ? :extract : :extract_full_paths
108
+ args = { type => true, file: archive, yes: true, show_progress: true }.merge(opts.except(type, :file, :yes))
109
+ run(**args) do |line, stream, job|
110
+ job.percent = line.extract_numbers.first if line =~ /\d+\%/
111
+ end
112
+ end
113
+
114
+ def extract!(archive, **opts)
115
+ extract(archive, opts.merge(synchronous: true))
116
+ end
117
+
118
+ [:add, :update, :delete].each do |type|
119
+ define_method(type) do |archive, file, **opts|
120
+ args = { type => true, archive: [archive, file], yes: true }.merge(opts.except(type, :archive, :yes))
121
+ run(**args)
122
+ end
123
+
124
+ define_method("#{type}!") do |archive, file, **opts|
125
+ send(type, archive, file, opts.merge(synchronous: true))
126
+ end
127
+ end
128
+
129
+ # TODO Have test return something better
130
+ def test(archive, **opts)
131
+ args = { test: true, file: archive, yes: nil }.merge(opts.except(:test, :file, :yes))
132
+ run(**args)
133
+ end
134
+
135
+ def test!(archive, **opts)
136
+ test(archive, opts.merge(synchronous: true))
137
+ end
138
+ end
@@ -0,0 +1,43 @@
1
+ require_relative 'item'
2
+ require_relative 'file'
3
+ require_relative 'dir'
4
+
5
+ class SevenZip < CLIChef::Cookbook
6
+ class Archive
7
+ include BBLib::Effortless
8
+
9
+ attr_file :path, required: true
10
+ attr_ary_of File, :files
11
+ attr_ary_of Dir, :dirs
12
+
13
+ after :path=, :load_archive
14
+
15
+ def size
16
+ ::File.size(path)
17
+ end
18
+
19
+ def extract(**opts)
20
+ SevenZip.extract(path, **opts)
21
+ end
22
+
23
+ def add(file, **opts)
24
+ SevenZip.add(path, file, **opts)
25
+ end
26
+
27
+ def delete(file, **opts)
28
+ SevenZip.delete(path, file, **opts)
29
+ end
30
+
31
+ protected
32
+
33
+ def load_archive
34
+ self.files.clear
35
+ items = SevenZip.list(self.path)
36
+ items.map { |i| i.archive = self }
37
+ self.dirs = items.find_all { |i| i.is_a?(Dir) }
38
+ self.files = items.find_all { |i| !i.is_a?(Dir) }
39
+ true
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ class SevenZip < CLIChef::Cookbook
2
+ class Archive
3
+ class Dir < Item
4
+
5
+ def self.folder
6
+ '+'
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ class SevenZip < CLIChef::Cookbook
2
+ class Archive
3
+ class File < Item
4
+
5
+ def self.folder
6
+ '-'
7
+ end
8
+
9
+ def filename
10
+ path.file_name
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ class SevenZip < CLIChef::Cookbook
2
+ class Archive
3
+ class Item
4
+ include BBLib::Effortless
5
+
6
+ attr_str :path, required: true
7
+ attr_of Archive, :archive, serialize: false
8
+ attr_int :size, :packed_size, :volume_index, :offset
9
+ attr_time :modified, :created, :accessed
10
+ attr_str :comment, :crc, :archive_method, :characteristics
11
+ attr_str :host_os, :version
12
+ attr_bool :encrypted, pre_proc: proc { |x| x == '-' }
13
+
14
+ init_type :loose
15
+
16
+ def self.folder
17
+ ''
18
+ end
19
+
20
+ setup_init_foundation(:folder) do |a, b|
21
+ a.to_s[0] == b.to_s[0]
22
+ end
23
+
24
+ def extract(**opts)
25
+ raise RunTimeError, "No archive has been set for this #{self.class} so it cannot be extracted." unless archive
26
+ SevenZip.extract([archive.path, self.path], **opts)
27
+ end
28
+
29
+ def delete(**opts)
30
+ raise RunTimeError, "No archive has been set for this #{self.class} so it cannot be deleted." unless archive
31
+ archive.delete(self.path)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ module BBLib
2
+ def self.scan_files_and_archives(path, *filters, recursive: false, archive_types: SevenZip::SUPPORTED_ARCHIVES, &block)
3
+ matches = []
4
+ filters = filters.map { |filter| filter.is_a?(Regexp) ? filter : /^#{Regexp.quote(filter).gsub('\\*', '.*')}$/ }
5
+ archive_filters = archive_types.map { |type| /\.#{Regexp.quote(type)}$/i }
6
+
7
+ scan_files(path, *(filters + archive_filters), recursive: recursive) do |file|
8
+ if archive_filters.any? { |filter| filter =~ file } && !filters.any? { |filter| filter =~ file }
9
+ match = false
10
+ begin
11
+ SevenZip.list_files(file).select do |archive|
12
+ match = true if filters.any? { |filter| filter =~ archive.path }
13
+ end
14
+ rescue => _e
15
+ # Nothing, archive check failed
16
+ end
17
+ if match
18
+ matches << file
19
+ yield file if block_given?
20
+ end
21
+ else
22
+ matches << file
23
+ yield file if block_given?
24
+ end
25
+ end
26
+ matches
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'version'
2
+ require_relative 'components/ingredient'
3
+ require_relative 'components/exit_code'
4
+ require_relative 'components/result'
5
+ require_relative 'components/job'
6
+ require_relative 'components/cookbook'
@@ -0,0 +1,104 @@
1
+ module CLIChef
2
+ class Cookbook
3
+ include BBLib::Effortless
4
+
5
+ attr_ary_of String, :default_locations, singleton: true, add_rem: true
6
+ attr_ary_of ExitCode, :exit_codes, singleton: true, add_rem: true
7
+ attr_ary_of Ingredient, :ingredients, singleton: true, add_rem: true
8
+ attr_str :description, singleton: true
9
+ attr_of Class, :default_job_class, default: CLIChef::Job, singleton: true
10
+
11
+ attr_str :path, allow_nil: true, default_proc: proc { |x| x.class.path }
12
+ attr_of Result, :result, default: nil, allow_nil: true, serialize: false
13
+
14
+ before :path, :check_default_locations
15
+
16
+ bridge_method :default_job_class, :default_locations, :exit_codes, :ingredients, :description, :ingredient
17
+
18
+ # Returns true if the path is either set to a valid file or can be found in the
19
+ # environment
20
+ def ready?
21
+ path && (File.exist?(path) || BBLib::OS.which(path))
22
+ end
23
+
24
+ # Executes a string as a command to this CLI wrapper in a job (threaded)
25
+ def execute(string, opts = {}, &block)
26
+ raise RuntimeError, "A valid path is currently not set for #{self.class}. Please set a valid path to the executable first." unless path
27
+ return execute!(string, opts.except(:synchronous), &block) if opts[:synchronous]
28
+ string = "#{clean_path} #{string}"
29
+ BBLib.logger.debug("About to run cmd: #{string}")
30
+ (opts.delete(:job_class) || default_job_class).new(opts.merge(command: string, parent: self)).tap do |job|
31
+ job.run(&block)
32
+ end
33
+ end
34
+
35
+ # Synchonous version of execute
36
+ def execute!(string, opts = {}, &block)
37
+ while (job ||= execute(string, opts, &block)).running?
38
+ # Nothing
39
+ end
40
+ job.result
41
+ end
42
+
43
+ # Runs a command within a Job (seperate thread)
44
+ # For when a command should be run asynchronously
45
+ def run(**args, &block)
46
+ return run!(args.except(:synchronous), &block) if args[:synchronous]
47
+ execute(prepare_args(args), &block)
48
+ end
49
+
50
+ # Blocking version of run that is not executed within a thread.
51
+ # For when a command should be run synchronously
52
+ def run!(**args, &block)
53
+ execute!(prepare_args(args), &block)
54
+ end
55
+
56
+ # Returns the full command line that would be run based on the given arguments
57
+ def prepare(**args)
58
+ "#{clean_path} #{prepare_args(args)}"
59
+ end
60
+
61
+ def prepare_args(**args)
62
+ args.map do |name, arg|
63
+ ingredient = self.ingredient(name)
64
+ raise ArgumentError, "Unknown parameter #{name} for #{self.class}." unless ingredient
65
+ ingredient.to_s(arg)
66
+ end.join(' ')
67
+ end
68
+
69
+ # Produces a dynamic help menu for this wrapper. Useful mostly for console or
70
+ # command line based interactions.
71
+ def menu
72
+
73
+ end
74
+
75
+ def self.prototype
76
+ @prototype ||= self.new
77
+ end
78
+
79
+ def self.method_missing(method, *args, &block)
80
+ prototype.respond_to?(method) ? prototype.send(method, *args, &block) : super
81
+ end
82
+
83
+ def self.respond_to_missing?(method, include_private = false)
84
+ prototype.respond_to?(method) || super
85
+ end
86
+
87
+ def self.ingredient(name)
88
+ ingredients.find { |i| i.match?(name) }
89
+ end
90
+
91
+ protected
92
+
93
+ def check_default_locations
94
+ return if @path
95
+ return unless found = default_locations.find { |path| File.exist?(path) || BBLib::OS.which(path) }
96
+ self.path = found
97
+ end
98
+
99
+ def clean_path
100
+ path.to_s.match?(/\s/) ? "\"#{path}\"" : path
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,19 @@
1
+ module CLIChef
2
+ class ExitCode
3
+ include BBLib::Effortless
4
+
5
+ attr_int :code, required: true, arg_at: 0
6
+ attr_str :description, default_proc: proc { |x| x.code == 0 ? 'Success' : 'Undefined Exit Code' }, arg_at: 1
7
+ attr_bool :error, default: false, arg_at: 2
8
+
9
+ def describe
10
+ "#{error? ? 'ERROR: ' : nil}#{description} (#{code})"
11
+ end
12
+ end
13
+
14
+ class ExitError < StandardError
15
+ def initialize(msg = 'The application returned an exit code that indicates an error')
16
+ super
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ module CLIChef
2
+ class Ingredient
3
+ include BBLib::Effortless
4
+ attr_sym :name
5
+ attr_ary_of Symbol, :aliases, pre_proc: proc { |x| [x].flatten.map { |i| i.to_s.to_sym } }
6
+ attr_str :flag, default: nil, allow_nil: true
7
+ attr_of Object, :argument, serialize: false
8
+ attr_str :delimiter, :flag_delimiter, default: ' '
9
+ attr_str :description
10
+ attr_bool :boolean_argument, default: false
11
+ attr_ary :allowed_values, add_rem: true
12
+
13
+ before :argument=, :allowed!, send_arg: true
14
+
15
+ def to_s(value = nil)
16
+ return '' if boolean_argument? && value == false
17
+ cleaned_arg = cleaned_argument(value)
18
+ argument == false ? '' : "#{flag}#{flag && !cleaned_arg.empty? ? flag_delimiter : nil}#{cleaned_arg}"
19
+ end
20
+
21
+ def cleaned_argument(value = nil)
22
+ allowed!(value)
23
+ return '' if boolean_argument?
24
+ [value].flatten(1).map(&:to_s).map do |arg|
25
+ arg.match?(/\s/) && !arg.encap_by?('"') ? "\"#{arg.gsub('"','\\"')}\"" : arg
26
+ end.join(delimiter)
27
+ end
28
+
29
+ def match?(name)
30
+ self.name == name || aliases.include?(name)
31
+ end
32
+
33
+ def allowed?(value)
34
+ return true if allowed_values.empty?
35
+ allowed_values.any? do |av|
36
+ av === value || av.nil? && (value == true || value == false)
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ def allowed!(value)
43
+ raise ArgumentError, "#{name} does not accept the value passed to it: #{value} (#{value.class})" unless allowed?(value)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,103 @@
1
+ module CLIChef
2
+ class Job
3
+ include BBLib::Effortless
4
+
5
+ attr_str :command
6
+ attr_of Thread, :thread, protected: true
7
+ attr_of Result, :result, default: nil, allow_nil: true
8
+ attr_float :percent, default: 0
9
+ attr_float :eta, default: nil, allow_nil: true
10
+ attr_of BBLib::TaskTimer, :timer, default: BBLib::TaskTimer.new, serialize: false
11
+ attr_of Object, :parent, default: nil, allow_nil: true, serialize: false
12
+ attr_hash :arguments, default: {}
13
+
14
+ setup_init_foundation(:type)
15
+
16
+ def self.type
17
+ self.class.to_s.split('::').last.method_case.to_sym
18
+ end
19
+
20
+ serialize_method :type
21
+ bridge_method :type
22
+
23
+ def run(&block)
24
+ timer.start
25
+ self.percent = 0.0
26
+ self.thread = Thread.new do
27
+ self.result = Result.new(body: '')
28
+ # TODO Have command killed when parent process dies
29
+ Open3.popen3(command) do |sin, out, err, pr|
30
+ self.result.pid = pr.pid
31
+ { stdout: out, stderr: err }.each do |name, stream|
32
+ stream.each do |line|
33
+ block ? yield(line, name, self) : process_line(line, name)
34
+ end
35
+ end
36
+ self.result.exit_code = code_for(pr.value.exitstatus)
37
+ end
38
+ self.percent = 100.0
39
+ self.timer.stop
40
+ self.result
41
+ end
42
+ running?
43
+ end
44
+
45
+ def code_for(code)
46
+ exit_codes.find do |ec|
47
+ ec.code == code
48
+ end || ExitCode.new(code)
49
+ end
50
+
51
+ def exit_codes
52
+ parent ? parent.exit_codes : []
53
+ end
54
+
55
+ def running?
56
+ thread && thread.alive?
57
+ end
58
+
59
+ def done?
60
+ !running?
61
+ end
62
+
63
+ def duration
64
+ timer.current || timer.last
65
+ end
66
+
67
+ def kill
68
+ return true unless thread
69
+ thread.kill
70
+ end
71
+
72
+ def error?
73
+ result && result.exit_code.error?
74
+ end
75
+
76
+ def success?
77
+ !error?
78
+ end
79
+
80
+ def eta
81
+ @eta || estimate_eta
82
+ end
83
+
84
+ def estimate_eta
85
+ return 0 unless percent && timer.current && percent.positive?
86
+ (100 - percent) / timer.current
87
+ end
88
+
89
+ protected
90
+
91
+ def process_line(line, stream = :stdout)
92
+ self.result.body = self.result.body + line
93
+ case stream
94
+ when :stderr
95
+ STDERR.puts line unless line.to_s.empty?
96
+ else
97
+ # Nothing happens with stdout in the default job class
98
+ # puts line
99
+ end
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,11 @@
1
+ module CLIChef
2
+ class Result
3
+ include BBLib::Effortless
4
+
5
+ attr_of Object, :body, default: nil, allow_nil: true
6
+ attr_str :cmd, default: nil, allow_nil: true
7
+ attr_of ExitCode, :exit_code, default: nil, allow_nil: true
8
+ attr_int :pid, default: 0
9
+
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module CLIChef
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cli_chef
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brandon Black
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bblib
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: CLI Chef makes building command line wrappers easy and simple to incorporate
70
+ with your Ruby projects.
71
+ email:
72
+ - d2sm10@hotmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - CODE_OF_CONDUCT.md
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - cli_chef.gemspec
88
+ - lib/bblib/bbfiles.rb
89
+ - lib/cli_chef.rb
90
+ - lib/cli_chef/apps/hand_brake.rb
91
+ - lib/cli_chef/apps/hand_brake/hand_brake_job.rb
92
+ - lib/cli_chef/apps/media_info.rb
93
+ - lib/cli_chef/apps/sevenzip.rb
94
+ - lib/cli_chef/apps/sevenzip/archive.rb
95
+ - lib/cli_chef/apps/sevenzip/dir.rb
96
+ - lib/cli_chef/apps/sevenzip/file.rb
97
+ - lib/cli_chef/apps/sevenzip/item.rb
98
+ - lib/cli_chef/apps/sevenzip/util.rb
99
+ - lib/cli_chef/cli_chef.rb
100
+ - lib/cli_chef/components/cookbook.rb
101
+ - lib/cli_chef/components/exit_code.rb
102
+ - lib/cli_chef/components/ingredient.rb
103
+ - lib/cli_chef/components/job.rb
104
+ - lib/cli_chef/components/result.rb
105
+ - lib/cli_chef/version.rb
106
+ homepage: http://github.com/bblack16/cli-chef
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.7.4
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: CLI Chef is a simple and quick CLI wrapper framework for Ruby.
130
+ test_files: []