scene-toolkit 0.1.8 → 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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ coverage
3
+ rdoc
4
+ pkg
data/Gemfile CHANGED
@@ -1,5 +1,2 @@
1
1
  source "http://rubygems.org"
2
- gem "activesupport", "~> 3.0.0"
3
- gem "i18n", "~> 0.5.0"
4
- gem "rainbow", "~> 1.1"
5
- gem "optitron", "~> 0.2.2"
2
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,19 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scene-toolkit (0.2.0)
5
+ activesupport (~> 3.0.0)
6
+ colored
7
+ i18n
8
+ nestful
9
+ optitron
10
+
1
11
  GEM
2
12
  remote: http://rubygems.org/
3
13
  specs:
4
- activesupport (3.0.3)
5
- callsite (0.0.4)
6
- i18n (0.5.0)
7
- optitron (0.2.2)
14
+ activesupport (3.0.9)
15
+ callsite (0.0.5)
16
+ bundler (~> 1.0.0)
17
+ colored (1.2)
18
+ i18n (0.6.0)
19
+ nestful (0.0.6)
20
+ activesupport (>= 3.0.0.beta)
21
+ optitron (0.3.2)
8
22
  callsite (~> 0.0.4)
23
+ parameters_extra (~> 0.2.0)
24
+ parameters_extra (0.2.0)
9
25
  ruby2ruby (~> 1.2.4)
10
26
  ruby_parser (~> 2.0)
11
27
  sexp_processor (~> 3.0.4)
12
- rainbow (1.1)
13
28
  ruby2ruby (1.2.5)
14
29
  ruby_parser (~> 2.0)
15
30
  sexp_processor (~> 3.0)
16
- ruby_parser (2.0.5)
31
+ ruby_parser (2.0.6)
17
32
  sexp_processor (~> 3.0)
18
33
  sexp_processor (3.0.5)
19
34
 
@@ -21,7 +36,4 @@ PLATFORMS
21
36
  ruby
22
37
 
23
38
  DEPENDENCIES
24
- activesupport (~> 3.0.0)
25
- i18n (~> 0.5.0)
26
- optitron (~> 0.2.2)
27
- rainbow (~> 1.1)
39
+ scene-toolkit!
data/Rakefile CHANGED
@@ -1,18 +1,2 @@
1
- # encoding: utf-8
2
- require 'rubygems'
3
- require 'rake'
4
-
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gem|
8
- gem.name = "scene-toolkit"
9
- gem.summary = %Q{Tool to assist scene MP3 library maintenance}
10
- gem.description = %Q{Tool to assist scene MP3 library maintenance}
11
- gem.email = "knoopx@gmail.com"
12
- gem.homepage = "http://github.com/knoopx/scene-toolkit"
13
- gem.authors = ["Víctor Martínez"]
14
- end
15
- Jeweler::GemcutterTasks.new
16
- rescue LoadError
17
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
- end
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/scene-toolkit CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby -rubygems
2
2
 
3
- $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
4
-
5
- require 'scene_toolkit'
3
+ require "bundler/setup"
4
+ require 'scene_toolkit/cli'
6
5
 
