what_cd 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -3
- data/bin/what_cd +59 -32
- data/lib/what_cd.rb +6 -10
- data/lib/what_cd/better.rb +73 -0
- data/lib/what_cd/better_plugins/better_plugin.rb +10 -0
- data/lib/what_cd/better_plugins/convert.rb +60 -0
- data/lib/what_cd/better_plugins/new_dir.rb +46 -0
- data/lib/what_cd/metadata.rb +135 -0
- data/lib/what_cd/sanitize.rb +57 -0
- data/lib/what_cd/sanitize_plugins/bitrate.rb +66 -0
- data/lib/what_cd/sanitize_plugins/cover.rb +36 -0
- data/lib/what_cd/sanitize_plugins/dir_name.rb +76 -0
- data/lib/what_cd/sanitize_plugins/file_name.rb +70 -0
- data/lib/what_cd/sanitize_plugins/hidden.rb +35 -0
- data/lib/what_cd/sanitize_plugins/sanitize_plugin.rb +6 -0
- data/lib/what_cd/sanitize_plugins/tags.rb +40 -0
- data/lib/what_cd/version.rb +2 -2
- metadata +39 -13
- data/lib/better.rb +0 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0780f43cbdc8861be4195ee4812dcc80b7b9d8b
|
4
|
+
data.tar.gz: 618d617b22d2fef1d5f465ff1c75d1d068f45ade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99d5643b3da74e5db694b6b3cf01289429782db57006b4556cb08d01d8ebeb0c331ae5aef9e65f4d94eb9732912900f61c9951c1046bc15dc0227c5fbf6ed6e1
|
7
|
+
data.tar.gz: a75af750dbe7330b7c588541b869d1e8cc8d14718636889836b14d28adf48e24503b2c5daa0324b0db6b22c3be4b2c01df18f2cad28d2de881702c63724db0e7
|
data/README.md
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# what_cd
|
2
|
+
What.CD uploader toolkit that makes your uploads squeaky clean.
|
3
3
|
|
4
|
-
|
4
|
+
## Installation
|
5
|
+
```bash
|
6
|
+
gem install what_cd
|
7
|
+
```
|
8
|
+
Create a default config file
|
9
|
+
```bash
|
10
|
+
cp what_cd/etc/.what_cd ~/.what_cd
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
```bash
|
15
|
+
what_cd sanitize [dir]
|
16
|
+
```
|
17
|
+
Sanitizing a release will iterate over a number of plugins under lib/what_cd/sanitize_plugins. Each plugin may modify the files themselves, the file names, or the directory name to adhere to What.CD guidelines. They may also throw errors if there is an issue with the upload ie. a mutt rip.
|
18
|
+
|
19
|
+
```bash
|
20
|
+
what_cd metadata [dir]
|
21
|
+
```
|
22
|
+
The metadata command will generate metadata about the release information that makes it easy to fill out the upload form.
|
23
|
+
|
24
|
+
## License
|
25
|
+
|
26
|
+
Please see LICENSE at the top level of the repository.
|
data/bin/what_cd
CHANGED
@@ -1,40 +1,67 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'commander/import'
|
5
|
+
require 'logging'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
require '../lib/what_cd'
|
9
|
+
require '../lib/what_cd/version'
|
10
|
+
require '../lib/what_cd/sanitize'
|
11
|
+
require '../lib/what_cd/better'
|
12
|
+
require '../lib/what_cd/metadata'
|
13
|
+
|
14
|
+
# :name is optional, otherwise uses the basename of this executable
|
15
|
+
program :name, 'WHAT.CD'
|
16
|
+
program :version, WhatCD::VERSION
|
17
|
+
program :description, 'WHAT.CD tools'
|
18
|
+
|
19
|
+
global_option('--verbose') { $verbose = true }
|
20
|
+
|
21
|
+
command :sanitize do |c|
|
22
|
+
c.syntax = 'what_cd sanitize [dir]'
|
23
|
+
c.description = 'Sanitizes releases'
|
24
|
+
c.action do |args, options|
|
25
|
+
# Preconditions
|
26
|
+
raise ArgumentError.new("dir argument required") unless args.count > 0
|
27
|
+
path = sanitize_path(args[0])
|
28
|
+
Sanitize.run(path)
|
29
|
+
end
|
7
30
|
end
|
8
31
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Usage: #{File.basename($0)} [directory]
|
18
|
-
|
19
|
-
Options are:
|
20
|
-
BANNER
|
21
|
-
opts.separator ''
|
22
|
-
opts.on('-v', '--version',
|
23
|
-
"Show the #{File.basename($0)} version number and exit") { require 'what-cd/version'; puts "what-cd #{WhatCD::VERSION}"; exit }
|
24
|
-
opts.on('-t', '--tracker',
|
25
|
-
"Create torrent file with tracker") { |tracker| OPTIONS[:tracker] = tracker }
|
26
|
-
opts.on('-e', '--encoding', String,
|
27
|
-
"Set lame encoding options", "Default: #{WhatCD.default_encoding}") { |encoding| OPTIONS[:encoding] = encoding }
|
28
|
-
opts.on('-h', '--help',
|
29
|
-
'Show this help message.') { puts opts; exit }
|
30
|
-
opts.parse!(ARGV)
|
31
|
-
|
32
|
-
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
33
|
-
puts opts; exit
|
32
|
+
command :metadata do |c|
|
33
|
+
c.syntax = 'what_cd metadata [dir]'
|
34
|
+
c.description = 'Gets release metadata for uploading'
|
35
|
+
c.action do |args, options|
|
36
|
+
# Preconditions
|
37
|
+
raise ArgumentError.new("dir argument required") unless args.count > 0
|
38
|
+
path = sanitize_path(args[0])
|
39
|
+
Metadata.run(path)
|
34
40
|
end
|
41
|
+
end
|
35
42
|
|
36
|
-
|
37
|
-
|
43
|
+
command :better do |c|
|
44
|
+
c.syntax = 'what_cd better [args] [dir]'
|
45
|
+
c.description = 'Converts FLAC releases to MP3'
|
46
|
+
c.option '--quality [320/V0]', String, 'Specifies what quality MP3 to convert to'
|
47
|
+
c.action do |args, options|
|
48
|
+
# Preconditions
|
49
|
+
raise ArgumentError.new("dir argument required") unless args.count > 0
|
50
|
+
raise ArgumentError.new("quality option required") unless options.quality
|
51
|
+
raise ArgumentError.new("specified quality invalid. Must be 320 or V0") unless options.quality == "320" || options.quality == "V0"
|
52
|
+
path = sanitize_path(args[0])
|
53
|
+
Better.run(path, options.quality, $verbose)
|
54
|
+
end
|
55
|
+
end
|
38
56
|
|
39
|
-
|
57
|
+
def sanitize_path(path)
|
58
|
+
# Force add a trailing '/'
|
59
|
+
#path = path.chomp('/')
|
60
|
+
#path << '/'
|
61
|
+
|
62
|
+
if (Pathname.new(path).absolute?)
|
63
|
+
return path
|
64
|
+
else
|
65
|
+
return Dir.pwd + '/' + path
|
66
|
+
end
|
40
67
|
end
|
data/lib/what_cd.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
|
-
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
|
-
|
3
|
+
require 'logging'
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
module WhatCD
|
6
|
+
|
7
|
+
# YAML config
|
8
|
+
CONFIG = "#{Dir.home}/.what_cd"
|
7
9
|
|
8
|
-
class << self
|
9
|
-
# V0
|
10
|
-
def default_encoding
|
11
|
-
'--preset extreme'
|
12
|
-
end
|
13
|
-
end
|
14
10
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
'''
|
6
|
+
This tool is primarily for working through better.php with ease.
|
7
|
+
Essentially this tool will look at an existing release directory with
|
8
|
+
FLAC files and create a matching release with MP3 files. A best attempt
|
9
|
+
is made to create an appropriate folder name. Any non-flac files are moved
|
10
|
+
over and then flac files are converted to an MP3.
|
11
|
+
|
12
|
+
'''
|
13
|
+
|
14
|
+
module Better
|
15
|
+
|
16
|
+
@log = Logging.logger[self]
|
17
|
+
@log.appenders = Logging.appenders.stdout
|
18
|
+
|
19
|
+
def self. setup_log(verbose)
|
20
|
+
if verbose
|
21
|
+
@log.level = :debug
|
22
|
+
else
|
23
|
+
@log.level = :info
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Load from config file
|
28
|
+
def self.run(path, quality, verbose=false)
|
29
|
+
setup_log(verbose)
|
30
|
+
|
31
|
+
# Load configured plugins
|
32
|
+
begin
|
33
|
+
# Get config
|
34
|
+
config = YAML.load_file(WhatCD::CONFIG)
|
35
|
+
# Get configured plugins
|
36
|
+
configured_plugins = config['commands']['better']['plugins']
|
37
|
+
|
38
|
+
if configured_plugins
|
39
|
+
# Run them
|
40
|
+
self.run_plugins(path, configured_plugins, quality)
|
41
|
+
else
|
42
|
+
@log.info "No plugins to run. Edit your config to enable plugins."
|
43
|
+
end
|
44
|
+
rescue Errno::ENOENT
|
45
|
+
@log.error "Missing gem config file '~/.what_cd'"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.run_plugins(path, configured_plugins, quality)
|
50
|
+
# Get this directory
|
51
|
+
current_dir = File.dirname(__FILE__)
|
52
|
+
|
53
|
+
# Set the context to be passed around between plugins
|
54
|
+
context = {}
|
55
|
+
context[:path] = path
|
56
|
+
context[:quality] = quality
|
57
|
+
|
58
|
+
# Iterate over the configured plugins and dynamically execute them
|
59
|
+
configured_plugins.each do |configured_plugin|
|
60
|
+
file = "#{current_dir}/better_plugins/#{configured_plugin}.rb"
|
61
|
+
require file
|
62
|
+
file_name = File.basename(file, '.rb')
|
63
|
+
# using ActiveSupport for camelcase and constantize
|
64
|
+
plugin = file_name.camelcase.constantize
|
65
|
+
# Check to ensure ruby file defines a class
|
66
|
+
if plugin.class == Class
|
67
|
+
@log.info "Bettering with plugin #{plugin}"
|
68
|
+
context = plugin.new.better(context)
|
69
|
+
@log.debug "context returned as #{context}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module BetterPlugin
|
2
|
+
|
3
|
+
def better(context)
|
4
|
+
raise 'This method should be implemented by every better plugin and return the context, modified or not'
|
5
|
+
end
|
6
|
+
|
7
|
+
def description
|
8
|
+
raise 'This method should be implemented by every better plugin and describe the plugin'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
require File.expand_path("../better_plugin", __FILE__)
|
4
|
+
|
5
|
+
class Convert
|
6
|
+
|
7
|
+
include BetterPlugin
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@log = Logging.logger[self]
|
11
|
+
@log.appenders = Logging.appenders.stdout
|
12
|
+
if $verbose
|
13
|
+
@log.level = :debug
|
14
|
+
else
|
15
|
+
@log.level = :info
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def better(context)
|
20
|
+
path = context[:path]
|
21
|
+
quality = context[:quality]
|
22
|
+
|
23
|
+
if quality == "320"
|
24
|
+
convert(path, "insane")
|
25
|
+
elsif quality == "V0"
|
26
|
+
convert(path, "extreme")
|
27
|
+
else
|
28
|
+
# rage
|
29
|
+
end
|
30
|
+
|
31
|
+
# Remove old flac files
|
32
|
+
cleanup(path)
|
33
|
+
|
34
|
+
return context
|
35
|
+
end
|
36
|
+
|
37
|
+
def convert(path, preset)
|
38
|
+
|
39
|
+
files = Dir.chdir(path) { Dir["*.flac"] }
|
40
|
+
|
41
|
+
files.each do |flac_file|
|
42
|
+
@log.info "Converting #{flac_file}"
|
43
|
+
cmd = "cd #{Shellwords.escape(path)}; flac2mp3 #{Shellwords.escape(flac_file)} --encoding='--preset #{preset}' > /dev/null 2>&1"
|
44
|
+
output = system "bash -c \"#{cmd}\""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def cleanup(path)
|
49
|
+
files = Dir.chdir(path) { Dir["*.flac"] }
|
50
|
+
|
51
|
+
files.each do |flac_file|
|
52
|
+
@log.info "Deleting #{flac_file}"
|
53
|
+
FileUtils.rm_rf(path + flac_file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def description
|
58
|
+
"Creates a new directory for the MP3 files that will be converted from FLAC"
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
require File.expand_path("../better_plugin", __FILE__)
|
4
|
+
|
5
|
+
class NewDir
|
6
|
+
|
7
|
+
include BetterPlugin
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@log = Logging.logger[self]
|
11
|
+
@log.appenders = Logging.appenders.stdout
|
12
|
+
if $verbose
|
13
|
+
@log.level = :debug
|
14
|
+
else
|
15
|
+
@log.level = :info
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def better(context)
|
20
|
+
path = context[:path]
|
21
|
+
quality = context[:quality]
|
22
|
+
new_path = determine_new_dir_name(path, quality)
|
23
|
+
|
24
|
+
@log.info "Creating path for MP3 files at #{new_path} and copying files over"
|
25
|
+
# Create the new directory if it does not exist
|
26
|
+
FileUtils.cp_r path, new_path unless File.exists?(new_path)
|
27
|
+
|
28
|
+
context[:path] = new_path
|
29
|
+
return context
|
30
|
+
end
|
31
|
+
|
32
|
+
# Intelligently decide on a new directory name
|
33
|
+
def determine_new_dir_name(path, quality)
|
34
|
+
if path.include? 'FLAC'
|
35
|
+
return path.gsub 'FLAC', "MP3 #{quality}"
|
36
|
+
elsif path.include? 'flac'
|
37
|
+
return path.gsub! 'flac', "MP3 #{quality}"
|
38
|
+
else
|
39
|
+
return path.gsub! '/', " [MP3 #{quality}]"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def description
|
44
|
+
"Creates a new directory for the MP3 files that will be converted from FLAC"
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'yaml'
|
4
|
+
require 'mp3info'
|
5
|
+
|
6
|
+
module Metadata
|
7
|
+
|
8
|
+
@log = Logging.logger[self]
|
9
|
+
@log.appenders = Logging.appenders.stdout
|
10
|
+
if $verbose
|
11
|
+
@log.level = :debug
|
12
|
+
else
|
13
|
+
@log.level = :info
|
14
|
+
end
|
15
|
+
|
16
|
+
# Load from config file
|
17
|
+
def self.run(path)
|
18
|
+
|
19
|
+
metadata = {}
|
20
|
+
metadata['album_title'] = self.get_album_title(path)
|
21
|
+
metadata['artists'] = self.get_artists(path)
|
22
|
+
metadata['year'] = self.get_year(path)
|
23
|
+
# We only handle MP3 files at the moment
|
24
|
+
metadata['format'] = 'MP3'
|
25
|
+
metadata ['bitrate'] = self.get_bitrate(path)
|
26
|
+
|
27
|
+
|
28
|
+
puts metadata
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_artists(path)
|
32
|
+
artists = []
|
33
|
+
|
34
|
+
Dir.entries(path).each do |f|
|
35
|
+
if !File.directory? f
|
36
|
+
# Fix Tags
|
37
|
+
if File.extname(f) == ".mp3"
|
38
|
+
filename = File.basename(f, File.extname(f))
|
39
|
+
file_path = path + f
|
40
|
+
|
41
|
+
Mp3Info.open(file_path) do |mp3|
|
42
|
+
if mp3.tag.artist
|
43
|
+
artists.push(mp3.tag.artist)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
return artists
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.get_year(path)
|
54
|
+
file_path = self.get_first_mp3(path)
|
55
|
+
|
56
|
+
if file_path
|
57
|
+
Mp3Info.open(file_path) do |mp3|
|
58
|
+
return mp3.tag.year
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.get_album_title(path)
|
64
|
+
file_path = self.get_first_mp3(path)
|
65
|
+
|
66
|
+
if file_path
|
67
|
+
Mp3Info.open(file_path) do |mp3|
|
68
|
+
return mp3.tag.album
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.get_first_mp3(path)
|
74
|
+
Dir.entries(path).each do |f|
|
75
|
+
if !File.directory? f
|
76
|
+
# Fix Tags
|
77
|
+
if File.extname(f) == ".mp3"
|
78
|
+
filename = File.basename(f, File.extname(f))
|
79
|
+
file_path = path + f
|
80
|
+
return file_path
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.get_bitrate(path)
|
87
|
+
files = Dir.chdir(path) { Dir["*.mp3"] }
|
88
|
+
return if files.empty?
|
89
|
+
|
90
|
+
bitrates = {}
|
91
|
+
|
92
|
+
files.each do |file_name|
|
93
|
+
file_path = path + file_name
|
94
|
+
Mp3Info.open(file_path) do |mp3|
|
95
|
+
hash = {:bitrate => mp3.bitrate, :vbr => mp3.vbr}
|
96
|
+
bitrates[file_name] = hash
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
vbr_list = bitrates.values.map{|value| value[:vbr] }
|
101
|
+
# Will only have 1-2 count (true & false)
|
102
|
+
if vbr_list.uniq.count > 1
|
103
|
+
raise BitrateError.new("Release has both constant and variable bitrate tracks!")
|
104
|
+
else
|
105
|
+
vbr = vbr_list.first
|
106
|
+
end
|
107
|
+
|
108
|
+
bitrate_list = bitrates.values.map{|value| value[:bitrate] }
|
109
|
+
# Remove duplicates
|
110
|
+
uniq_bitrate_list = bitrate_list.uniq
|
111
|
+
|
112
|
+
# If the only vbr value is false, this is CBR
|
113
|
+
if not vbr and uniq_bitrate_list.uniq.count > 1
|
114
|
+
raise BitrateError.new("Release tracks are constant bit rate but multiple bitrates found!")
|
115
|
+
elsif vbr
|
116
|
+
puts "Calculating average bitrate"
|
117
|
+
# Determine average VBR of release
|
118
|
+
avg_bitrate = bitrate_list.inject(:+) / bitrate_list.length
|
119
|
+
if avg_bitrate > 220
|
120
|
+
return 'V0 (VBR)'
|
121
|
+
elsif avg_bitrate > 192
|
122
|
+
return 'V2 (VBR)'
|
123
|
+
else
|
124
|
+
raise BitrateError.new("Release average bit rate is too low")
|
125
|
+
end
|
126
|
+
else
|
127
|
+
return bitrate_list.first
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class BitrateError < StandardError
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Sanitize
|
6
|
+
|
7
|
+
@log = Logging.logger[self]
|
8
|
+
@log.appenders = Logging.appenders.stdout
|
9
|
+
if $verbose
|
10
|
+
@log.level = :debug
|
11
|
+
else
|
12
|
+
@log.level = :info
|
13
|
+
end
|
14
|
+
|
15
|
+
# Load from config file
|
16
|
+
def self.run(path)
|
17
|
+
|
18
|
+
# Load configured plugins
|
19
|
+
begin
|
20
|
+
# Get config
|
21
|
+
config = YAML.load_file(WhatCD::CONFIG)
|
22
|
+
# Get configured plugins
|
23
|
+
configured_plugins = config['commands']['sanitize']['plugins']
|
24
|
+
# Run them
|
25
|
+
self.run_plugins(path, configured_plugins)
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
@log.error "Missing gem config file '~/.what_cd'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.run_plugins(path, configured_plugins)
|
32
|
+
@log.info "Sanitizing release located at #{path}"
|
33
|
+
# Get this directory
|
34
|
+
current_dir = File.dirname(__FILE__)
|
35
|
+
|
36
|
+
# Set the context to be passed around between plugins
|
37
|
+
context = {}
|
38
|
+
context[:path] = path
|
39
|
+
|
40
|
+
# Iterate over the configured plugins and dynamically execute them
|
41
|
+
configured_plugins.each do |configured_plugin|
|
42
|
+
file = "#{current_dir}/sanitize_plugins/#{configured_plugin}.rb"
|
43
|
+
require file
|
44
|
+
file_name = File.basename(file, '.rb')
|
45
|
+
# using ActiveSupport for camelcase and constantize
|
46
|
+
plugin = file_name.camelcase.constantize
|
47
|
+
# Check to ensure ruby file defines a class
|
48
|
+
if plugin.class == Class
|
49
|
+
@log.info "Sanitizing with plugin #{plugin}"
|
50
|
+
context = plugin.new.sanitize(context)
|
51
|
+
@log.debug "Context returned as #{context}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@log.info "Sanitization finished. Sanitized release located at #{context[:path]}"
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'mp3info'
|
3
|
+
|
4
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
5
|
+
|
6
|
+
class Bitrate
|
7
|
+
|
8
|
+
include SanitizePlugin
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@log = Logging.logger[self]
|
12
|
+
@log.appenders = Logging.appenders.stdout
|
13
|
+
if $verbose
|
14
|
+
@log.level = :debug
|
15
|
+
else
|
16
|
+
@log.level = :info
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def sanitize(context)
|
21
|
+
path = context[:path]
|
22
|
+
files = Dir.chdir(path) { Dir["*.mp3"] }
|
23
|
+
return if files.empty?
|
24
|
+
|
25
|
+
bitrates = {}
|
26
|
+
|
27
|
+
files.each do |file_name|
|
28
|
+
file_path = path + file_name
|
29
|
+
Mp3Info.open(file_path) do |mp3|
|
30
|
+
hash = {:bitrate => mp3.bitrate, :vbr => mp3.vbr}
|
31
|
+
bitrates[file_name] = hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Improve this to print out the violating file name
|
36
|
+
vbr_list = bitrates.values.map{|value| value[:vbr] }
|
37
|
+
if vbr_list.uniq.count > 1
|
38
|
+
raise BitrateError.new("Release has both constant and variable bitrate tracks!")
|
39
|
+
else
|
40
|
+
vbr = vbr_list.first
|
41
|
+
end
|
42
|
+
|
43
|
+
bitrate_list = bitrates.values.map{|value| value[:bitrate] }
|
44
|
+
# Remove duplicates
|
45
|
+
bitrate_list = bitrate_list.uniq
|
46
|
+
|
47
|
+
# If the only vbr value is false, this is CBR
|
48
|
+
if not vbr and bitrate_list.uniq.count > 1
|
49
|
+
raise BitrateError.new("Release tracks are constant bit rate but multiple bitrates found!")
|
50
|
+
else
|
51
|
+
bitrate = bitrate_list.first
|
52
|
+
end
|
53
|
+
|
54
|
+
if vbr
|
55
|
+
@log.info "Variable bitrate detected"
|
56
|
+
else
|
57
|
+
@log.info "Constant bitrate detected at #{bitrate} kbps"
|
58
|
+
end
|
59
|
+
|
60
|
+
return context
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class BitrateError < StandardError
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
4
|
+
|
5
|
+
class Cover
|
6
|
+
|
7
|
+
include SanitizePlugin
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@log = Logging.logger[self]
|
11
|
+
@log.appenders = Logging.appenders.stdout
|
12
|
+
if $verbose
|
13
|
+
@log.level = :debug
|
14
|
+
else
|
15
|
+
@log.level = :info
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sanitize(context)
|
20
|
+
path = context[:path]
|
21
|
+
images = Dir.chdir(path) { Dir["*.jpg"] }
|
22
|
+
|
23
|
+
if images.count == 1
|
24
|
+
file_name = images[0]
|
25
|
+
file_path = path + file_name
|
26
|
+
new_file_path = path + '00. cover.jpg'
|
27
|
+
File.rename(file_path, new_file_path)
|
28
|
+
elsif images.count > 1
|
29
|
+
@log.info "Several .jpg files in release directory. Unable to determine cover art."
|
30
|
+
else
|
31
|
+
@log.debug "No .jpg files found in release directory."
|
32
|
+
end
|
33
|
+
|
34
|
+
return context
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'mp3info'
|
2
|
+
require 'logging'
|
3
|
+
|
4
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
5
|
+
|
6
|
+
class DirName
|
7
|
+
include SanitizePlugin
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@log = Logging.logger[self]
|
11
|
+
@log.appenders = Logging.appenders.stdout
|
12
|
+
if $verbose
|
13
|
+
@log.level = :debug
|
14
|
+
else
|
15
|
+
@log.level = :info
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sanitize(context)
|
20
|
+
path = context[:path]
|
21
|
+
# To get release info we need to look at an mp3
|
22
|
+
file_path = get_first_mp3_path(path)
|
23
|
+
|
24
|
+
if file_path
|
25
|
+
@log.debug "Opening file_path #{file_path}"
|
26
|
+
Mp3Info.open(file_path) do |mp3|
|
27
|
+
if mp3.tag.album
|
28
|
+
new_dir = mp3.tag.album
|
29
|
+
|
30
|
+
# Add year if available
|
31
|
+
if mp3.tag.year
|
32
|
+
new_dir = new_dir + " [#{mp3.tag.year}]"
|
33
|
+
end
|
34
|
+
|
35
|
+
quality = get_quality_string(mp3.bitrate, mp3.vbr)
|
36
|
+
new_dir = new_dir + " #{quality}"
|
37
|
+
|
38
|
+
parts = path.split("/")
|
39
|
+
parts[-1] = new_dir
|
40
|
+
new_path = parts.join("/")
|
41
|
+
# Add trailing '/' that was removed from splitting
|
42
|
+
new_path << '/'
|
43
|
+
if new_dir != path.chomp("/")
|
44
|
+
@log.debug "Renaming directory to #{new_dir}"
|
45
|
+
File.rename(path, new_path)
|
46
|
+
context[:path] = new_path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
return context
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_first_mp3_path(path)
|
55
|
+
Dir.entries(path).each do |f|
|
56
|
+
if !File.directory?(f) and File.extname(f) == ".mp3"
|
57
|
+
file_path = path + f
|
58
|
+
return file_path
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
return nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_quality_string(bitrate, vbr)
|
66
|
+
if vbr
|
67
|
+
if bitrate > 230
|
68
|
+
return "[MP3 V0]"
|
69
|
+
elsif bitrate > 190
|
70
|
+
return "[MP3 V2]"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
return "[MP3 #{bitrate}]"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'mp3info'
|
2
|
+
require 'logging'
|
3
|
+
require 'active_support/all'
|
4
|
+
|
5
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
6
|
+
|
7
|
+
class FileName
|
8
|
+
include SanitizePlugin
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@log = Logging.logger[self]
|
12
|
+
@log.appenders = Logging.appenders.stdout
|
13
|
+
if $verbose
|
14
|
+
@log.level = :debug
|
15
|
+
else
|
16
|
+
@log.level = :info
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def sanitize(context)
|
21
|
+
path = context[:path]
|
22
|
+
Dir.chdir(path) { Dir["*.mp3"] }.each do |f|
|
23
|
+
file_name = f
|
24
|
+
file_path = path + file_name
|
25
|
+
|
26
|
+
new_file_name = get_fixed_file_name(file_path)
|
27
|
+
|
28
|
+
if new_file_name != file_name
|
29
|
+
new_file_path = path + new_file_name
|
30
|
+
File.rename(file_path, new_file_path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
return context
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_fixed_file_name(file_path)
|
38
|
+
# Return the original file_path by default
|
39
|
+
new_file_path = file_path
|
40
|
+
|
41
|
+
Mp3Info.open(file_path) do |mp3|
|
42
|
+
if mp3.tag.title and mp3.tag.artist and mp3.tag.tracknum
|
43
|
+
@log.debug "Reconstructing file name for #{file_path}"
|
44
|
+
title = mp3.tag.title
|
45
|
+
artist = mp3.tag.artist
|
46
|
+
tracknum = mp3.tag.tracknum.to_s
|
47
|
+
# Append a 0 to tracknum if necessary
|
48
|
+
if tracknum.length == 1
|
49
|
+
tracknum = "0" + tracknum
|
50
|
+
end
|
51
|
+
|
52
|
+
# Append a period to the trackname
|
53
|
+
tracknum = tracknum + "."
|
54
|
+
|
55
|
+
# Handle any remix parenthesis
|
56
|
+
title_parts = title.split('-')
|
57
|
+
if title_parts.length > 1
|
58
|
+
title_part = title_parts[0].strip
|
59
|
+
remix_part = title_parts[1].strip
|
60
|
+
|
61
|
+
title = title_part + " (#{remix_part})"
|
62
|
+
end
|
63
|
+
|
64
|
+
new_file_path = "#{tracknum} #{artist} - #{title}" + File.extname(file_path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
return new_file_path
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'mp3info'
|
2
|
+
require 'logging'
|
3
|
+
require 'active_support/all'
|
4
|
+
|
5
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
6
|
+
|
7
|
+
class Hidden
|
8
|
+
include SanitizePlugin
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@log = Logging.logger[self]
|
12
|
+
@log.appenders = Logging.appenders.stdout
|
13
|
+
if $verbose
|
14
|
+
@log.level = :debug
|
15
|
+
else
|
16
|
+
@log.level = :info
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def sanitize(context)
|
21
|
+
path = context[:path]
|
22
|
+
Dir.entries(path).each do |entry|
|
23
|
+
# We only want to remove hidden files... not directories. ASK ME HOW I KNOW!
|
24
|
+
if !File.directory? entry
|
25
|
+
# Remove hidden files
|
26
|
+
if entry.start_with? "."
|
27
|
+
@log.debug "Deleting hidden file #{path + entry}"
|
28
|
+
FileUtils.rm_rf(path + entry)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
return context
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'mp3info'
|
2
|
+
require 'logging'
|
3
|
+
|
4
|
+
require File.expand_path("../sanitize_plugin", __FILE__)
|
5
|
+
|
6
|
+
class Tags
|
7
|
+
|
8
|
+
include SanitizePlugin
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@log = Logging.logger[self]
|
12
|
+
@log.appenders = Logging.appenders.stdout
|
13
|
+
if $verbose
|
14
|
+
@log.level = :debug
|
15
|
+
else
|
16
|
+
@log.level = :info
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def sanitize(context)
|
21
|
+
path = context[:path]
|
22
|
+
|
23
|
+
Dir.entries(path).each do |f|
|
24
|
+
if !File.directory?(f) and File.extname(f) == ".mp3"
|
25
|
+
file_path = path + f
|
26
|
+
Mp3Info.open(file_path) do |mp3|
|
27
|
+
# Remove all comments
|
28
|
+
if not mp3.tag.comments.nil? or not mp3.tag2.COMM.nil?
|
29
|
+
mp3.tag.comments = nil
|
30
|
+
mp3.tag2.COMM = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Handle publisher tag
|
34
|
+
mp3.tag2.TPUB = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return context
|
39
|
+
end
|
40
|
+
end
|
data/lib/what_cd/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
module WhatCD
|
2
|
+
VERSION = "0.0.5"
|
3
3
|
end
|
metadata
CHANGED
@@ -1,41 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: what_cd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Parraga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: commander
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mp3info
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: mktorrent
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- -
|
45
|
+
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
47
|
version: '0'
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- -
|
52
|
+
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '0'
|
41
55
|
description: Useful CLI tools for What.cd
|
@@ -47,10 +61,22 @@ extensions: []
|
|
47
61
|
extra_rdoc_files: []
|
48
62
|
files:
|
49
63
|
- README.md
|
50
|
-
- lib/better.rb
|
51
|
-
- lib/what_cd/version.rb
|
52
|
-
- lib/what_cd.rb
|
53
64
|
- bin/what_cd
|
65
|
+
- lib/what_cd.rb
|
66
|
+
- lib/what_cd/better.rb
|
67
|
+
- lib/what_cd/better_plugins/better_plugin.rb
|
68
|
+
- lib/what_cd/better_plugins/convert.rb
|
69
|
+
- lib/what_cd/better_plugins/new_dir.rb
|
70
|
+
- lib/what_cd/metadata.rb
|
71
|
+
- lib/what_cd/sanitize.rb
|
72
|
+
- lib/what_cd/sanitize_plugins/bitrate.rb
|
73
|
+
- lib/what_cd/sanitize_plugins/cover.rb
|
74
|
+
- lib/what_cd/sanitize_plugins/dir_name.rb
|
75
|
+
- lib/what_cd/sanitize_plugins/file_name.rb
|
76
|
+
- lib/what_cd/sanitize_plugins/hidden.rb
|
77
|
+
- lib/what_cd/sanitize_plugins/sanitize_plugin.rb
|
78
|
+
- lib/what_cd/sanitize_plugins/tags.rb
|
79
|
+
- lib/what_cd/version.rb
|
54
80
|
homepage: https://github.com/Sovietaced/what-cd
|
55
81
|
licenses: []
|
56
82
|
metadata: {}
|
@@ -60,17 +86,17 @@ require_paths:
|
|
60
86
|
- lib
|
61
87
|
required_ruby_version: !ruby/object:Gem::Requirement
|
62
88
|
requirements:
|
63
|
-
- -
|
89
|
+
- - ">="
|
64
90
|
- !ruby/object:Gem::Version
|
65
91
|
version: '0'
|
66
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
93
|
requirements:
|
68
|
-
- -
|
94
|
+
- - ">="
|
69
95
|
- !ruby/object:Gem::Version
|
70
96
|
version: '0'
|
71
97
|
requirements: []
|
72
98
|
rubyforge_project:
|
73
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.4.6
|
74
100
|
signing_key:
|
75
101
|
specification_version: 4
|
76
102
|
summary: Useful CLI tools for What.cd
|
data/lib/better.rb
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
require 'shellwords'
|
3
|
-
require 'mktorrent'
|
4
|
-
|
5
|
-
'''
|
6
|
-
This tool is primarily for working through better.php with ease.
|
7
|
-
Essentially this tool will look at an existing release directory with
|
8
|
-
FLAC files and create a matching release with MP3 V0 files. A best attempt
|
9
|
-
is made to create an appropriate folder name. Any non-flac files are moved
|
10
|
-
over and then flac files are converted to MP3 V0.
|
11
|
-
|
12
|
-
Dependencies:
|
13
|
-
flac2mp3: https://github.com/ymendel/flac2mp3
|
14
|
-
|
15
|
-
'''
|
16
|
-
|
17
|
-
class Better
|
18
|
-
|
19
|
-
attr_reader :dir, :dir_name, :new_dir, :files, :tracker
|
20
|
-
|
21
|
-
def initialize(dir_name, tracker)
|
22
|
-
# dir will be modified later
|
23
|
-
@dir = dir_name
|
24
|
-
@dir_name = dir_name.chomp("/")
|
25
|
-
@tracker = tracker
|
26
|
-
@files = []
|
27
|
-
|
28
|
-
run
|
29
|
-
end
|
30
|
-
|
31
|
-
def run
|
32
|
-
|
33
|
-
handle_dirs
|
34
|
-
|
35
|
-
load_flac_files
|
36
|
-
|
37
|
-
puts @files
|
38
|
-
|
39
|
-
copy_files(@dir, @new_dir)
|
40
|
-
|
41
|
-
# Finally, convert to MP3 V0 if necessary
|
42
|
-
convert(@dir, @new_dir)
|
43
|
-
|
44
|
-
#create_torrent
|
45
|
-
|
46
|
-
puts "Woot! Conversion successful!"
|
47
|
-
puts "Find your files in #{new_dir}"
|
48
|
-
end
|
49
|
-
|
50
|
-
def load_flac_files
|
51
|
-
flac_files = Dir.entries(dir).select { |e| e.include? '.flac' }
|
52
|
-
|
53
|
-
if flac_files.empty?
|
54
|
-
puts "No flac files found. Exiting..."
|
55
|
-
exit
|
56
|
-
end
|
57
|
-
|
58
|
-
flac_files.each do | flac_file|
|
59
|
-
@files.push(dir + flac_file)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
def handle_dirs
|
65
|
-
# clear up inconsistencies
|
66
|
-
if @dir.chars.last != '/'
|
67
|
-
@dir = @dir + '/'
|
68
|
-
end
|
69
|
-
|
70
|
-
# Since mutable
|
71
|
-
@new_dir = @dir.dup
|
72
|
-
|
73
|
-
determine_new_dir_name(@new_dir)
|
74
|
-
|
75
|
-
# Attach to full path
|
76
|
-
@dir = Dir.pwd + "/" + @dir
|
77
|
-
@new_dir = Dir.pwd + "/" + @new_dir
|
78
|
-
|
79
|
-
# Create the new directory if it does not exist
|
80
|
-
Dir.mkdir(@new_dir) unless File.exists?(@new_dir)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Intelligently decide on a new directory name
|
84
|
-
def determine_new_dir_name(new_dir)
|
85
|
-
if new_dir.include? 'FLAC'
|
86
|
-
new_dir.gsub! 'FLAC', 'MP3 V0'
|
87
|
-
elsif new_dir.include? 'flac'
|
88
|
-
new_dir.gsub! 'flac', 'MP3 V0'
|
89
|
-
else
|
90
|
-
new_dir.gsub! '/', ' [MP3 V0]/'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def convert(dir, new_dir)
|
95
|
-
|
96
|
-
@files.each do |flac_file|
|
97
|
-
puts "Converting #{flac_file}"
|
98
|
-
cmd = "cd #{Shellwords.escape(new_dir)}; flac2mp3 #{Shellwords.escape(flac_file)} --encoding='--preset extreme' > /dev/null 2>&1"
|
99
|
-
output = system "bash -c \"#{cmd}\""
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def copy_files(dir, new_dir)
|
104
|
-
# Copy all files from dir to new_dir
|
105
|
-
cmd = "cp -r #{Shellwords.escape(dir)}/* #{Shellwords.escape(new_dir)}"
|
106
|
-
system "bash -c \"#{cmd}\""
|
107
|
-
|
108
|
-
# Remove any flac files, as they will be replaced by MP#s
|
109
|
-
cmd = "rm #{Shellwords.escape(new_dir)}*.flac"
|
110
|
-
system "bash -c \"#{cmd}\""
|
111
|
-
end
|
112
|
-
|
113
|
-
def create_torrent
|
114
|
-
t = Torrent.new(@tracker)
|
115
|
-
@files.each do |flac_file|
|
116
|
-
t.add_file(flac_file)
|
117
|
-
end
|
118
|
-
t.defaultdir = @dir_name
|
119
|
-
t.set_private
|
120
|
-
t.write_torrent("#{@dir_name}.torrent")
|
121
|
-
end
|
122
|
-
end
|