scene-toolkit 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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)