7
6
  SceneToolkit::CLI.dispatch
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ module SceneToolkit
4
+ class CLI < Optitron::CLI
5
+ protected
6
+
7
+ def heading(release, color, &block)
8
+ puts release.name.send(color)
9
+ puts release.path
10
+ yield
11
+ puts
12
+ end
13
+
14
+ def warn(message)
15
+ puts " ✕ #{message}".yellow
16
+ end
17
+
18
+ def info(message)
19
+ puts " ■ #{message}".yellow
20
+ end
21
+
22
+ def error(message)
23
+ puts " ✕ #{message}".red
24
+ end
25
+
26
+ def print_errors(release)
27
+ release.errors.each do |validation, errors|
28
+ errors.each do |error|
29
+ warn "[#{validation.to_s.humanize}] #{error}"
30
+ end
31
+ end
32
+ end
33
+
34
+ def print_warnings(release)
35
+ release.warnings.each do |validation, warnings|
36
+ warnings.each do |warning|
37
+ warn "[#{validation.to_s.humanize}] #{warning}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def move_release(release, destination)
43
+ target_dir = File.join(destination, release.name)
44
+ info "Moving release to #{target_dir}"
45
+
46
+ if File.directory?(target_dir)
47
+ error "Target directory already exists. Skipping."
48
+ else
49
+ begin
50
+ FileUtils.mv(release.path, target_dir)
51
+ rescue => e
52
+ error e.message
53
+ end
54
+ end
55
+ end
56
+
57
+ def each_release(source, &block)
58
+ raise ArgumentError unless block_given?
59
+ raise ArgumentError("#{source} is not a directory") unless File.directory?(source)
60
+
61
+ releases = []
62
+
63
+ Dir.glob(File.join(source, "**", "*.mp3")).each do |file|
64
+ release_path = File.expand_path(File.dirname(file))
65
+
66
+ unless releases.include?(release_path)
67
+ release = SceneToolkit::Release.new(release_path)
68
+ releases << release_path
69
+ yield(release)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module SceneToolkit
4
+ class CLI < Optitron::CLI
5
+ desc "Generate missing or invalid release playlists"
6
+ opt "force", "Force the modification of existing files"
7
+
8
+ def playlists(directory_string)
9
+ each_release(directory_string) do |release|
10
+ if release.valid_playlist?
11
+ heading(release, :green) do
12
+ info("Skipping. Playlist seems to be OK.")
13
+ end
14
+ else
15
+ heading(release, :green) do
16
+ print_errors(release)
17
+
18
+ candidates = release.files.select { |f| %w(.nfo .m3u .sfv).include?(File.extname(f).downcase) }.group_by { |f| File.basename(f, '.*') }
19
+ if candidates.none?
20
+ error "Unable to guess playlist filename"
21
+ next
22
+ end
23
+
24
+ playlist_filename = [candidates.max { |k, v| v.size }.first, ".m3u"].join
25
+
26
+ playlist_path = File.join(release.path, playlist_filename)
27
+ if File.exist?(playlist_path) and not params[:force]
28
+ error "Playlist #{playlist_filename} already exists. Use --force to replace it."
29
+ else
30
+ info "Generating new playlist: #{playlist_filename}"
31
+ File.open(playlist_path, "w") do |playlist_file|
32
+ release.mp3_files.map { |f| File.basename(f) }.each do |mp3_file|
33
+ playlist_file.puts mp3_file
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'nestful'
4
+ require 'scene_toolkit/ext'
5
+
6
+ module SceneToolkit
7
+ class CLI < Optitron::CLI
8
+ class GoogleMatcher
9
+ def self.match(release)
10
+ regex = Regexp.new(Regexp.escape(release.name), Regexp::IGNORECASE)
11
+ response = Nestful.get("http://www.google.com/search", :params => { :q => release.name, :num => 100 })
12
+ response.scan(regex).uniq.reject(&:downcase?)
13
+ end
14
+ end
15
+
16
+ class OrlydbMatcher
17
+ def self.match(release)
18
+ regex = Regexp.new(Regexp.escape(release.name), Regexp::IGNORECASE)
19
+ response = Nestful.get("http://orlydb.com", :params => { :q => release.search_string })
20
+ response.scan(regex).uniq.reject(&:downcase?)
21
+ end
22
+ end
23
+
24
+ desc "Repair release names"
25
+
26
+ def rename(directory_string)
27
+ each_release(directory_string) do |release|
28
+ unless release.name.downcase?
29
+ heading(release, :green) { info "Skipping. Release name seems to be OK." }
30
+ next
31
+ end
32
+
33
+ match = nil
34
+ [OrlydbMatcher, GoogleMatcher].each do |matcher|
35
+ matches = matcher.match(release)
36
+ if matches.one?
37
+ match = matches.first
38
+ break
39
+ end
40
+ end
41
+
42
+ if match.present?
43
+ heading(release, :green) { info "Renamed #{release.name} => #{match}" }
44
+ release.rename!(match)
45
+ else
46
+ heading(release, :red) { error "No matches found for #{release.name}" }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ require 'scene_toolkit/release'
4
+
5
+ module SceneToolkit
6
+ class CLI < Optitron::CLI
7
+ desc "Verify library or release. Executes all validations if none specified"
8
+ SceneToolkit::Release.available_validations.each { |name, desc| opt name, desc }
9
+ opt "hide_valid", "Do not display valid releases"
10
+ opt "move_invalid_to", "Move INVALID releases to specified folder", :type => :string
11
+ opt "move_valid_to", "Move VALID releases to specified folder", :type => :string
12
+
13
+ def verify(directory_string)
14
+ validations_to_exec = []
15
+ SceneToolkit::Release.available_validations.keys.each do |name|
16
+ validations_to_exec << name if params.delete(name).eql?(true)
17
+ end
18
+
19
+ if validations_to_exec.none?
20
+ validations_to_exec = SceneToolkit::Release::available_validations.keys
21
+ end
22
+
23
+ invalid_target_directory = params.delete(:move_invalid_to)
24
+ unless invalid_target_directory.nil?
25
+ raise ArgumentError.new("#{invalid_target_directory} does not exist") unless File.directory?(invalid_target_directory)
26
+ end
27
+
28
+ valid_target_directory = params.delete(:move_valid_to)
29
+ unless valid_target_directory.nil?
30
+ raise ArgumentError.new("#{invalid_target_directory} does not exist") unless File.directory?(valid_target_directory)
31
+ end
32
+
33
+ release_count = 0
34
+ valid_releases = 0
35
+ invalid_releases = 0
36
+
37
+ each_release(directory_string) do |release|
38
+ release_count += 1
39
+ if release.valid?(validations_to_exec)
40
+ valid_releases += 1
41
+ if not params[:hide_valid] or not valid_target_directory.nil?
42
+ heading(release, :green) do
43
+ print_errors(release)
44
+ print_warnings(release)
45
+ unless valid_target_directory.nil?
46
+ move_release(release, valid_target_directory)
47
+ end
48
+ end
49
+ end
50
+ else
51
+ invalid_releases += 1
52
+ heading(release, :red) do
53
+ print_errors(release)
54
+ print_warnings(release)
55
+ unless invalid_target_directory.nil?
56
+ move_release(release, invalid_target_directory)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ puts "#{valid_releases} of #{release_count} releases valid"
63
+ puts "#{invalid_releases} of #{release_count} releases invalid"
64
+ end
65
+ end
66
+ end
@@ -1,168 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'fileutils'
4
-
5
- gem 'rainbow', "~> 1.1"
6
- gem "optitron", "~> 0.2.2"
7
-
3
+ require 'active_support/all'
8
4
  require 'optitron'
