iml 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IML::Normalizer
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def call(attrs)
9
+ attrs = attrs.transform_keys(&:to_sym)
10
+ attrs = normalize_video_codec(attrs)
11
+ attrs = normalize_audio_codec(attrs)
12
+ attrs = normalize_source(attrs)
13
+ attrs = normalize_titles(attrs)
14
+ remove_blank_values(attrs)
15
+ end
16
+
17
+ private
18
+
19
+ def normalize_video_codec(attrs)
20
+ codec = attrs[:codec]
21
+ return attrs unless codec
22
+
23
+ codec_down = codec.downcase
24
+ normalized = @config.codec_map[codec_down]
25
+ attrs.merge(codec: normalized || codec)
26
+ end
27
+
28
+ def normalize_audio_codec(attrs)
29
+ audio = attrs[:audio]
30
+ return attrs unless audio
31
+
32
+ audio_down = audio.downcase
33
+ audio_entry = @config.audio_map[audio_down]
34
+ return attrs unless audio_entry.is_a?(Hash)
35
+
36
+ final_names = @config.audio_map.values
37
+ .select { |v| v.is_a?(Hash) }
38
+ .map { |v| v["name"] }
39
+
40
+ return attrs if final_names.include?(audio)
41
+
42
+ attrs.merge(
43
+ audio: audio_entry["name"],
44
+ channels: audio_entry["channels"]
45
+ )
46
+ end
47
+
48
+ def normalize_source(attrs)
49
+ source = attrs[:source]
50
+ return attrs unless source
51
+
52
+ normalized = @config.source_map[source.downcase]
53
+ attrs.merge(source: normalized || source)
54
+ end
55
+
56
+ def normalize_titles(attrs)
57
+ attrs = titleize_field(attrs, :title)
58
+ attrs = titleize_field(attrs, :episode_title)
59
+ if attrs[:episode_title].to_s.strip.empty?
60
+ attrs = attrs.merge(episode_title: nil)
61
+ end
62
+ attrs
63
+ end
64
+
65
+ def titleize_field(attrs, field)
66
+ value = attrs[field]
67
+ return attrs unless value.is_a?(String) && !value.empty?
68
+
69
+ attrs.merge(field => to_title(value))
70
+ end
71
+
72
+ def to_title(str)
73
+ str.tr(".", " ").tr("_", " ").gsub(/\b\w/, &:upcase)
74
+ end
75
+
76
+ def remove_blank_values(attrs)
77
+ attrs.reject { |_, v| v.nil? || (v.is_a?(String) && v.empty?) }
78
+ end
79
+ end
data/lib/iml/parser.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IML::Parser
4
+ def initialize(config: IML.configuration, pattern_builder: nil, normalizer: nil)
5
+ @config = config
6
+ @pattern_builder = pattern_builder || IML::PatternBuilder.new(config)
7
+ @normalizer = normalizer || IML::Normalizer.new(config)
8
+ end
9
+
10
+ def parse(filename)
11
+ try_tv(filename) || try_movie(filename)
12
+ end
13
+
14
+ private
15
+
16
+ def try_tv(filename)
17
+ captures = match_first(filename, @pattern_builder.tv_patterns)
18
+ return unless captures
19
+
20
+ attrs = @normalizer.call(captures)
21
+ IML::Media::TvSeries.new(**attrs)
22
+ end
23
+
24
+ def try_movie(filename)
25
+ captures = match_first(filename, @pattern_builder.movie_patterns)
26
+ return unless captures
27
+
28
+ attrs = @normalizer.call(captures)
29
+ IML::Media::Movie.new(**attrs)
30
+ end
31
+
32
+ def match_first(filename, patterns)
33
+ patterns.each do |pattern|
34
+ match = filename.match(pattern)
35
+ return match.named_captures if match
36
+ end
37
+ nil
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IML::PatternBuilder
4
+ FIELDS = %i[
5
+ title year tags quality source codec audio group
6
+ extension season episode episode_title extras
7
+ video_tags bit_depth
8
+ ].freeze
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ @field_patterns = {}
13
+ FIELDS.each { |f| @field_patterns[f] = config.pattern_for(f) }
14
+ end
15
+
16
+ def movie_patterns
17
+ @movie_patterns ||= [
18
+ # Standard: title.year.tags.quality.source.codec.audio-group.ext
19
+ build('^%<title>s\.%<year>s\.?%<tags>s\.?%<quality>s?\.?%<source>s\.%<codec>s\.?%<audio>s?-?%<group>s\.%<extension>s$'),
20
+ # Audio before codec: title.year.tags.quality.source.audio.codec-group.ext
21
+ build('^%<title>s\.%<year>s\.?%<tags>s\.?%<quality>s?\.?%<source>s\.%<audio>s\.?%<codec>s-?%<group>s\.%<extension>s$'),
22
+ # Bracket format: title_(year)_[quality,source,audio,codec]_-_group.ext
23
+ build('^%<title>s_\(%<year>s\)_\[%<quality>s,%<source>s,%<audio>s,%<codec>s\]_-_%<group>s.%<extension>s$'),
24
+ # P2P with bracket group: title.year.tags.quality.source.extras[group].ext
25
+ build('^%<title>s\.%<year>s\.?%<tags>s\.?%<quality>s?\.?%<source>s\.%<extras>s%<group>s\.%<extension>s$'),
26
+ # Extended: title.year.quality.video_tags.source.codec.bit_depth.audio-group.ext
27
+ build('^%<title>s\.%<year>s\.?%<tags>s\.?%<quality>s?\.?%<video_tags>s%<source>s\.%<codec>s\.?%<bit_depth>s\.?%<audio>s?-?%<group>s\.%<extension>s$')
28
+ ].freeze
29
+ end
30
+
31
+ def tv_patterns
32
+ @tv_patterns ||= [
33
+ build('^%<title>s.S%<season>sE%<episode>s.?%<episode_title>s?.?%<quality>s?.%<source>s.%<audio>s?\.?%<codec>s-%<group>s.%<extension>s$')
34
+ ].freeze
35
+ end
36
+
37
+ private
38
+
39
+ def build(template)
40
+ Regexp.new(format(template, @field_patterns), Regexp::IGNORECASE)
41
+ end
42
+ end
data/lib/iml/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module IML
4
4
  # Version
