iml 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eae63aa7eb68ca077d98468b14ac404b8ae95e55
4
+ data.tar.gz: a24253b80613ef0fda7d7bdde7b0b8ee28334e9d
5
+ SHA512:
6
+ metadata.gz: 89b69eee16a50ba2155cf03f7e4a7e5337dd97d3308046bc162cae60e78205b26237eb684fc82c6224d5ef819b710a5b7fa058d3dcccd2fc75fb99da36e5bdef
7
+ data.tar.gz: '096033b1a19b068f6931ce08575aba84b8e87b18626936c5556a114215c062d9980d5bedf8e08834222e4cd08d86f302e612b5a6ef45d3f4042232796e9bfcf6'
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .ruby-version
13
+ names.txt
14
+ output/*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,17 @@
1
+ Metrics/LineLength:
2
+ Max: 200
3
+ Exclude:
4
+ - 'lib/iml.rb'
5
+ Metrics/BlockLength:
6
+ Exclude:
7
+ - '*.gemspec'
8
+ - '*.rake'
9
+ Style/ClassAndModuleChildren:
10
+ Enabled: false
11
+ Documentation:
12
+ Enabled: false
13
+ Rails:
14
+ Enabled: true
15
+ AllCops:
16
+ Exclude:
17
+ - 'spec/**/*'
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4
5
+ - 2.5
6
+ before_install: gem install bundler -v 1.16.2
7
+ script: bundle exec rspec && bundle exec codeclimate-test-reporter
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in iml.gemspec
8
+ gemspec
@@ -0,0 +1,69 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ iml (0.1.4)
5
+ activesupport (~> 5.2)
6
+ tqdm (~> 0.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.2.0)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ codeclimate-test-reporter (1.0.7)
17
+ simplecov
18
+ coderay (1.1.2)
19
+ concurrent-ruby (1.0.5)
20
+ diff-lcs (1.3)
21
+ docile (1.3.1)
22
+ i18n (1.0.1)
23
+ concurrent-ruby (~> 1.0)
24
+ json (2.1.0)
25
+ method_source (0.9.0)
26
+ minitest (5.11.3)
27
+ pry (0.11.3)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.9.0)
30
+ rake (12.3.1)
31
+ rspec (3.7.0)
32
+ rspec-core (~> 3.7.0)
33
+ rspec-expectations (~> 3.7.0)
34
+ rspec-mocks (~> 3.7.0)
35
+ rspec-core (3.7.1)
36
+ rspec-support (~> 3.7.0)
37
+ rspec-expectations (3.7.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.7.0)
40
+ rspec-mocks (3.7.0)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.7.0)
43
+ rspec-support (3.7.1)
44
+ simplecov (0.16.1)
45
+ docile (~> 1.1)
46
+ json (>= 1.8, < 3)
47
+ simplecov-html (~> 0.10.0)
48
+ simplecov-html (0.10.2)
49
+ thread_safe (0.3.6)
50
+ tqdm (0.3.0)
51
+ tzinfo (1.2.5)
52
+ thread_safe (~> 0.1)
53
+ yard (0.9.14)
54
+
55
+ PLATFORMS
56
+ ruby
57
+
58
+ DEPENDENCIES
59
+ bundler (~> 1.16)
60
+ codeclimate-test-reporter (~> 1.0)
61
+ iml!
62
+ pry (~> 0.11)
63
+ rake (~> 12.3)
64
+ rspec (~> 3.7)
65
+ simplecov (~> 0.16)
66
+ yard (~> 0.9)
67
+
68
+ BUNDLED WITH
69
+ 1.16.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Adam Ladachowski
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,63 @@
1
+ [![Gem Version](https://img.shields.io/gem/v/iml.svg)](https://rubygems.org/gems/iml)
2
+ [![build status](https://travis-ci.org/aladac/iml.svg?branch=master)](https://travis-ci.org/aladac/iml)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/232800c6e4d8778937b2/maintainability)](https://codeclimate.com/github/aladac/iml/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/232800c6e4d8778937b2/test_coverage)](https://codeclimate.com/github/aladac/iml/test_coverage)
5
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/github/aladac/iml/master)
6
+
7
+ [![IML](https://github.com/aladac/iml/raw/master/doc/iml-logo.png)](https://rubygems.org/gems/iml)
8
+
9
+ *Intricate (Media) Matching Logic*
10
+
11
+ This is a media file handling library which is supposed to "guess" the intended type of media file based on specific naming patterns.
12
+ Its main purpose is to serve as runtime for renaming media files according to specified patterns.
13
+ The gem includes an executable `iml` through which rename operations are possible.
14
+
15
+ ## Installation
16
+
17
+ This gem requires ruby >= 2.4
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'iml'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install iml
32
+
33
+ ## Usage
34
+
35
+ ### Command Line
36
+
37
+ ```
38
+ Usage: iml [options] MEDIA_FILE [MEDIA_FILE] ...
39
+ -v, --[no-]verbose Run verbosely
40
+ -p, --[no-]pretend Dry run, do not move any files
41
+ -t, --target PATH Path to move media files to, default: current directory
42
+ -o, --movie-format FORMAT Format of the output path of movies, default: '%T (%Y).%f'
43
+ -O, --tv-format FORMAT Format of the output path of TV series, default: '%T/Season %s/%T - S%SE%E.%f'
44
+ -l, --list-formats Format description
45
+ -f, --force Use the force, override output files
46
+
47
+ $ iml -v Some.Cool.Movie.2018.1080p.BRRip.x264.aac-GROUP.mp4
48
+ I, [2018-07-06T13:38:29.836887 #70771] INFO -- : Some.Cool.Movie.2018.1080p.BRRip.x264.aac-GROUP.mp4 looks like a movie
49
+ I, [2018-07-06T13:38:29.837047 #70771] INFO -- : Moving Some.Cool.Movie.2018.1080p.BRRip.x264.aac-GROUP.mp4 to Some Cool Movie (2018).mp4
50
+ ```
51
+
52
+ ### Code
53
+
54
+ ```ruby
55
+ title = "An.Interesting.TV.Show.S01E01.1080p.WEBRIP.h265-GROUP.mkv"
56
+ => "An.Interesting.TV.Show.S01E01.1080p.WEBRIP.h265-GROUP.mkv"
57
+ IML::Text.new(title).detect
58
+ => #<IML::TVSeries title="An Interesting Tv Show", season="01", episode="01", quality="1080p", source="WEBRIP", codec="h265", group="GROUP", extension="mkv">
59
+ ```
60
+
61
+ ## License
62
+
63
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ desc 'Console'
9
+ task :console do
10
+ require 'bundler/setup'
11
+ require 'iml'
12
+ require 'pry'
13
+ Pry.start
14
+ end
15
+
16
+ task default: :spec
data/bin/iml ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'iml'
5
+ require 'optparse'
6
+
7
+ options = {}
8
+ @logger = Logger.new(STDOUT)
9
+
10
+ begin
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{File.basename $PROGRAM_NAME} [options] MEDIA_FILE [MEDIA_FILE] ..."
13
+
14
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
15
+ options[:verbose] = v
16
+ end
17
+ opts.on('-p', '--[no-]pretend', 'Dry run, do not move any files') do |p|
18
+ options[:pretend] = p
19
+ end
20
+ opts.on('-t', '--target PATH', 'Path to move media files to, default: current directory') do |t|
21
+ options[:target] = t
22
+ end
23
+ opts.on('-o', '--movie-format FORMAT', "Format of the output path of movies, default: '#{IML::Movie::DEFAULT_FORMAT}'") do |f|
24
+ options[:movie_format] = f
25
+ end
26
+ opts.on('-O', '--tv-format FORMAT', "Format of the output path of TV series, default: '#{IML::TVSeries::DEFAULT_FORMAT}'") do |f|
27
+ options[:tv_format] = f
28
+ end
29
+ opts.on('-l', '--list-formats', 'Format description') do |f|
30
+ options[:format] = f
31
+ end
32
+ opts.on('-f', '--force', 'Use the force, override output files') do |f|
33
+ options[:force] = f
34
+ end
35
+ puts opts if opts.default_argv.empty? && STDIN.tty?
36
+ end.parse!
37
+ rescue OptionParser::InvalidOption => e
38
+ @logger.error e.message
39
+ end
40
+
41
+ if options[:format]
42
+ puts <<~FORMAT
43
+ Formatting description:
44
+ %T - title
45
+ %t - episode title
46
+ %Y - year
47
+ %S - season number string as found in the input path
48
+ %s - season number integer
49
+ %E - episode number string as found in the input path
50
+ %e - episode number integer
51
+ %g - group name
52
+ %a - audio codec
53
+ %v - video codec
54
+ %f - file format / extension
55
+ %z - source
56
+ %q - quality
57
+
58
+ FORMAT
59
+ exit
60
+ end
61
+
62
+ input = STDIN.tty? ? ARGV : $stdin.readlines
63
+
64
+ input ||= ARGV
65
+
66
+ def file_operations(path, media, options)
67
+ pathname = media.pathname
68
+ dirname = pathname.dirname
69
+ unless dirname.to_s == '.'
70
+ @logger.info "Creating #{dirname}" if options[:verbose] || options[:pretend]
71
+ media.create_dir
72
+ end
73
+ @logger.info "Moving #{path} to #{pathname}" if options[:verbose] || options[:pretend]
74
+ media.move(path)
75
+ end
76
+
77
+ input.map { |p| Pathname(p.chomp) }.tqdm(desc: 'Processing input', leave: true).each do |path|
78
+ filename = path.basename.to_s
79
+ unless path.exist?
80
+ @logger.warn("File #{path} does not exist, skipping")
81
+ next
82
+ end
83
+ media = IML::Text.new(filename, options).detect
84
+ unless media
85
+ @logger.warn "#{path} doesn't look like a media file" if options[:verbose] || options[:pretend]
86
+ next
87
+ end
88
+ if media.movie?
89
+ media.format_string = options[:movie_format] if options[:movie_format]
90
+ @logger.info "#{path} looks like a movie"
91
+ elsif media.tv?
92
+ media.format_string = options[:tv_format] if options[:tv_format]
93
+ @logger.info "#{path} looks like a TV series"
94
+ end
95
+ file_operations(path, media, options)
96
+ end
@@ -0,0 +1,12 @@
1
+ dependencies:
2
+ activesupport: ~> 5.2
3
+ tqdm: ~> 0.3
4
+
5
+ development_dependencies:
6
+ codeclimate-test-reporter: ~> 1.0
7
+ bundler: ~> 1.16
8
+ rake: ~> 12.3
9
+ rspec: ~> 3.7
10
+ pry: ~> 0.11
11
+ simplecov: ~> 0.16
12
+ yard: ~> 0.9
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'iml/version'
6
+ require 'yaml'
7
+
8
+ Gem::Specification.new do |spec|
9
+ gemspec = YAML.load_file('gemspec.yml')
10
+ spec.name = 'iml'
11
+ spec.version = IML::VERSION
12
+ spec.authors = ['Adam Ladachowski']
13
+ spec.email = ['adam.ladachowski@gmail.com']
14
+
15
+ spec.summary = 'Media string and object manipulation library'
16
+ spec.description = 'Library which parses strings into media objects'
17
+ spec.homepage = 'https://github.com/aladac/iml'
18
+ spec.license = 'MIT'
19
+
20
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
21
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
22
+ if spec.respond_to?(:metadata)
23
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
+ else
25
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
26
+ 'public gem pushes.'
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.bindir = 'bin'
35
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
36
+ spec.require_paths = ['lib']
37
+
38
+ spec.required_ruby_version = '>= 2.4'
39
+ spec.metadata['yard.run'] = 'yri'
40
+
41
+ gemspec['dependencies'].each do |name, version|
42
+ spec.add_dependency(name, version)
43
+ end
44
+
45
+ gemspec['development_dependencies'].each do |name, version|
46
+ spec.add_development_dependency(name, version)
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'tqdm'
5
+ require 'logger'
6
+ require 'fileutils'
7
+ require 'pathname'
8
+ require 'active_support/inflector'
9
+ require 'active_support/core_ext/object/blank'
10
+ require 'active_support/core_ext/hash'
11
+ require 'ostruct'
12
+ require 'pry' unless ENV['BUNDLER_VERSION'].nil?
13
+ require 'iml/version'
14
+ require 'iml/base'
15
+ require 'iml/patterns'
16
+ require 'iml/text'
17
+ require 'iml/movie'
18
+ require 'iml/tvseries'
19
+ require 'iml/hash'
20
+
21
+ # IML Namespace
22
+ module IML
23
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base media file class
4
+ class IML::Base < OpenStruct
5
+ # @return <String> Allows retrieving and setting of the format string for the output name
6
+ attr_accessor :format_string
7
+ # @return <String> Allows for setting and getting the output path sans the output base filename
8
+ attr_accessor :prefix
9
+ # @return <Boolean> Allows for setting and getting dry run setting
10
+ attr_accessor :pretend
11
+
12
+ delegate :dirname, to: :pathname # @return <Pathname> output path sans the output base filename
13
+ delegate :basename, to: :pathname # @return <Pathname> output base filename
14
+
15
+ def initialize(hash = nil, options = {})
16
+ @prefix = options[:target]
17
+ @pretend = options[:pretend]
18
+ super(hash)
19
+ process if hash
20
+ end
21
+
22
+ # @return [String] formated output filename
23
+ def present
24
+ format_string = output_format
25
+ self.class::PLACEHOLDERS.each do |placeholder, attribute|
26
+ format_string = format_string.gsub(placeholder, send(attribute).to_s)
27
+ end
28
+ format_string
29
+ end
30
+
31
+ # @return [Pathname] full output path of the media file
32
+ def pathname
33
+ @prefix ? Pathname(@prefix) + Pathname(present) : Pathname(present)
34
+ end
35
+
36
+ # Creates the output directory if needed
37
+ # @return [Array<String>] array containing the path of the created output directory
38
+ # @example
39
+ # movie = IML::Text.new('Cool.Movie.2018.720p.BluRay.H264.AAC2.0-GROUP.mp4').detect
40
+ # # => <IML::Movie title="Cool Movie", year="2018", quality="720p", source="BluRay" ..>
41
+ # movie.create_dir
42
+ # # => ["."]
43
+ def create_dir
44
+ FileUtils.mkdir_p dirname unless @pretend
45
+ end
46
+
47
+ # Moves the media file to the output directory
48
+ # @return <Integer> 0 on success, 1 on failure
49
+ def move(path)
50
+ FileUtils.mv path, pathname unless @pretend
51
+ rescue Errno::ENOENT
52
+ 1
53
+ end
54
+
55
+ private
56
+
57
+ def output_format
58
+ format_string || self.class::DEFAULT_FORMAT
59
+ end
60
+
61
+ # Process the IML::Base object and apply some normalizing and cleanup on the fields
62
+ def process
63
+ normalize_video_codec_name
64
+ normalize_audio_codec_name
65
+ titleize
66
+ delete_fields
67
+ end
68
+
69
+ def normalize_video_codec_name
70
+ self.codec = IML::Patterns.config.codec[codec.downcase] unless IML::Patterns.config.codec.value?(codec.downcase)
71
+ end
72
+
73
+ def normalize_audio_codec_name
74
+ return false if final_audio_format?
75
+ self.channels = IML::Patterns.config.audio[audio.downcase][:channels]
76
+ self.audio = IML::Patterns.config.audio[audio.downcase][:name]
77
+ end
78
+
79
+ def final_audio_format?
80
+ return true if IML::Patterns.config.audio.values.map { |a| a[:name] }.include?(audio) || !audio
81
+ end
82
+
83
+ def delete_fields
84
+ each_pair do |k, v|
85
+ delete_field(k) unless v
86
+ end
87
+ end
88
+
89
+ def titleize
90
+ self.title = IML::Text.new(title).to_title if title.is_a?(String)
91
+ self.episode_title = IML::Text.new(episode_title).to_title if episode_title.is_a?(String)
92
+ self.episode_title = nil if episode_title.to_s.empty?
93
+ end
94
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # IML's own re-implementation of hash with key/method funky patch and active_supports HashWithIndeifferetAccess
4
+ class IML::Hash < HashWithIndifferentAccess
5
+ # Way to access Hash keys by method_name
6
+ def method_missing(method_name)
7
+ key?(method_name) ? self[method_name] : super
8
+ end
9
+
10
+ # @return <Boolean> Responds true for methods that have names of Hash keys
11
+ def respond_to_missing?(method_name, _include_private = false)
12
+ key?(method_name) || false
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Movie media file type class
4
+ class IML::Movie < IML::Base
5
+ # Formatting placeholders for Movies
6
+ PLACEHOLDERS = {
7
+ '%T' => :title,
8
+ '%Y' => :year,
9
+ '%f' => :extension,
10
+ '%v' => :codec,
11
+ '%a' => :audio,
12
+ '%g' => :group,
13
+ '%z' => :source,
14
+ '%q' => :quality
15
+ }.freeze
16
+
17
+ # Default formatting string
18
+ DEFAULT_FORMAT = '%T (%Y).%f'
19
+
20
+ # @return <Boolean> always true for IML::Movie objects
21
+ def movie?
22
+ true
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Contains methods to build the regexp to match against the input filenames
4
+ class IML::Patterns
5
+ # @return [IML::Hash] pattern config
6
+ def config
7
+ IML::Hash.new ::YAML.load_file(pattern_file)
8
+ end
9
+
10
+ # @return [IML::Hash] pattern config singleton version
11
+ def self.config
12
+ new.config
13
+ end
14
+
15
+ # Mini format arrtibute DSL
16
+ def method_missing(method_name)
17
+ m = config[method_name]
18
+ super unless m
19
+ if m.is_a?(Hash)
20
+ "(?<#{method_name}>(#{m.keys.join('|')}))"
21
+ elsif m.is_a?(Array)
22
+ "(?<#{method_name}>(#{m.join('|')}))"
23
+ elsif m.is_a?(String)
24
+ "(?<#{method_name}>#{m})"
25
+ end
26
+ end
27
+
28
+ # @return <Boolean> true when method name is a config key
29
+ def respond_to_missing?(method_name, _include_private = false)
30
+ config.key?(method_name) || false
31
+ end
32
+
33
+ # @return [Array<Regex>] Array of Regexp patterns to match filenames of Movies
34
+ def movie
35
+ [
36
+ /#{format_pattern('^%<title>s\.%<year>s\.?%<quality>s?\.%<source>s\.%<codec>s\.?%<audio>s?-?%<group>s\.%<extension>s$')}/i,
37
+ /#{format_pattern('^%<title>s_\(%<year>s\)_\[%<quality>s,%<source>s,%<audio>s,%<codec>s\]_-_%<group>s.%<extension>s$')}/i
38
+ ]
39
+ end
40
+
41
+ # @return [Array<Regex>] Array of Regexp patterns to match filenames of TV Shows
42
+ def tv
43
+ [
44
+ /#{format_pattern('^%<title>s.S%<season>sE%<episode>s.?%<episode_title>s?.?%<quality>s?.%<source>s.%<audio>s?\.?%<codec>s-%<group>s.%<extension>s$')}/i
45
+ ]
46
+ end
47
+
48
+ private
49
+
50
+ def pattern_file
51
+ Pathname(__dir__) + '..' + '..' + 'patterns.yml'
52
+ end
53
+
54
+ def media_info
55
+ {
56
+ title: title, year: year, quality: quality, source: source, codec: codec, audio: audio, group: group,
57
+ extension: extension, season: season, episode: episode, episode_title: episode_title
58
+ }
59
+ end
60
+
61
+ def format_pattern(pattern)
62
+ format(pattern, media_info)
63
+ end
64
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Parsing and mangling of text metadata
4
+ class IML::Text < String
5
+ attr_accessor :options
6
+
7
+ def initialize(string = nil, options = {})
8
+ @options = options
9
+ super(string.to_s)
10
+ end
11
+
12
+ # Convert IML::Text to desired title format
13
+ def to_title
14
+ tr('.', ' ').tr('_', ' ').titleize
15
+ end
16
+
17
+ # Determine if IML::Text matches rules for a media type
18
+ # @return [<IML::Movie>, <IML::TVSeries>] Media type object
19
+ def detect
20
+ tv? || movie? || false
21
+ end
22
+
23
+ private
24
+
25
+ def tv?
26
+ match = match_patterns(IML::Patterns.new.tv)
27
+ match ? IML::TVSeries.new(match, options) : false
28
+ end
29
+
30
+ def movie?
31
+ match = match_patterns(IML::Patterns.new.movie)
32
+ match ? IML::Movie.new(match, options) : false
33
+ end
34
+
35
+ def match_patterns(patterns)
36
+ patterns.each do |pattern|
37
+ match = match_and_return(pattern)
38
+ return match if match
39
+ end
40
+ false
41
+ end
42
+
43
+ def match_and_return(pattern)
44
+ match = self.match(pattern)
45
+ if match
46
+ match.named_captures
47
+ else
48
+ false
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TV Series media file type class
4
+ class IML::TVSeries < IML::Base
5
+ # Formatting placeholders for TV Series
6
+ PLACEHOLDERS = {
7
+ '%T' => :title,
8
+ '%E' => :episode,
9
+ '%S' => :season,
10
+ '%f' => :extension,
11
+ '%e' => :episode_i,
12
+ '%s' => :season_i,
13
+ '%t' => :episode_title,
14
+ '%a' => :audio,
15
+ '%v' => :codec,
16
+ '%q' => :quality,
17
+ '%g' => :group,
18
+ '%z' => :source
19
+ }.freeze
20
+
21
+ # Default formatting sting
22
+ DEFAULT_FORMAT = '%T/Season %s/%T - S%SE%E.%f'
23
+
24
+ # @return <Boolean> always true for IML::TVSeries
25
+ def tv?
26
+ true
27
+ end
28
+
29
+ # @return <Integer> Season number in Integer
30
+ def season_i
31
+ season.to_i
32
+ end
33
+
34
+ # @return <Integer> episode number in Integer
35
+ def episode_i
36
+ episode.to_i
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IML
4
+ # Version
5
+ VERSION = '0.1.4'
6
+ end
@@ -0,0 +1,56 @@
1
+ ---
2
+ quality:
3
+ - 720p
4
+ - 1080p
5
+ - 2160p
6
+ codec:
7
+ x264: h.264
8
+ h264: h.264
9
+ x265: h.265
10
+ h265: h.265
11
+ avc: h.264
12
+ xvid: Xvid
13
+ source:
14
+ hdtv: HDTV
15
+ bdrip: BD
16
+ dvdrip: DVD
17
+ webrip: WEB
18
+ bluray: BD
19
+ nf.web-dl: Netflix
20
+ brrip: BD
21
+ web: WEB
22
+ amzn.webrip: Amazon
23
+ audio:
24
+ aac:
25
+ name: AAC
26
+ flac:
27
+ name: FLAC
28
+ ac3:
29
+ name: AC3
30
+ dts:
31
+ name: DTS
32
+ dts-hd.ma.5.1:
33
+ name: DTS-HD Master
34
+ channels: '5.1'
35
+ truehd.7.1.atmos:
36
+ name: TrueHD Atmos
37
+ channels: '7.1'
38
+ dd5.1:
39
+ name: AC3
40
+ channels: '5.1'
41
+ ddp5.1:
42
+ name: E-AC3
43
+ channels: '5.1'
44
+ aac2.0:
45
+ name: AAC
46
+ channels: '2.0'
47
+ season: "\\d{2}"
48
+ episode: "\\d{2}"
49
+ episode_title: ".*?"
50
+ extension:
51
+ - avi
52
+ - mp4
53
+ - mkv
54
+ group: "[\\[\\]\\w]*?"
55
+ title: ".*"
56
+ year: "\\d{4}"
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Adam Ladachowski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tqdm
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: codeclimate-test-reporter
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.16'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '12.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '12.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.16'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.16'
125
+ - !ruby/object:Gem::Dependency
126
+ name: yard
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.9'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.9'
139
+ description: Library which parses strings into media objects
140
+ email:
141
+ - adam.ladachowski@gmail.com
142
+ executables:
143
+ - iml
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - ".rspec"
149
+ - ".rubocop.yml"
150
+ - ".travis.yml"
151
+ - Gemfile
152
+ - Gemfile.lock
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - bin/iml
157
+ - doc/iml-logo.png
158
+ - gemspec.yml
159
+ - iml.gemspec
160
+ - lib/iml.rb
161
+ - lib/iml/base.rb
162
+ - lib/iml/hash.rb
163
+ - lib/iml/movie.rb
164
+ - lib/iml/patterns.rb
165
+ - lib/iml/text.rb
166
+ - lib/iml/tvseries.rb
167
+ - lib/iml/version.rb
168
+ - patterns.yml
169
+ homepage: https://github.com/aladac/iml
170
+ licenses:
171
+ - MIT
172
+ metadata:
173
+ allowed_push_host: https://rubygems.org
174
+ yard.run: yri
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '2.4'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.6.14.1
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: Media string and object manipulation library
195
+ test_files: []