9
- require 'rainbow'
5
+ require 'fileutils'
6
+ require 'colored'
7
+ require 'scene_toolkit/release'
8
+ require 'scene_toolkit/cli/helpers'
9
+ require 'scene_toolkit/cli/playlists'
10
+ require 'scene_toolkit/cli/rename'
11
+ require 'scene_toolkit/cli/verify'
10
12
 
11
13
  module SceneToolkit
12
14
  class CLI < Optitron::CLI
13
- desc "Repair releases"
14
- opt "playlist", "Repair wrong playlist or generate missing ones"
15
- opt "force", "Force the modification of existing files"
16
-
17
- def repair(directory_string)
18
- params.underscore_and_symbolize_keys!
19
-
20
- each_release(directory_string) do |release|
21
- if params[:playlist]
22
- unless release.valid_playlist?
23
- puts release.name.foreground(:red)
24
- puts release.path
25
- print_errors(release)
26
-
27
- candidates = release.files.select { |f| %w(.nfo .m3u .sfv).include?(File.extname(f).downcase) }.group_by { |f| File.basename(f, '.*') }
28
- if candidates.none?
29
- puts " ✕ Unable to guess playlist filename".foreground(:red)
30
- next
31
- end
32
-
33
- playlist_filename = [candidates.max { |k, v| v.size }.first, ".m3u"].join
34
-
35
- playlist_path = File.join(release.path, playlist_filename)
36
- if File.exist?(playlist_path) and not params[:force]
37
- puts " ✕ Playlist #{playlist_filename} already exists. Use --force to replace it.".foreground(:red)
38
- else
39
- puts " ■ Generating new playlist: #{playlist_filename}".foreground(:yellow)
40
- File.open(playlist_path, "w") do |playlist_file|
41
- release.mp3_files.map { |f| File.basename(f) }.each do |mp3_file|
42
- playlist_file.puts mp3_file
43
- end
44
- end
45
- end
46
- puts
47
- end
48
- end
49
- end
50
- end
51
-
52
- desc "Verify library or release. Executes all validations if none specified"
53
- opt "name", "Validate release name"
54
- opt "required-files", "Validate inclusion of required files"
55
- opt "playlist", "Validate playlist against existing files"
56
- opt "checksum", "Validate release CRC-32 checksum"
57
- opt "hide-valid", "Do not display valid releases"
58
- opt "move-invalid-to", "Move INVALID releases to specified folder", :type => :string
59
- opt "move-valid-to", "Move VALID releases to specified folder", :type => :string
60
-
61
- def verify(directory_string)
62
- params.underscore_and_symbolize_keys!
63
- validations = []
64
- SceneToolkit::Release::VALIDATIONS.each do |validation|
65
- validations << validation if params.delete(validation).eql?(true)
66
- end
67
-
68
- if validations.none?
69
- validations = SceneToolkit::Release::VALIDATIONS
70
- end
71
-
72
- invalid_target_directory = params.delete(:move_invalid_to)
73
- unless invalid_target_directory.nil?
74
- raise ArgumentError.new("#{invalid_target_directory} does not exist") unless File.directory?(invalid_target_directory)
75
- end
76
-
77
- valid_target_directory = params.delete(:move_valid_to)
78
- unless valid_target_directory.nil?
79
- raise ArgumentError.new("#{invalid_target_directory} does not exist") unless File.directory?(valid_target_directory)
80
- end
81
-
82
- release_count = 0
83
- valid_releases = 0
84
- invalid_releases = 0
85
-
86
- each_release(directory_string) do |release|
87
- release_count += 1
88
- if release.valid?(validations)
89
- valid_releases += 1
90
- if not params[:hide_valid] or not valid_target_directory.nil?
91
- puts release.name.foreground(:green)
92
- puts release.path
93
- print_errors(release)
94
- print_warnings(release)
95
- unless valid_target_directory.nil?
96
- move_release(release, valid_target_directory)
97
- end
98
- puts
99
- end
100
- else
101
- invalid_releases += 1
102
- puts release.name.foreground(:red)
103
- puts release.path
104
- print_errors(release)
105
- print_warnings(release)
106
- unless invalid_target_directory.nil?
107
- move_release(release, invalid_target_directory)
108
- end
109
- puts
110
- end
111
- end
112
-
113
- puts
114
- puts "#{valid_releases} of #{release_count} releases valid".foreground(:yellow)
115
- puts "#{invalid_releases} of #{release_count} releases invalid".foreground(:yellow)
116
- end
117
-
118
- protected
119
-
120
- def print_errors(release)
121
- release.errors.each do |validation, errors|
122
- errors.each do |error|
123
- puts " ✕ [#{validation.to_s.humanize}] #{error}".foreground(:red)
124
- end
125
- end
126
- end
127
-
128
- def print_warnings(release)
129
- release.warnings.each do |validation, warnings|
130
- warnings.each do |warning|
131
- puts " ✕ [#{validation.to_s.humanize}] #{warning}".foreground(:yellow)
132
- end
133
- end
134
- end
135
-
136
- def move_release(release, destination)
137
- target_dir = File.join(destination, release.name)
138
- puts " ■ Moving release to #{target_dir}".foreground(:yellow)
139
-
140
- if File.directory?(target_dir)
141
- puts " ✕ Target directory already exists. Skipping.".foreground(:red)
142
- else
143
- begin
144
- FileUtils.mv(release.path, target_dir)
145
- rescue => e
146
- puts e.message.foreground(:red)
147
- end
148
- end
149
- end
150
-
151
- def each_release(source, &block)
152
- raise ArgumentError unless block_given?
153
- raise ArgumentError("#{source} is not a directory") unless File.directory?(source)
154
-
155
- releases = []
156
-
157
- Dir.glob(File.join(source, "**", "*.mp3")).each do |file|
158
- release_path = File.expand_path(File.dirname(file))
159
-
160
- unless releases.include?(release_path)
161
- release = SceneToolkit::Release.new(release_path)
162
- releases << release_path
163
- yield(release)
164
- end
165
- end
166
- end
167
15
  end