5
- VERSION = '0.1.6'
5
+ VERSION = "0.2.0"
6
6
  end
data/lib/iml.rb CHANGED
@@ -1,30 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open-uri'
4
- require 'yaml'
5
- require 'tqdm'
6
- require 'logger'
7
- require 'fileutils'
8
- require 'pathname'
9
- require 'active_support/inflector'
10
- require 'active_support/core_ext/object/blank'
11
- require 'active_support/core_ext/hash'
12
- require 'ostruct'
13
- require 'pry' unless ENV['BUNDLER_VERSION'].nil?
14
- require 'iml/version'
15
- require 'iml/base'
16
- require 'iml/patterns'
17
- require 'iml/text'
18
- require 'iml/movie'
19
- require 'iml/tvseries'
20
- require 'iml/hash'
3
+ require "yaml"
4
+ require "fileutils"
5
+ require "iml/version"
21
6
 
22
- begin
23
- require 'iml-imdb'
24
- rescue LoadError
25
- puts "IMDB support disabled, to enable add 'iml-imdb' to Gemfile or install manually"
26
- end
27
-
28
- # IML Namespace
29
7
  module IML
8
+ class Error < StandardError; end
9
+ class FileNotFoundError < Error; end
10
+
11
+ module Media; end
12
+
13
+ class << self
14
+ def configuration(path = nil)
15
+ if path
16
+ @configuration = Configuration.new(path)
17
+ else
18
+ @configuration ||= Configuration.new
19
+ end
20
+ end
21
+
22
+ def reset_configuration!
23
+ @configuration = nil
24
+ end
25
+
26
+ def parse(filename)
27
+ Parser.new.parse(filename)
28
+ end
29
+ end
30
30
  end
31
+
32
+ require "iml/configuration"
33
+ require "iml/pattern_builder"
34
+ require "iml/normalizer"
35
+ require "iml/formatter"
36
+ require "iml/file_mover"
37
+ require "iml/parser"
38
+ require "iml/media/movie"
39
+ require "iml/media/tv_series"
data/patterns.yml CHANGED
@@ -6,8 +6,10 @@ quality:
6
6
  codec:
7
7
  x264: h.264
8
8
  h264: h.264
9
+ h.264: h.264
9
10
  x265: h.265
10
11
  h265: h.265
12
+ h.265: h.265
11
13
  avc: h.264
12
14
  xvid: Xvid
13
15
  source:
@@ -15,11 +17,20 @@ source:
15
17
  bdrip: BD
16
18
  dvdrip: DVD
17
19
  webrip: WEB
20
+ web-dl: WEB
18
21
  bluray: BD
