lossfully 0.0.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/.bnsignore +19 -0
- data/.gitmodules +3 -0
- data/CHANGELOG +4 -0
- data/COPYING +674 -0
- data/README.rdoc +96 -0
- data/Rakefile +27 -0
- data/lib/lossfully/audio_file.rb +134 -0
- data/lib/lossfully/generator.rb +614 -0
- data/lib/lossfully/input_rules.rb +176 -0
- data/lib/lossfully/thread_pool.rb +162 -0
- data/lib/lossfully.rb +79 -0
- data/test/test_audio_file.rb +47 -0
- data/test/test_input_rules.rb +125 -0
- data/test/test_thread_pool.rb +77 -0
- data/version.txt +1 -0
- metadata +100 -0
data/README.rdoc
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
= lossfully
|
2
|
+
|
3
|
+
Smartly generate transcoded (lossy or otherwise) versions of your main music
|
4
|
+
library.
|
5
|
+
|
6
|
+
== Examples
|
7
|
+
|
8
|
+
This is not your average, "drag a bunch of folders on me and press go"
|
9
|
+
audio converter. You need to write a file that points to your library
|
10
|
+
and describes how you want to generate a second library. Let's start
|
11
|
+
with:
|
12
|
+
|
13
|
+
require 'lossfully'
|
14
|
+
|
15
|
+
Lossfully.generate '~/share/music/' => '~/share/music_lossy' do
|
16
|
+
encode '.ogg'
|
17
|
+
end
|
18
|
+
|
19
|
+
That will copy everything from ~/share/music to ~/share/music_lossy,
|
20
|
+
encoding all of the non-vorbis audio files to vorbis. So far, this is
|
21
|
+
something any of those GUI programs can do. But maybe you don't want
|
22
|
+
to convert your lossy formats into oggs, lest the gods of information
|
23
|
+
theory smite you for your transcoding wickedness. Well, no problem.
|
24
|
+
This converts any wav, flac, or wv files found into ogg, but copies
|
25
|
+
the rest without modification:
|
26
|
+
|
27
|
+
Lossfully.generate '~/share/music/' => '~/share/music_lossy' do
|
28
|
+
encode :lossless => '.ogg'
|
29
|
+
end
|
30
|
+
|
31
|
+
But maybe this new library is going on your portable device, so you
|
32
|
+
can't take all of your music and you need to shrink some of the lossy
|
33
|
+
files, too. How about just taking the songs from a playlist, and
|
34
|
+
reencoding the lossy files that have an average bitrate over 192 kbps:
|
35
|
+
|
36
|
+
Lossfully.generate '~/share/music/awesome.m3u' => '~/share/music_lossy' do
|
37
|
+
encode :lossless => ['.ogg', 4] # use quality 4 for the encoding
|
38
|
+
encode [:lossy, 192] => ['.ogg', 4]
|
39
|
+
end
|
40
|
+
|
41
|
+
You see, where this is going, right? Maybe something like:
|
42
|
+
|
43
|
+
Lossfully.generate '~/share/music/awesome.m3u' => '~/share/music_lossy' do
|
44
|
+
clobber :rename
|
45
|
+
threads 2
|
46
|
+
remove_missing true
|
47
|
+
|
48
|
+
skip /\.cue/
|
49
|
+
skip /\.jpg/
|
50
|
+
|
51
|
+
encode :lossless => 'flac'
|
52
|
+
encode [:ogg, 192] => ['.ogg', 4]
|
53
|
+
encode [:mp3, 300] => ['.mp3', -192.2]
|
54
|
+
encode :lossy => 'ogg'
|
55
|
+
end
|
56
|
+
|
57
|
+
For more details, check out the documentation of Lossfully::Generator.
|
58
|
+
|
59
|
+
== Requirements
|
60
|
+
|
61
|
+
* A copy of sox must be found in your path, which is what actually
|
62
|
+
does the transcoding. There is currently no check to see if your
|
63
|
+
version of sox is compiled with LAME (support for MP3s, etc). When
|
64
|
+
confronted with audio files that sox can't handle, Lossfully might
|
65
|
+
treat them as nonaudio files, skip them silently, or crash. Who
|
66
|
+
knows.
|
67
|
+
|
68
|
+
* Lots of audio files.
|
69
|
+
|
70
|
+
== Install
|
71
|
+
|
72
|
+
* gem install lossfully
|
73
|
+
|
74
|
+
== Author
|
75
|
+
|
76
|
+
Original author: Don March <don@ohspite.net>
|
77
|
+
|
78
|
+
== License
|
79
|
+
|
80
|
+
Copyright (C) 2011 Don March.
|
81
|
+
|
82
|
+
Licensed under the GNU General Public License.
|
83
|
+
|
84
|
+
Lossfully is free software: you can redistribute it and/or modify it
|
85
|
+
under the terms of the GNU General Public License as published by the
|
86
|
+
Free Software Foundation, either version 3 of the License, or (at your
|
87
|
+
option) any later version.
|
88
|
+
|
89
|
+
Lossfully is distributed in the hope that it will be useful, but
|
90
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
91
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
92
|
+
General Public License for more details.
|
93
|
+
|
94
|
+
You should have received a copy of the GNU General Public License
|
95
|
+
along with this program. If not, see
|
96
|
+
<http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bones'
|
3
|
+
rescue LoadError
|
4
|
+
abort '### Please install the "bones" gem ###'
|
5
|
+
end
|
6
|
+
|
7
|
+
# ensure_in_path 'lib'
|
8
|
+
|
9
|
+
task :default => 'test:run'
|
10
|
+
task 'gem:release' => 'test:run'
|
11
|
+
|
12
|
+
Bones do
|
13
|
+
name 'lossfully'
|
14
|
+
authors 'Don'
|
15
|
+
email 'don@ohspite.net'
|
16
|
+
url 'https://github.com/ohspite/lossfully'
|
17
|
+
history_file 'CHANGELOG'
|
18
|
+
readme_file 'README.rdoc'
|
19
|
+
rdoc.main 'README.rdoc'
|
20
|
+
summary 'Smartly generate transcoded (lossy or not) versions of your main music library.'
|
21
|
+
description 'Smartly generate transcoded (lossy or not) versions of your main music library.'
|
22
|
+
|
23
|
+
ignore_file '.gitignore'
|
24
|
+
exclude %w(tmp$ bak$ ~$ CVS \.svn/ \.git/ \.brz/ \.bzrignore ^pkg/ ^coverage/, ^test/data)
|
25
|
+
rdoc.include %w(README ^lib/ ^bin/ ^ext/ \.txt$ \.rdoc$)
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2011 Don March
|
3
|
+
#
|
4
|
+
# This file is part of Lossfully.
|
5
|
+
#
|
6
|
+
# Lossfully is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Lossfully is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
require 'fileutils'
|
22
|
+
|
23
|
+
module Lossfully
|
24
|
+
class AudioFile
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Raises RuntimeError if +path+ is not a file.
|
29
|
+
#
|
30
|
+
def self.check_file path
|
31
|
+
raise "Does not exist: #{path}" unless File.file? path
|
32
|
+
end
|
33
|
+
|
34
|
+
public
|
35
|
+
|
36
|
+
def self.soxi_command path, options=''
|
37
|
+
check_file path
|
38
|
+
|
39
|
+
# system("soxi -V0 #{path}")
|
40
|
+
return nil if File.extname(path) == '.m3u'
|
41
|
+
p = IO.popen("sox --info -V0 #{options} \"#{path}\"")
|
42
|
+
return ((Process.wait2 p.pid)[1] == 0) ? p.gets.chomp : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.def_soxi(method, options='')
|
46
|
+
class_eval <<-EOM
|
47
|
+
def self.#{method} path
|
48
|
+
self.soxi_command path, '#{options}'
|
49
|
+
end
|
50
|
+
EOM
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return the encoding of the file ('soxi -e'). If soxi does not
|
54
|
+
# recognize the file as audio, return nil.
|
55
|
+
#
|
56
|
+
def_soxi :encoding, '-e'
|
57
|
+
|
58
|
+
# Return the file-type as a symbol ('soxi -t'). If soxi does not
|
59
|
+
# recognize the file as audio, return nil.
|
60
|
+
#
|
61
|
+
def self.type path
|
62
|
+
t = soxi_command path, '-t'
|
63
|
+
return t ? t.to_sym : nil
|
64
|
+
end
|
65
|
+
# def_soxi :type, '-t'
|
66
|
+
|
67
|
+
# Return the bitrate of the file as a string ('soxi -B'). If soxi
|
68
|
+
# does not recognize the file as audio, return nil.
|
69
|
+
#
|
70
|
+
def_soxi :bitrate, '-B'
|
71
|
+
|
72
|
+
# Return the bitrate of the file as an integer in kbps ('soxi -B'). If soxi
|
73
|
+
# does not recognize the file as audio, return nil.
|
74
|
+
#
|
75
|
+
def self.bitrate_kbps path
|
76
|
+
b = bitrate(path)
|
77
|
+
return b.to_f * (b[-1..-1] == 'k' ? 1 : 1000)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return the duration of the file in seconds as a Float
|
81
|
+
# ('soxi -D'). If soxi does not recognize the file as audio,
|
82
|
+
# return nil.
|
83
|
+
#
|
84
|
+
def self.duration path
|
85
|
+
#(soxi_command path, '-D').to_f
|
86
|
+
`soxi -V0 -D \"#{path}\"`.chomp.to_f
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
alias :is_audio? :type
|
91
|
+
alias :length :duration
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.encode input_path, output_path, options='', effect_options=''
|
95
|
+
FileUtils.mkdir_p(File.dirname(output_path)) unless File.directory? output_path
|
96
|
+
system("sox \"#{input_path}\" #{options} \"#{output_path}\" #{effect_options}")
|
97
|
+
end
|
98
|
+
|
99
|
+
def initialize path
|
100
|
+
@path = path
|
101
|
+
|
102
|
+
# Actually, let's not do this. This gets checked every time a
|
103
|
+
# method is run anyway, so this way we can just use the class as
|
104
|
+
# a wrapper around a path string for files that aren't audio.
|
105
|
+
#
|
106
|
+
# raise "Not recognized as an audio file: #{file}" unless is_audio?
|
107
|
+
end
|
108
|
+
|
109
|
+
attr_reader :path
|
110
|
+
|
111
|
+
# This could be done with method_missing.
|
112
|
+
def self.delegate_and_memoize(method, class_method=nil)
|
113
|
+
class_method ||= method
|
114
|
+
class_eval <<-EOM
|
115
|
+
def #{method}
|
116
|
+
@#{method} ||= self.class.#{class_method}(@path)
|
117
|
+
end
|
118
|
+
EOM
|
119
|
+
end
|
120
|
+
|
121
|
+
def encode output_path, options='', effect_options=''
|
122
|
+
self.class.encode @path, output_path, options, effect_options
|
123
|
+
end
|
124
|
+
|
125
|
+
delegate_and_memoize :encoding
|
126
|
+
delegate_and_memoize :type
|
127
|
+
delegate_and_memoize :bitrate
|
128
|
+
delegate_and_memoize :bitrate_kbps
|
129
|
+
delegate_and_memoize :duration
|
130
|
+
|
131
|
+
alias :length :duration
|
132
|
+
alias :is_audio? :type
|
133
|
+
end
|
134
|
+
end
|