168
16
  end
@@ -1 +1,5 @@
1
- require 'scene_towolkit/ext/hash'
1
+ class String
2
+ def downcase?
3
+ self == self.downcase
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_files_with_extension :mp3, :sfv, :nfo, :m3u
8
+ end
9
+
10
+ module ClassMethods
11
+ protected
12
+
13
+ def has_files_with_extension(*exts)
14
+ exts.each do |ext|
15
+ define_method "#{ext}_files" do
16
+ files.select { |f| File.extname(f).downcase.eql?(".#{ext}") }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Rename
4
+ extend ActiveSupport::Concern
5
+
6
+ def rename!(new_name)
7
+ File.rename(@path, File.join(File.dirname(@path), new_name))
8
+ end
9
+
10
+ def search_string
11
+ @name.gsub(/[^A-Za-z0-9]+/, " ").gsub(/\s+/, " ")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Validations
4
+ module Checksum
5
+ def self.included(base)
6
+ base.register_validation(:checksum, "Validate release CRC-32 checksum")
7
+ end
8
+
9
+ def valid_checksum?
10
+ @errors[:checksum], @warnings[:checksum] = [], []
11
+
12
+ if sfv_files.any?
13
+ sfv_files.each do |sfv|
14
+ files_to_check = files.inject({ }) do |collection, file|
15
+ collection[File.basename(file).downcase] = File.expand_path(file)
16
+ collection
17
+ end
18
+
19
+ matched_something = false
20
+ File.read(sfv).split(/[\r\n]+/).each do |line|
21
+ line.strip!
22
+
23
+ if (/(generated|raped)/i =~ line and not /MorGoTH/i =~ line)
24
+ @warnings[:checksum] << "Possibly tampered SFV: #{line.strip}"
25
+ end
26
+
27
+ if match = /^(.+?)\s+([\dA-Fa-f]{8})$/.match(line)
28
+ matched_something = true
29
+ filename, checksum = match.captures
30
+ filename.strip!
31
+ filename.downcase!
32
+
33
+ if files_to_check.has_key?(filename)
34
+ unless Zlib.crc32(File.read(files_to_check[filename])).eql?(checksum.hex)
35
+ @errors[:checksum] << "File #{filename} is corrupted"
36
+ end
37
+ else
38
+ @errors[:checksum] << "File #{filename} not found"
39
+ end
40
+ end
41
+ end
42
+ @warnings[:checksum] << "No files to verify found" unless matched_something
43
+ end
44
+ else
45
+ @errors[:checksum] << "No *.sfv files found"
46
+ end
47
+ @errors[:checksum].empty?
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,20 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Validations
4
+ module Name
5
+ def self.included(base)
6
+ base.register_validation(:name, "Validate release name")
7
+ end
8
+
9
+ def valid_name?
10
+ @errors[:name], @warnings[:name] = [], []
11
+
12
+ @errors[:name] << "Release name is not a valid scene release name" unless @name =~ /^([A-Z0-9\-_.()&]+)\-(\d{4}|\d{3}x|\d{2}xx)\-([A-Z0-9_]+)$/i
13
+ @errors[:name] << "Release name is lowercased" if @name.eql?(@name.downcase)
14
+ @errors[:name] << "Release name is uppercased" if @name.eql?(@name.upcase)
15
+ @errors[:name].empty?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Validations
4
+ module Playlist
5
+ def self.included(base)
6
+ base.register_validation(:playlist, "Validate playlist against existing files")
7
+ end
8
+
9
+ def valid_playlist?
10
+ @errors[:playlist], @warnings[:playlist] = [], []
11
+ if m3u_files.any?
12
+ m3u_files.each do |playlist|
13
+ begin
14
+ validate_playlist(playlist)
15
+ rescue => e
16
+ @errors[:playlist] << e.message
17
+ end
18
+ end
19
+ else
20
+ @errors[:playlist] << "No *.m3u files found"
21
+ end
22
+ @errors[:playlist].empty?
23
+ end
24
+
25
+
26
+ protected
27
+
28
+ def validate_playlist(playlist)
29
+ File.read(playlist).split(/[\r\n]+/).each do |filename|
30
+ filename.strip!
31
+ next if filename.blank? or filename.start_with?("#") or filename.start_with?(";")
32
+ @errors[:playlist] << "File #{filename} not found" unless File.exist?(File.join(@path, filename))
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Validations
4
+ module RequiredFiles
5
+ REQUIRED_FILES = [:sfv, :nfo, :m3u]
6
+
7
+ def self.included(base)
8
+ base.register_validation(:required_files, "Validate inclusion of required files")
9
+ end
10
+
11
+ def valid_required_files?
12
+ @errors[:required_files], @warnings[:required_files] = [], []
13
+ REQUIRED_FILES.each do |ext|
14
+ file_count = send("#{ext}_files")
15
+ @errors[:required_files] << "No *.#{ext} files found." if file_count.none?
16
+ @warnings[:required_files] << "Multiple *.#{ext} files found." if file_count.size > 1
17
+ end
18
+ @warnings[:required_files].empty?
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module SceneToolkit
2
+ class Release
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+
6
+ def valid?(validations_to_exec = @available_validations)
7
+ @errors, @warnings = { }, { }
8
+ validations_to_exec.each do |name|
9
+ send("valid_#{name}?")
10
+ end
11
+ @errors.values.sum { |errors| errors.size }.zero?
12
+ end
13
+
14
+ included do
15
+ cattr_accessor :available_validations
16
+ @@available_validations = { }
17
+
18
+ def self.register_validation(name, description)
19
+ @@available_validations[name] = description
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,119 +1,37 @@
1
1
  require 'digest/md5'
