lastfm-m3u 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/Rakefile +8 -6
- data/bin/lastfm-m3u +158 -0
- data/lastfm-m3u.gemspec +1 -0
- data/lib/lastfm-m3u.rb +1 -1
- data/lib/lastfm-m3u/base.rb +11 -0
- data/lib/lastfm-m3u/lastfm.rb +48 -0
- data/lib/lastfm-m3u/mp3_info_ext.rb +57 -0
- data/lib/lastfm-m3u/search/file_search.rb +101 -0
- data/lib/lastfm-m3u/util.rb +19 -0
- data/lib/lastfm-m3u/version.rb +3 -0
- metadata +21 -4
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
require 'rubygems'
|
3
|
+
require "bundler/setup"
|
2
4
|
require "bundler/gem_tasks"
|
3
5
|
|
4
6
|
# RSpec
|
5
|
-
require 'rspec/core'
|
6
|
-
require 'rspec/core/rake_task'
|
7
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
9
|
-
end
|
10
|
-
task :default => :spec
|
7
|
+
require 'rspec/core'
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
10
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
11
|
+
end
|
12
|
+
task :default => :spec
|
data/bin/lastfm-m3u
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'highline/import'
|
6
|
+
require 'ruby-progressbar'
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
9
|
+
require 'lastfm-m3u'
|
10
|
+
require 'lastfm-m3u/util'
|
11
|
+
|
12
|
+
options = OpenStruct.new
|
13
|
+
OptionParser.new do |opts|
|
14
|
+
opts.banner = "LastfmM3u Playlist Creator".color(:blue)
|
15
|
+
opts.define_head "To get started: lastfm-m3u --init".color(:green)
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator "Usage: lastfm-m3u [options]".color(:yellow)
|
18
|
+
opts.separator "Note: defaults to filename-only search. id3/both search is more thorough, but much slower"
|
19
|
+
opts.separator ""
|
20
|
+
opts.separator "Examples:"
|
21
|
+
#opts.separator " lastfm-m3u # auto-search config-music_dir mp3 id3 tags for all unique artists, query last.fm for top tracks and create m3us for each artist from available track files"
|
22
|
+
opts.separator ' lastfm-m3u -a "Biosphere,Brian Eno"'
|
23
|
+
opts.separator " lastfm-m3u -a Biosphere"
|
24
|
+
opts.separator " lastfm-m3u -a Biosphere -t both"
|
25
|
+
opts.separator " lastfm-m3u -a Biosphere -d ~/music/Biosphere"
|
26
|
+
#opts.separator " lastfm-m3u -a Biosphere -o biosphere.m3u"
|
27
|
+
opts.separator ""
|
28
|
+
opts.separator ""
|
29
|
+
opts.separator "Options:"
|
30
|
+
|
31
|
+
# Type of search
|
32
|
+
types = %w[file id3 both]
|
33
|
+
type_aliases = { "f" => "file", "i" => "id3", "b" => "both" }
|
34
|
+
type_list = (type_aliases.keys + types).join(',')
|
35
|
+
opts.on("--type TYPE", "-t TYPE", types, type_aliases, "Select search type", " (#{type_list})") do |type|
|
36
|
+
options.type = type.to_sym
|
37
|
+
end
|
38
|
+
options.type = :file if options.type.blank? or options.type.nil?
|
39
|
+
|
40
|
+
# init config
|
41
|
+
opts.on('-i', '--init', "init lastfm API access config") do |opt|
|
42
|
+
options.init = true
|
43
|
+
end
|
44
|
+
|
45
|
+
# List of artists
|
46
|
+
opts.on('-a "x,y,z"', Array, "one or more artists separated by commas (enclose with quotes if any spaces)") do |artists|
|
47
|
+
options.artists = artists
|
48
|
+
end
|
49
|
+
|
50
|
+
# directory
|
51
|
+
options.directory = Dir.pwd
|
52
|
+
opts.on("-d DIR", "--directory DIR", "music directory") do |directory|
|
53
|
+
options.directory = directory
|
54
|
+
end
|
55
|
+
|
56
|
+
#opts.on('-o', '--output FILENAME', 'Write m3u to FILENAME') do |fn| # writes single m3u when multiple artists
|
57
|
+
#options.out_file = fn
|
58
|
+
#end
|
59
|
+
|
60
|
+
opts.on('-p', '--path PATH', 'Specify path to use for m3u file entries (e.g. MPD requires a path relative to its music directory - i.e. -d /home/user/music/Biosphere -p /home/user/music where the second one is MPD\'s root)') do |path|
|
61
|
+
options.path = Pathname path
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('-l', '--limit NUM', 'Specify limit of tracks to fetch from Last.fm (defaults to all)') do |limit|
|
65
|
+
options.limit = limit.to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on('-f', '--not-found', 'Show not found') do
|
69
|
+
options.not_found = true
|
70
|
+
end
|
71
|
+
|
72
|
+
#opts.on('-f', '--force', 'Overwrite existing output file(s)') do
|
73
|
+
#options.force = true
|
74
|
+
#end
|
75
|
+
|
76
|
+
opts.on('--debug [LEVEL]', 'Enable debugging output (Optional Level :: 0=all, 1=info,warn,error, 2=warn,error, 3=error)') do |level|
|
77
|
+
if level
|
78
|
+
options.debug = level.to_i
|
79
|
+
else
|
80
|
+
options.debug = 0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on_tail('-v', '--version', 'Show LastfmM3u version') do
|
85
|
+
puts "LastfmM3u v#{LastfmM3u::VERSION}"
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on_tail('-h', '--help', 'Show this help message') do
|
90
|
+
puts opts
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
end.parse!
|
94
|
+
|
95
|
+
if options.init
|
96
|
+
lastfm = LastfmM3u::Lastfm.new('')
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
|
100
|
+
unless options.artists
|
101
|
+
puts "Please provide at least one artist (-a Elvis).".color(:red) + " See".color(:green) + " --help".color(:yellow) + " for usage or".color(:green) + " --init".color(:yellow) + " to get started".color(:green)
|
102
|
+
exit
|
103
|
+
end
|
104
|
+
|
105
|
+
if options.debug
|
106
|
+
$logger.level = options.debug
|
107
|
+
end
|
108
|
+
|
109
|
+
options.artists.each do |artist|
|
110
|
+
not_found = []
|
111
|
+
lastfm = LastfmM3u::Lastfm.new(artist)
|
112
|
+
lastfm.root_dir = options.directory if options.directory
|
113
|
+
lastfm.limit = options.limit if options.limit
|
114
|
+
tracks = lastfm.artist.top_tracks
|
115
|
+
|
116
|
+
puts
|
117
|
+
puts "Searching filesystem with method: ".color(:green) + options.type.to_s.capitalize.color(:yellow)
|
118
|
+
puts
|
119
|
+
num_tracks = (options.limit.nil? ? tracks.size : options.limit)
|
120
|
+
progressbar = ProgressBar.create(starting_at: 0, total: num_tracks, format: "%a".color(:cyan) + "|".color(:magenta) + "%B".color(:green) + "|".color(:magenta) + "%p%%".color(:cyan))
|
121
|
+
lastfm.find_tracks(tracks, options.type, progressbar).each do |track, track_set|
|
122
|
+
if track_set.empty?
|
123
|
+
not_found << track
|
124
|
+
next
|
125
|
+
end
|
126
|
+
|
127
|
+
if track_set.size > 1
|
128
|
+
unless found_track = LastfmM3u::Util.choose_track(track, track_set)
|
129
|
+
not_found << track
|
130
|
+
next
|
131
|
+
end
|
132
|
+
puts
|
133
|
+
else
|
134
|
+
found_track = track_set.first
|
135
|
+
end
|
136
|
+
if options.path
|
137
|
+
lastfm.m3u.add_file(found_track.relative_path_from options.path)
|
138
|
+
else
|
139
|
+
lastfm.m3u.add_file(found_track)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
puts
|
143
|
+
margin = 10
|
144
|
+
output_size = artist.length+10
|
145
|
+
puts " "*margin + ("="*output_size).color(:blue)
|
146
|
+
puts artist.center(output_size+margin*2).color(:green)
|
147
|
+
puts " "*margin + "#{num_tracks - not_found.size} of #{num_tracks} tracks found".center(output_size).color(:green)
|
148
|
+
puts " "*margin + ("="*output_size).color(:blue)
|
149
|
+
|
150
|
+
lastfm.m3u.write(File.join(lastfm.m3u_path, "#{artist}.m3u"))
|
151
|
+
|
152
|
+
unless not_found.empty? or options.not_found.nil?
|
153
|
+
puts
|
154
|
+
puts "Not Found:".color(:red)
|
155
|
+
not_found.each {|track| puts "#{track}".color(:yellow) }
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
data/lastfm-m3u.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_development_dependency 'terminal-notifier-guard', '~> 1.5.3'
|
22
22
|
gem.add_development_dependency 'rb-fsevent', '~> 0.9'
|
23
23
|
gem.add_development_dependency 'debugger', '~> 1.5.0'
|
24
|
+
gem.add_development_dependency 'pry-debugger', '~> 0.2'
|
24
25
|
gem.add_development_dependency 'simplecov', '~> 0.7.1'
|
25
26
|
|
26
27
|
gem.add_dependency 'rockstar', '~> 0.7.1'
|
data/lib/lastfm-m3u.rb
CHANGED
@@ -4,7 +4,6 @@ require 'pathname'
|
|
4
4
|
require 'lastfm-m3u/version'
|
5
5
|
|
6
6
|
require 'rockstar'
|
7
|
-
require 'mp3info'
|
8
7
|
require 'm3uzi'
|
9
8
|
require 'rainbow'
|
10
9
|
require 'ansi/logger'
|
@@ -12,6 +11,7 @@ require 'ansi/logger'
|
|
12
11
|
require 'lastfm-m3u/base'
|
13
12
|
require 'lastfm-m3u/lastfm'
|
14
13
|
require 'lastfm-m3u/search/file_search'
|
14
|
+
require 'lastfm-m3u/mp3_info_ext'
|
15
15
|
|
16
16
|
module LastfmM3u
|
17
17
|
$logger = ANSI::Logger.new(STDOUT)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module LastfmM3u
|
4
|
+
class Lastfm < Base
|
5
|
+
attr_accessor :artist, :root_dir, :limit, :tracks
|
6
|
+
|
7
|
+
def initialize(artist, root_dir='/home/jon/music/trance')
|
8
|
+
FileUtils.mkdir_p "#{ENV['HOME']}/.config"
|
9
|
+
config_path = "#{ENV['HOME']}/.config/lastfm-m3u.yml"
|
10
|
+
unless File.exists? config_path
|
11
|
+
puts "Copying empty config file to #{config_path}...".color(:green)
|
12
|
+
FileUtils.cp File.join(File.dirname(__FILE__), '../../config/lastfm.yml.dist'), config_path
|
13
|
+
end
|
14
|
+
config_hash = YAML.load_file(config_path)
|
15
|
+
Rockstar.lastfm = config_hash
|
16
|
+
unless config_hash.values.any? {|v| v==''}
|
17
|
+
@artist = Rockstar::Artist.new artist
|
18
|
+
@root_dir = root_dir
|
19
|
+
@limit = nil
|
20
|
+
super()
|
21
|
+
else
|
22
|
+
puts "Please add your api_key and api_secret to:\n #{config_path}\nGet your keys at http://www.last.fm/api/account".color(:yellow)
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_tracks(tracks, search_type = :file, progressbar = nil)
|
28
|
+
begin
|
29
|
+
raise InvalidSearchType unless SEARCH_TYPES.include?(search_type)
|
30
|
+
rescue InvalidSearchType
|
31
|
+
$logger.error("Invalid Search Type '#{search_type}', use one of #{SEARCH_TYPES.join(',')}")
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
found_tracks = {}
|
35
|
+
search = LastfmM3u::FileSearch.new(root_dir)
|
36
|
+
self.limit ||= tracks.size
|
37
|
+
tracks[0..limit-1].each do |track|
|
38
|
+
track.name = CGI.unescapeHTML(track.name)
|
39
|
+
found_tracks[track.name] = search.find(track.name, {search_type: search_type})
|
40
|
+
progressbar.increment if progressbar
|
41
|
+
end
|
42
|
+
found_tracks
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class InvalidSearchType < Exception; end
|
48
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'mp3info'
|
2
|
+
|
3
|
+
class Mp3InfoExt < Mp3Info
|
4
|
+
attr_reader :query, :query_type
|
5
|
+
|
6
|
+
def initialize(query, query_type, entry)
|
7
|
+
@query = query
|
8
|
+
@query_type = query_type
|
9
|
+
super(entry)
|
10
|
+
end
|
11
|
+
|
12
|
+
def found?
|
13
|
+
return true if @query_type == :track && title_found?
|
14
|
+
return true if @query_type == :album && album_found?
|
15
|
+
return true if @query_type == :artist && artist_found?
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
|
19
|
+
# ARTIST
|
20
|
+
def artist_found?
|
21
|
+
tag2_artist_found? || tag1_artist_found?
|
22
|
+
end
|
23
|
+
|
24
|
+
def tag2_artist_found?
|
25
|
+
hastag2? && tag2.TPE1.downcase == query.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def tag1_artist_found?
|
29
|
+
hastag1? && tag1.artist.downcase == query.downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
# ALBUM
|
33
|
+
def album_found?
|
34
|
+
tag2_album_found? || tag1_album_found?
|
35
|
+
end
|
36
|
+
|
37
|
+
def tag2_album_found?
|
38
|
+
hastag2? && tag2.TALB.downcase == query.downcase
|
39
|
+
end
|
40
|
+
|
41
|
+
def tag1_album_found?
|
42
|
+
hastag1? && tag1.album.downcase == query.downcase
|
43
|
+
end
|
44
|
+
|
45
|
+
# TITLE
|
46
|
+
def title_found?
|
47
|
+
tag2_title_found? || tag1_title_found?
|
48
|
+
end
|
49
|
+
|
50
|
+
def tag2_title_found?
|
51
|
+
hastag2? && tag2.TIT2.downcase == query.downcase
|
52
|
+
end
|
53
|
+
|
54
|
+
def tag1_title_found?
|
55
|
+
hastag1? and tag1.title.downcase == query.downcase
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module LastfmM3u
|
2
|
+
require 'lastfm-m3u'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
class Search < Base
|
6
|
+
end
|
7
|
+
|
8
|
+
class FileSearch < Search
|
9
|
+
attr_accessor :root_dir
|
10
|
+
|
11
|
+
def initialize(root_dir)
|
12
|
+
@root_dir = Pathname root_dir
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
def find(query, options={})
|
17
|
+
query_type = options[:query_type] || :track
|
18
|
+
file_or_id3 = options[:search_type] || :both
|
19
|
+
prefer_flac = options[:prefer_flac] || true
|
20
|
+
|
21
|
+
files = find_unique_files(query, query_type, file_or_id3)
|
22
|
+
if prefer_flac && !(flac_files=trim_non_flac(files)).empty?
|
23
|
+
files = flac_files
|
24
|
+
end
|
25
|
+
$logger.debug "#{query} => #{files}"
|
26
|
+
files
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find_unique_files(query, query_type, file_or_id3)
|
33
|
+
files = id3_files = []
|
34
|
+
files = find_in_filesystem(query, query_type) if [:both, :file].include? file_or_id3
|
35
|
+
id3_files = find_by_id3(query, query_type) if [:both, :id3].include? file_or_id3
|
36
|
+
(files + id3_files).sort.uniq
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_in_filesystem(name, query_type)
|
40
|
+
if query_type == :artist or query_type == :album
|
41
|
+
find_by_dir(name)
|
42
|
+
else
|
43
|
+
find_by_filename(name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_by_dir(name)
|
48
|
+
found_entries = []
|
49
|
+
Dir.glob("#{root_dir}/**/*").each do |entry|
|
50
|
+
entry = Pathname.new entry
|
51
|
+
found_entries << entry.to_s if entry.directory? and normalize(entry).to_s =~ /.*#{Regexp.escape name}.*/i
|
52
|
+
end
|
53
|
+
found_entries
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_by_filename(name)
|
57
|
+
found_entries = []
|
58
|
+
Dir.glob("#{root_dir}/**/*.{mp3,flac}").each do |entry|
|
59
|
+
begin
|
60
|
+
entry = Pathname.new entry
|
61
|
+
if normalize(entry) =~ /.*#{Regexp.escape name}.*/i
|
62
|
+
$logger.debug "#{name} => #{entry}"
|
63
|
+
found_entries << entry.to_s
|
64
|
+
end
|
65
|
+
rescue ArgumentError => e
|
66
|
+
# for some reason it gets 'invalid byte sequence in UTF-8
|
67
|
+
# even though replacing ascii below with utf-8 causes nothing to be replaced.
|
68
|
+
entry = Pathname entry.to_s.encode('ascii', invalid: :replace, undef: :replace, replace: '??')
|
69
|
+
$logger.warn "#{e.message} => #{entry.relative_path_from(root_dir)}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
found_entries
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_by_id3(query, query_type)
|
76
|
+
query = CGI.unescapeHTML(query)
|
77
|
+
found_entries = []
|
78
|
+
$logger.debug query
|
79
|
+
Dir.glob("#{root_dir}/**/*.mp3").each do |entry|
|
80
|
+
begin
|
81
|
+
id3 = Mp3InfoExt.new(query, query_type, entry)
|
82
|
+
found_entries << id3.filename if id3.found?
|
83
|
+
rescue Mp3InfoError => e
|
84
|
+
$logger.warn "#{e.message} => #{(Pathname.new entry).relative_path_from(root_dir)}"
|
85
|
+
rescue NoMethodError => e
|
86
|
+
$logger.warn "No tags found => #{(Pathname.new entry).relative_path_from(root_dir)}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
found_entries
|
90
|
+
end
|
91
|
+
|
92
|
+
def normalize(pathname)
|
93
|
+
pathname.basename.to_s.gsub('-', ' ').gsub('_', ' ').sub(/#{pathname.extname}$/, '')
|
94
|
+
end
|
95
|
+
|
96
|
+
def trim_non_flac(pathnames)
|
97
|
+
pathnames.select{|pn| pn.to_s =~ /\.flac$/}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module LastfmM3u
|
2
|
+
module Util
|
3
|
+
def self.choose_track(track, track_set)
|
4
|
+
choose do |menu|
|
5
|
+
menu.index = :letter
|
6
|
+
menu.index_suffix = ") "
|
7
|
+
|
8
|
+
menu.prompt = "For #{track}, which file would you like to use? ".color(:green)
|
9
|
+
|
10
|
+
track_set.each do |track|
|
11
|
+
menu.choice track do
|
12
|
+
return track
|
13
|
+
end
|
14
|
+
end
|
15
|
+
menu.choice "none" do return nil end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lastfm-m3u
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- - ~>
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 1.5.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: pry-debugger
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.2'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0.2'
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: simplecov
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -258,6 +274,7 @@ files:
|
|
258
274
|
- lib/lastfm-m3u.rb
|
259
275
|
- lib/lastfm-m3u/base.rb
|
260
276
|
- lib/lastfm-m3u/lastfm.rb
|
277
|
+
- lib/lastfm-m3u/mp3_info_ext.rb
|
261
278
|
- lib/lastfm-m3u/search/file_search.rb
|
262
279
|
- lib/lastfm-m3u/util.rb
|
263
280
|
- lib/lastfm-m3u/version.rb
|
@@ -284,7 +301,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
284
301
|
version: '0'
|
285
302
|
segments:
|
286
303
|
- 0
|
287
|
-
hash:
|
304
|
+
hash: 3207334904113421642
|
288
305
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
289
306
|
none: false
|
290
307
|
requirements:
|
@@ -293,7 +310,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
293
310
|
version: '0'
|
294
311
|
segments:
|
295
312
|
- 0
|
296
|
-
hash:
|
313
|
+
hash: 3207334904113421642
|
297
314
|
requirements: []
|
298
315
|
rubyforge_project:
|
299
316
|
rubygems_version: 1.8.25
|