19
22
  nf.web-dl: Netflix
23
+ nf.webrip: Netflix
20
24
  brrip: BD
21
25
  web: WEB
22
26
  amzn.webrip: Amazon
27
+ amzn.web-dl: Amazon
28
+ atvp.web-dl: Apple TV+
29
+ dsnp.web-dl: Disney+
30
+ hmax.web-dl: HBO Max
31
+ pcok.web-dl: Peacock
32
+ it.web-dl: iTunes
33
+ it.webrip: iTunes
23
34
  audio:
24
35
  aac:
25
36
  name: AAC
@@ -32,18 +43,39 @@ audio:
32
43
  dts-hd.ma.5.1:
33
44
  name: DTS-HD Master
34
45
  channels: '5.1'
46
+ dts-hd.ma.7.1:
47
+ name: DTS-HD Master
48
+ channels: '7.1'
35
49
  truehd.7.1.atmos:
36
50
  name: TrueHD Atmos
37
51
  channels: '7.1'
52
+ truehd.5.1:
53
+ name: TrueHD
54
+ channels: '5.1'
38
55
  dd5.1:
39
56
  name: AC3
40
57
  channels: '5.1'
58
+ dd2.0:
59
+ name: AC3
60
+ channels: '2.0'
41
61
  ddp5.1:
42
62
  name: E-AC3
43
63
  channels: '5.1'
64
+ ddp2.0:
65
+ name: E-AC3
66
+ channels: '2.0'
67
+ ddp5.1.atmos:
68
+ name: E-AC3 Atmos
69
+ channels: '5.1'
44
70
  aac2.0:
45
71
  name: AAC
46
72
  channels: '2.0'
73
+ aac5.1:
74
+ name: AAC
75
+ channels: '5.1'
76
+ eac3:
77
+ name: E-AC3
78
+ tags: "(?:REPACK|PROPER|RERIP|INTERNAL|REAL|iNTERNAL)?"
47
79
  season: "\\d{2}"
48
80
  episode: "\\d{2}"
49
81
  episode_title: ".*?"
@@ -51,6 +83,9 @@ extension:
51
83
  - avi
52
84
  - mp4
53
85
  - mkv
54
- group: "[\\[\\]\\w]*?"
86
+ extras: "(?:[\\w+]+\\.)*[\\w+]*"
87
+ video_tags: "(?:(?:4K|UHD|HDR10\\+?|HDR|DV|SDR|HYBRID|MULTi)\\.?)*"
88
+ bit_depth: "(?:(?:10bit|8bit|12bit)\\.?)?"
89
+ group: "[\\[\\]\\w ]*?"
55
90
  title: ".*"
56
91
  year: "\\d{4}"
metadata CHANGED
@@ -1,127 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Ladachowski
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-07 00:00:00.000000000 Z
11
+ date: 2026-02-04 00:00:00.000000000 Z
12
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
13
  - !ruby/object:Gem::Dependency
56
14
  name: bundler
57
15
  requirement: !ruby/object:Gem::Requirement
58
16
  requirements:
59
- - - "~>"
17
+ - - ">="
60
18
  - !ruby/object:Gem::Version
61
- version: '1.16'
19
+ version: '2.4'
62
20
  type: :development
63
21
  prerelease: false
64
22
  version_requirements: !ruby/object:Gem::Requirement
65
23
  requirements:
66
- - - "~>"
24
+ - - ">="
67
25
  - !ruby/object:Gem::Version
68
- version: '1.16'
26
+ version: '2.4'
69
27
  - !ruby/object:Gem::Dependency
70
28
  name: rake
71
29
  requirement: !ruby/object:Gem::Requirement
72
30
  requirements:
73
31
  - - "~>"
74
32
  - !ruby/object:Gem::Version
75
- version: '12.3'
33
+ version: '13.0'
76
34
  type: :development
77
35
  prerelease: false
78
36
  version_requirements: !ruby/object:Gem::Requirement
79
37
  requirements:
80
38
  - - "~>"
81
39
  - !ruby/object:Gem::Version
82
- version: '12.3'
40
+ version: '13.0'
83
41
  - !ruby/object:Gem::Dependency
84
42
  name: rspec
85
43
  requirement: !ruby/object:Gem::Requirement
86
44
  requirements:
87
45
  - - "~>"
88
46
  - !ruby/object:Gem::Version
89
- version: '3.7'
47
+ version: '3.13'
90
48
  type: :development