2
2
  require 'zlib'
3
3
 
4
+ require 'scene_toolkit/release/helpers'
5
+ require 'scene_toolkit/release/rename'
6
+ require 'scene_toolkit/release/validations'
7
+ require 'scene_toolkit/release/validations/checksum'
8
+ require 'scene_toolkit/release/validations/name'
9
+ require 'scene_toolkit/release/validations/playlist'
10
+ require 'scene_toolkit/release/validations/required_files'
11
+
4
12
  module SceneToolkit
5
13
  class Release
6
- REQUIRED_FILES = [:sfv, :nfo, :m3u]
7
- VALIDATIONS = [:name, :required_files, :playlist, :checksum]
8
-
9
14
  attr_accessor :name, :path, :uid
10
15
  attr_accessor :errors, :warnings
11
16
 
17
+ include Validations
18
+ include Rename
19
+ include Helpers
20
+
21
+ include Validations::Checksum
22
+ include Validations::Name
23
+ include Validations::Playlist
24
+ include Validations::RequiredFiles
25
+
12
26
  def initialize(path)
13
- @path = path
27
+ @path = File.expand_path(path)
14
28
  @name = File.basename(path)
15
29
  @uid = Digest::MD5.hexdigest(@name.downcase.gsub(/[^A-Z0-9]/i, ' ').gsub(/\s+/, ' '))
