icemaker 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/README.md +33 -0
- data/Rakefile +10 -0
- data/bin/icemaker +5 -0
- data/icemaker.gemspec +26 -0
- data/lib/icemaker.rb +11 -0
- data/lib/icemaker/filter.rb +97 -0
- data/lib/icemaker/icemaker.rb +133 -0
- data/lib/icemaker/query.rb +37 -0
- data/lib/icemaker/version.rb +3 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c9704bb75c9c106080a4816897bffb3c9edf9953
|
4
|
+
data.tar.gz: 38727cf25913f22bb78e87b87ab3f10ad5549e38
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c44e11e086d03248c277dc4cd5fcec8a0bda44c4afd2b1807c742f2ed131cfe85c5d9a588b52b0362e5b3d830e3b9bb04e763773230e4306b1ad17d595fdcc10
|
7
|
+
data.tar.gz: 556f996c0971dbe5df781db0c90eb2ab548c58600f3e50bef48d5744be366ccbfad0416f4de99ebe7d30175f5c438700195e0be43ea4f5283f5e8164db9d4bce
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Icemaker
|
2
|
+
|
3
|
+
Create playlists by applying customized search filters to a set of audio files.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'icemaker'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install icemaker
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Development
|
26
|
+
|
27
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
28
|
+
|
29
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/twocats/icemaker.
|
data/Rakefile
ADDED
data/bin/icemaker
ADDED
data/icemaker.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'icemaker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "icemaker"
|
8
|
+
spec.version = Icemaker::VERSION
|
9
|
+
spec.authors = ["twocats"]
|
10
|
+
spec.email = ["twocats@smallisystems.com"]
|
11
|
+
|
12
|
+
spec.summary = "Create playlists by applying customized search filters to a set of audio files."
|
13
|
+
spec.homepage = "https://github.com/twocats/icemaker.git"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "bin"
|
17
|
+
spec.executables = "icemaker"
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "minitest"
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'taglib-ruby', ">= 0.7.0"
|
25
|
+
|
26
|
+
end
|
data/lib/icemaker.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module IceMaker
|
2
|
+
|
3
|
+
class Filter
|
4
|
+
|
5
|
+
attr_writer :tag_value
|
6
|
+
|
7
|
+
def regx
|
8
|
+
if @value_to_test.instance_of? Hash then
|
9
|
+
|
10
|
+
operator = @value_to_test.keys[0]
|
11
|
+
values = @value_to_test.values[0]
|
12
|
+
|
13
|
+
case operator
|
14
|
+
when :or, :union_of
|
15
|
+
values.any? { |value| @tag_value =~ (Regexp.new value) }
|
16
|
+
when :and, :intersection_of
|
17
|
+
values.all? { |value| @tag_value =~ (Regexp.new value) }
|
18
|
+
end
|
19
|
+
|
20
|
+
else
|
21
|
+
@tag_value =~ (Regexp.new @value_to_test)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def satisfies
|
27
|
+
regx
|
28
|
+
end
|
29
|
+
|
30
|
+
def has
|
31
|
+
if @value_to_test.instance_of? Hash then
|
32
|
+
|
33
|
+
operator = @value_to_test.keys[0]
|
34
|
+
values = @value_to_test.values[0]
|
35
|
+
|
36
|
+
case operator
|
37
|
+
when :or, :union_of
|
38
|
+
values.any? { |value| @tag_value.downcase.has substring: value.downcase }
|
39
|
+
when :and, :intersection_of
|
40
|
+
values.all? { |value| @tag_value.downcase.has substring: value.downcase }
|
41
|
+
end
|
42
|
+
|
43
|
+
else
|
44
|
+
@tag_value.downcase.has substring: @value_to_test.downcase
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def contains
|
49
|
+
has
|
50
|
+
end
|
51
|
+
|
52
|
+
def is
|
53
|
+
if @value_to_test.instance_of? Hash then
|
54
|
+
|
55
|
+
operator = @value_to_test.keys[0]
|
56
|
+
values = @value_to_test.values[0]
|
57
|
+
|
58
|
+
puts "error in filter, #{@tag_value} :is #{@value_to_test}; applying 'or' rather than 'and' for :is" if (operator == :and || operator == :intersection_of)
|
59
|
+
|
60
|
+
values.any? { |value| @tag_value.downcase == value.downcase }
|
61
|
+
else
|
62
|
+
@tag_value.downcase == @value_to_test.downcase
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def eq
|
67
|
+
is
|
68
|
+
end
|
69
|
+
|
70
|
+
def lte
|
71
|
+
@tag_value <= @value_to_test
|
72
|
+
end
|
73
|
+
|
74
|
+
def gte
|
75
|
+
@tag_value >= @value_to_test
|
76
|
+
end
|
77
|
+
|
78
|
+
def lt
|
79
|
+
@tag_value < @value_to_test
|
80
|
+
end
|
81
|
+
|
82
|
+
def gt
|
83
|
+
@tag_value > @value_to_test
|
84
|
+
end
|
85
|
+
|
86
|
+
def pass?
|
87
|
+
self.send @test
|
88
|
+
end
|
89
|
+
|
90
|
+
def initialize(tag, value_to_test, test)
|
91
|
+
@tag = tag
|
92
|
+
@value_to_test = value_to_test
|
93
|
+
@test = test
|
94
|
+
@tag_value = ''
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module IceMaker
|
2
|
+
|
3
|
+
VALID_MEDIA_EXTENSIONS = [".mp3", ".ogg", ".flac"]
|
4
|
+
UNKNOWN_VALUE = 'unknown'
|
5
|
+
|
6
|
+
class Playlist
|
7
|
+
|
8
|
+
def self.filter_tracks
|
9
|
+
|
10
|
+
@@filtered = ((Query.new with: @@icefile.filters).find in: @@tracks)
|
11
|
+
|
12
|
+
verbalize "#{@@filtered.length} tracks found."
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.make_tracks_from_files
|
17
|
+
|
18
|
+
@@tracks = []
|
19
|
+
|
20
|
+
@@files.each do |file|
|
21
|
+
|
22
|
+
TagLib::FileRef.open(file) do |info|
|
23
|
+
|
24
|
+
unless info.null?
|
25
|
+
|
26
|
+
@@tracks << {
|
27
|
+
file: file,
|
28
|
+
title: info.tag.title ? info.tag.title : UNKNOWN_VALUE,
|
29
|
+
artist: info.tag.artist ? info.tag.artist : UNKNOWN_VALUE,
|
30
|
+
album: info.tag.album ? info.tag.album : UNKNOWN_VALUE,
|
31
|
+
genre: info.tag.genre ? info.tag.genre : UNKNOWN_VALUE,
|
32
|
+
year: info.tag.year ? info.tag.year : UNKNOWN_VALUE,
|
33
|
+
length: info.audio_properties.length ? info.audio_properties.length : 0,
|
34
|
+
bitrate: info.audio_properties.bitrate ? info.audio_properties.bitrate : 0,
|
35
|
+
channels: info.audio_properties.channels ? info.audio_properties.channels : 0,
|
36
|
+
sample_rate: info.audio_properties.sample_rate ? info.audio_properties.sample_rate : 0
|
37
|
+
}
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.collect_files
|
45
|
+
|
46
|
+
@@files = []
|
47
|
+
|
48
|
+
@@icefile.sources.each do |source|
|
49
|
+
|
50
|
+
@@files << (get_entries from: source, and_recurse_into_if: @@icefile.is_recursive)
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
@@files.flatten!.map! { |file| "#{@@icefile.arbitrary_file_prefix}#{file}"}
|
55
|
+
|
56
|
+
@@files.uniq! if @@icefile.unique
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.remove_non_audio_files
|
61
|
+
|
62
|
+
verbalize "#{@@files.length} total files found."
|
63
|
+
|
64
|
+
@@files.reject! { |file|
|
65
|
+
!(VALID_MEDIA_EXTENSIONS.include? (File.extname file).downcase) || (File.directory? file )
|
66
|
+
}
|
67
|
+
|
68
|
+
verbalize "#{@@files.length} audio files found."
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.get_entries(params)
|
73
|
+
|
74
|
+
source = params[:from]
|
75
|
+
recurse = params[:and_recurse_into_if]
|
76
|
+
|
77
|
+
entries = []
|
78
|
+
|
79
|
+
if recurse
|
80
|
+
entries << Dir[File.join(source, "**", "*")]
|
81
|
+
else
|
82
|
+
entries << (Dir.entries source).reject! { |x| x == '..' || x == '.' }
|
83
|
+
entries.flatten!.map! { |file| "#{source}/#{file}"}
|
84
|
+
end
|
85
|
+
|
86
|
+
entries
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.write_playlist
|
91
|
+
File.open(@@icefile.output_to_file, 'w') { |f| f.puts(@@formatted) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.format_for_writing
|
95
|
+
@@formatted = @@filtered.map &@@icefile.formatter
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.verbalize(some_words)
|
99
|
+
puts some_words if @@icefile.verbose
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.make
|
103
|
+
|
104
|
+
@@icefile = Icefile.new
|
105
|
+
|
106
|
+
yield @@icefile
|
107
|
+
|
108
|
+
collect_files
|
109
|
+
remove_non_audio_files
|
110
|
+
make_tracks_from_files
|
111
|
+
filter_tracks
|
112
|
+
format_for_writing
|
113
|
+
write_playlist
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class Icefile
|
118
|
+
|
119
|
+
attr_accessor :sources, :is_recursive, :unique, :verbose,
|
120
|
+
:output_to_file, :arbitrary_file_prefix, :filters, :formatter
|
121
|
+
|
122
|
+
def initialize
|
123
|
+
@sources = []
|
124
|
+
@filters = []
|
125
|
+
@verbose = false
|
126
|
+
@is_recursive = false
|
127
|
+
@unique = true
|
128
|
+
@arbitrary_file_prefix = ''
|
129
|
+
@formatter = lambda { |track| "#{track[:file]}"}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module IceMaker
|
2
|
+
|
3
|
+
class Query
|
4
|
+
|
5
|
+
# TODO: THE NAMING/LANGUAGE IN THIS CLASS IS TEH SUCK
|
6
|
+
|
7
|
+
def find(params)
|
8
|
+
|
9
|
+
tracks = params[:in]
|
10
|
+
|
11
|
+
@filters.each { |expr|
|
12
|
+
|
13
|
+
tag = expr.keys[0]
|
14
|
+
value_to_test = expr.values[0].values[0]
|
15
|
+
operator = expr.values[0].keys[0]
|
16
|
+
|
17
|
+
filter = Filter.new tag, value_to_test, operator
|
18
|
+
|
19
|
+
tracks = tracks.select {
|
20
|
+
|
21
|
+
|track|
|
22
|
+
|
23
|
+
filter.tag_value = track[tag.to_sym]
|
24
|
+
|
25
|
+
filter.pass?
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
tracks
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(params)
|
34
|
+
@filters = params[:with]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: icemaker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- twocats
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: taglib-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.7.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.7.0
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- twocats@smallisystems.com
|
72
|
+
executables:
|
73
|
+
- icemaker
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bin/icemaker
|
83
|
+
- icemaker.gemspec
|
84
|
+
- lib/icemaker.rb
|
85
|
+
- lib/icemaker/filter.rb
|
86
|
+
- lib/icemaker/icemaker.rb
|
87
|
+
- lib/icemaker/query.rb
|
88
|
+
- lib/icemaker/version.rb
|
89
|
+
homepage: https://github.com/twocats/icemaker.git
|
90
|
+
licenses: []
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.4.8
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Create playlists by applying customized search filters to a set of audio
|
112
|
+
files.
|
113
|
+
test_files: []
|