91
49
  prerelease: false
92
50
  version_requirements: !ruby/object:Gem::Requirement
93
51
  requirements:
94
52
  - - "~>"
95
53
  - !ruby/object:Gem::Version
96
- version: '3.7'
54
+ version: '3.13'
97
55
  - !ruby/object:Gem::Dependency
98
- name: pry
56
+ name: simplecov
99
57
  requirement: !ruby/object:Gem::Requirement
100
58
  requirements:
101
59
  - - "~>"
102
60
  - !ruby/object:Gem::Version
103
- version: '0.11'
61
+ version: '0.22'
104
62
  type: :development
105
63
  prerelease: false
106
64
  version_requirements: !ruby/object:Gem::Requirement
107
65
  requirements:
108
66
  - - "~>"
109
67
  - !ruby/object:Gem::Version
110
- version: '0.11'
68
+ version: '0.22'
111
69
  - !ruby/object:Gem::Dependency
112
- name: simplecov
70
+ name: standard
113
71
  requirement: !ruby/object:Gem::Requirement
114
72
  requirements:
115
73
  - - "~>"
116
74
  - !ruby/object:Gem::Version
117
- version: '0.16'
75
+ version: '1.40'
118
76
  type: :development
119
77
  prerelease: false
120
78
  version_requirements: !ruby/object:Gem::Requirement
121
79
  requirements:
122
80
  - - "~>"
123
81
  - !ruby/object:Gem::Version
124
- version: '0.16'
82
+ version: '1.40'
125
83
  - !ruby/object:Gem::Dependency
126
84
  name: yard
127
85
  requirement: !ruby/object:Gem::Requirement
@@ -144,26 +102,31 @@ executables:
144
102
  extensions: []
145
103
  extra_rdoc_files: []
146
104
  files:
105
+ - ".claude/settings.local.json"
106
+ - ".github/workflows/ci.yml"
107
+ - ".github/workflows/publish.yml"
147
108
  - ".gitignore"
148
109
  - ".rspec"
149
- - ".rubocop.yml"
150
- - ".travis.yml"
151
110
  - Gemfile
152
111
  - Gemfile.lock
153
112
  - LICENSE.txt
113
+ - PLAN.md
154
114
  - README.md
155
115
  - Rakefile
116
+ - TODO.md
156
117
  - bin/iml
157
118
  - doc/iml-logo.png
158
119
  - gemspec.yml
159
120
  - iml.gemspec
160
121
  - 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
122
+ - lib/iml/configuration.rb
123
+ - lib/iml/file_mover.rb
124
+ - lib/iml/formatter.rb
125
+ - lib/iml/media/movie.rb
126
+ - lib/iml/media/tv_series.rb
127
+ - lib/iml/normalizer.rb
128
+ - lib/iml/parser.rb
129
+ - lib/iml/pattern_builder.rb
167
130
  - lib/iml/version.rb
168
131
  - patterns.yml
169
132
  homepage: https://github.com/aladac/iml
@@ -172,7 +135,7 @@ licenses:
172
135
  metadata:
173
136
  allowed_push_host: https://rubygems.org
174
137
  yard.run: yri
175
- post_install_message:
138
+ post_install_message:
176
139
  rdoc_options: []
177
140
  require_paths:
178
141
  - lib
@@ -180,16 +143,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
180
143
  requirements:
181
144
  - - ">="
182
145
  - !ruby/object:Gem::Version
183
- version: '2.4'
146
+ version: '3.1'
184
147
  required_rubygems_version: !ruby/object:Gem::Requirement
185
148
  requirements:
186
149
  - - ">="
187
150
  - !ruby/object:Gem::Version
188
151
  version: '0'
189
152
  requirements: []
190
- rubyforge_project:
191
- rubygems_version: 2.6.14.1
192
- signing_key:
153
+ rubygems_version: 3.5.22
154
+ signing_key:
193
155
  specification_version: 4
194
156
  summary: Media string and object manipulation library
195
157
  test_files: []
data/.rubocop.yml DELETED
@@ -1,15 +0,0 @@
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
- AllCops:
14
- Exclude:
15
- - 'spec/**/*'
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
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/lib/iml/base.rb DELETED
@@ -1,94 +0,0 @@
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 if codec
64
- normalize_audio_codec_name if audio
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
data/lib/iml/hash.rb DELETED
@@ -1,14 +0,0 @@
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
data/lib/iml/movie.rb DELETED
@@ -1,24 +0,0 @@
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