16
- @errors, @warnings = {}, {}
17
- end
18
-
19
- def valid?(validations = VALIDATIONS)
20
- @errors, @warnings = {}, {}
21
- validations.each do |validation|
22
- send("valid_#{validation}?")
23
- end
24
- @errors.sum { |validation, errors| errors.size }.zero?
25
- end
26
-
27
- def valid_required_files?
28
- @errors[:required_files], @warnings[:required_files] = [], []
29
- REQUIRED_FILES.each do |ext|
30
- file_count = send("#{ext}_files")
31
- @errors[:required_files] << "No *.#{ext} files found." if file_count.none?
32
- @warnings[:required_files] << "Multiple *.#{ext} files found." if file_count.size > 1
33
- end
34
- @warnings[:required_files].empty?
35
- end
36
-
37
- def valid_playlist?
38
- @errors[:playlist], @warnings[:playlist] = [], []
39
- if m3u_files.any?
40
- m3u_files.each do |playlist|
41
- File.read(playlist).split(/[\r\n]+/).each do |filename|
42
- filename.strip!
43
- next if filename.blank? or filename.start_with?("#") or filename.start_with?(";")
44
- @errors[:playlist] << "File #{filename} not found" unless File.exist?(File.join(@path, filename))
45
- end
46
- end
47
- else
48
- @errors[:playlist] << "No *.m3u files found"
49
- end
50
- @errors[:playlist].empty?
51
- end
52
-
53
- def valid_checksum?
54
- @errors[:checksum], @warnings[:checksum] = [], []
55
-
56
- if sfv_files.any?
57
- sfv_files.each do |sfv|
58
- files_to_check = files.inject({}) do |collection, file|
59
- collection[File.basename(file).downcase] = File.expand_path(file)
60
- collection
61
- end
62
-
63
- matched_something = false
64
- File.read(sfv).split(/[\r\n]+/).each do |line|
65
- line.strip!
66
-
67
- if (/(generated|raped)/i =~ line and not /MorGoTH/i =~ line)
68
- @warnings[:checksum] << "Possibly tampered SFV: #{line.strip}"
69
- end
70
-
71
- if match = /^(.+?)\s+([\dA-Fa-f]{8})$/.match(line)
72
- matched_something = true
73
- filename, checksum = match.captures
74
- filename.strip!
75
- filename.downcase!
76
-
77
- if files_to_check.has_key?(filename)
78
- unless Zlib.crc32(File.read(files_to_check[filename])).eql?(checksum.hex)
79
- @errors[:checksum] << "File #{filename} is corrupted"
80
- end
81
- else
82
- @errors[:checksum] << "File #{filename} not found"
83
- end
84
- end
85
- end
86
- @warnings[:checksum] << "No files to verify found" unless matched_something
87
- end
88
- else
89
- @errors[:checksum] << "No *.sfv files found"
90
- end
91
- @errors[:checksum].empty?
92
- end
93
-
94
- def valid_name?
95
- @errors[:name], @warnings[:name] = [], []
96
- @errors[:name] << "Release name is not a valid scene release name" unless @name =~ /^([A-Z0-9\-_.()&]+)\-(\d{4}|\d{3}x|\d{2}xx)\-([A-Z0-9_]+)$/i
97
- @errors[:name] << "Release name is lowercased" if @name.eql?(@name.downcase)
98
- @errors[:name] << "Release name is uppercased" if @name.eql?(@name.upcase)
99
- @errors[:name].empty?
30
+ @errors, @warnings = { }, { }
100
31
  end
101
32
 
102
33
  def files
103
34
  Dir.glob(File.join(@path, "*"))
104
35
  end
105
-
106
- class << self
107
- protected
108
-
109
- def has_files_with_extension(*exts)
110
- exts.each do |ext|
111
- define_method "#{ext}_files" do
112
- files.select { |f| File.extname(f).downcase.eql?(".#{ext}") }
113
- end
114
- end
115
- end
116
- end
117
- has_files_with_extension :mp3, :sfv, :nfo, :m3u
118
36
  end
119
37
  end
data/lib/scene_toolkit.rb CHANGED
@@ -1,7 +1,3 @@
1
- gem "activesupport", "~> 3.0.0"
2
-
3
- require 'active_support/all'
4
-
5
- require 'scene_toolkit/ext/hash'
6
- require 'scene_toolkit/release'
7
- require 'scene_toolkit/cli'
1
+ module SceneToolkit
2
+ VERSION = "0.2.0"
3
+ end
@@ -1,63 +1,23 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ require File.expand_path("../lib/scene_toolkit", __FILE__)
5
2
 
6
3
  Gem::Specification.new do |s|
7
4
  s.name = %q{scene-toolkit}
8
- s.version = "0.1.8"
5
+ s.version = SceneToolkit::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.homepage = "http://github.com/knoopx/scene-toolkit"
8
+ s.authors = ["Víctor Martínez"]
9
+ s.email = ["knoopx@gmail.com"]
10
+ s.summary = "Tool to assist scene MP3 library maintenance"
11
+ s.description = "Tool to assist scene MP3 library maintenance"
9
12
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["V\303\255ctor Mart\303\255nez"]
12
- s.date = %q{2011-01-14}
13
- s.default_executable = %q{scene-toolkit}
14
- s.description = %q{Tool to assist scene MP3 library maintenance}
15
- s.email = %q{knoopx@gmail.com}
16
- s.executables = ["scene-toolkit"]
17
- s.extra_rdoc_files = [
18
- "LICENSE",
19
- "README.rdoc"
20
- ]
21
- s.files = [
22
- "Gemfile",
23
- "Gemfile.lock",
24
- "LICENSE",
25
- "README.rdoc",
26
- "Rakefile",
27
- "VERSION",
28
- "bin/scene-toolkit",
29
- "lib/scene_toolkit.rb",
30
- "lib/scene_toolkit/cli.rb",
31
- "lib/scene_toolkit/ext.rb",
32
- "lib/scene_toolkit/ext/hash.rb",
33
- "lib/scene_toolkit/release.rb",
34
- "scene-toolkit.gemspec"
35
- ]
36
- s.homepage = %q{http://github.com/knoopx/scene-toolkit}
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = %q{1.3.7}
39
- s.summary = %q{Tool to assist scene MP3 library maintenance}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
40
16
 
41
- if s.respond_to? :specification_version then
42
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
- s.specification_version = 3
44
-
45
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
- s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.0"])
47
- s.add_runtime_dependency(%q<i18n>, ["~> 0.5.0"])
48
- s.add_runtime_dependency(%q<rainbow>, ["~> 1.1"])
49
- s.add_runtime_dependency(%q<optitron>, ["~> 0.2.2"])
50
- else
51
- s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
52
- s.add_dependency(%q<i18n>, ["~> 0.5.0"])
53
- s.add_dependency(%q<rainbow>, ["~> 1.1"])
54
- s.add_dependency(%q<optitron>, ["~> 0.2.2"])
55
- end
56
- else
57
- s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
58
- s.add_dependency(%q<i18n>, ["~> 0.5.0"])
59
- s.add_dependency(%q<rainbow>, ["~> 1.1"])
60
- s.add_dependency(%q<optitron>, ["~> 0.2.2"])
61
- end
17
+ s.add_dependency("activesupport", ["~> 3.0.0"])
18
+ s.add_dependency("i18n")
19
+ s.add_dependency("colored")
20
+ s.add_dependency("optitron")
21
+ s.add_dependency("nestful")
62
22
  end
63
23
 
metadata CHANGED
@@ -1,96 +1,81 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: scene-toolkit
3
- version: !ruby/object:Gem::Version
4
- hash: 11
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 8
10
- version: 0.1.8
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
13
- - "V\xC3\xADctor Mart\xC3\xADnez"
7
+ authors:
8
+ - Víctor Martínez
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-01-14 00:00:00 +01:00
19
- default_executable: scene-toolkit
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- prerelease: false
23
- type: :runtime
12
+ date: 2011-07-09 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
24
16
  name: activesupport
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2157064380 !ruby/object:Gem::Requirement
26
18
  none: false
27
- requirements:
19
+ requirements:
28
20
  - - ~>
29
- - !ruby/object:Gem::Version
30
- hash: 7
31
- segments:
32
- - 3
33
- - 0
34
- - 0
21
+ - !ruby/object:Gem::Version
35
22
  version: 3.0.0
36
- requirement: *id001
37
- - !ruby/object:Gem::Dependency
38
- prerelease: false
39
23
  type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2157064380
26
+ - !ruby/object:Gem::Dependency
40
27
  name: i18n
41
- version_requirements: &id002 !ruby/object:Gem::Requirement
28
+ requirement: &2157063960 !ruby/object:Gem::Requirement
42
29
  none: false
43
- requirements:
44
- - - ~>
45
- - !ruby/object:Gem::Version
46
- hash: 11
47
- segments:
48
- - 0
49
- - 5
50
- - 0
51
- version: 0.5.0
52
- requirement: *id002
53
- - !ruby/object:Gem::Dependency
54
- prerelease: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
55
34
  type: :runtime
56
- name: rainbow
57
- version_requirements: &id003 !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ~>
61
- - !ruby/object:Gem::Version
62
- hash: 13
63
- segments:
64
- - 1
65
- - 1
66
- version: "1.1"
67
- requirement: *id003
68
- - !ruby/object:Gem::Dependency
69
35
  prerelease: false
36
+ version_requirements: *2157063960
37
+ - !ruby/object:Gem::Dependency
38
+ name: colored
39
+ requirement: &2157063480 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
70
45
  type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2157063480
48
+ - !ruby/object:Gem::Dependency
71
49
  name: optitron
72
- version_requirements: &id004 !ruby/object:Gem::Requirement
50
+ requirement: &2157063060 !ruby/object:Gem::Requirement
73
51
  none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- hash: 19
78
- segments:
79
- - 0
80
- - 2
81
- - 2
82
- version: 0.2.2
83
- requirement: *id004
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2157063060
59
+ - !ruby/object:Gem::Dependency
60
+ name: nestful
61
+ requirement: &2157062640 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: *2157062640
84
70
  description: Tool to assist scene MP3 library maintenance
85
- email: knoopx@gmail.com
86
- executables:
71
+ email:
72
+ - knoopx@gmail.com
73
+ executables:
87
74
  - scene-toolkit
88
75
  extensions: []
89
-
90
- extra_rdoc_files:
91
- - LICENSE
92
- - README.rdoc
93
- files:
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
94
79
  - Gemfile
95
80
  - Gemfile.lock
96
81
  - LICENSE
@@ -100,43 +85,43 @@ files:
100
85
  - bin/scene-toolkit
101
86
  - lib/scene_toolkit.rb
102
87
  - lib/scene_toolkit/cli.rb
88
+ - lib/scene_toolkit/cli/helpers.rb
89
+ - lib/scene_toolkit/cli/playlists.rb
90
+ - lib/scene_toolkit/cli/rename.rb
91
+ - lib/scene_toolkit/cli/verify.rb
103
92
  - lib/scene_toolkit/ext.rb
104
- - lib/scene_toolkit/ext/hash.rb
105
93
  - lib/scene_toolkit/release.rb
94
+ - lib/scene_toolkit/release/helpers.rb
95
+ - lib/scene_toolkit/release/rename.rb
96
+ - lib/scene_toolkit/release/validations.rb
97
+ - lib/scene_toolkit/release/validations/checksum.rb
98
+ - lib/scene_toolkit/release/validations/name.rb
99
+ - lib/scene_toolkit/release/validations/playlist.rb
100
+ - lib/scene_toolkit/release/validations/required_files.rb
106
101
  - scene-toolkit.gemspec
107
102
  has_rdoc: true
108
103
  homepage: http://github.com/knoopx/scene-toolkit
109
104
  licenses: []
110
-
111
105
  post_install_message:
112
106
  rdoc_options: []
113
-
114
- require_paths:
107
+ require_paths:
115
108
  - lib
116
- required_ruby_version: !ruby/object:Gem::Requirement
109
+ required_ruby_version: !ruby/object:Gem::Requirement
117
110
  none: false
118
- requirements:
119
- - - ">="
120
- - !ruby/object:Gem::Version
121
- hash: 3
122
- segments:
123
- - 0
124
- version: "0"
125
- required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
116
  none: false
127
- requirements:
128
- - - ">="
129
- - !ruby/object:Gem::Version
130
- hash: 3
131
- segments:
132
- - 0
133
- version: "0"
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
134
121
  requirements: []
135
-
136
122
  rubyforge_project:
137
- rubygems_version: 1.3.7
123
+ rubygems_version: 1.6.2
138
124
  signing_key:
139
125
  specification_version: 3
140
126
  summary: Tool to assist scene MP3 library maintenance
141
127
  test_files: []
142
-
@@ -1,15 +0,0 @@
1
- module SceneToolkit
2
- module Ext
3
- module Hash
4
- def underscore_and_symbolize_keys!
5
- result = self.dup
6
- self.each_key do |k|
7
- result[k.underscore] = self[k]
8
- end
9
- result.symbolize_keys!
10
- end
11
- end
12
- end
13
- end
14
-
15
- Hash.send(:include, SceneToolkit::Ext::Hash)