album_credits 0.0.2

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
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ Album Credits
2
+ =============
3
+ ### Just because we listen to music digitally doesn't mean we shouldn't be able to access the album's liner notes.
4
+
5
+ Very much a work in progress but it's fairly functional.
6
+
7
+ Installation:
8
+ -------------
9
+ gem install album_credits
10
+
11
+
12
+ Usage:
13
+ ------
14
+ $ album_credits "tool" "lateralus"
15
+ ===================================================
16
+ Lateralus 2001-05-11
17
+ 13 songs
18
+ http://s.dsimg.com/image/R-1581120-1229978166.jpeg
19
+ The packaging is the same as the EU version.
20
+
21
+ Packaged in a translucent black slipcase over a unique stained jewel case. The jewel case design is off centre from the typical US release.
22
+
23
+ This pressing contains a typo on track 9 on both the Obi and the actual packaging. The correct spelling is the same as the album title: "Lateralus."
24
+
25
+ Recorded at:
26
+ Cello Studios, Hollywood, CA
27
+ The Hook, North Hollywood, CA
28
+ Big Empty Space, North Hollywood, CA
29
+ The Lodge, North Hollywood, CA
30
+
31
+ Mixed at Larrabee Sound North, North Hollywood, CA.
32
+ Mastered by Gateway Mastering Studios, Portland, ME.
33
+
34
+ Engineers:
35
+
36
+ David Bottrill
37
+ AKA: Bottrill, D. Bottril, D.B., Dave Botirill, Dave Bottril, Dave Bottrill, David Botirill, David Botrill, David Bottril, Frameman
38
+ 721 releases in discography
39
+ Abdelli (2 total)
40
+ * New Moon [1995 Real World Records, Virgin]
41
+ Afro Celt Sound System (4 total)
42
+ * Volume 1: Sound Magic [1996 Real World Records]
43
+ * Volume 2: Release [1999 Real World Records, Virgin]
44
+ Baaba Maal (4 total)
45
+ * Firin' In Fouta [1994 Mango, Mango]
46
+ Blackbud (4 total)
47
+ * From The Sky [2006 Independiente]
48
+ * Forever [2006 Independiente]
49
+
50
+ ...
51
+
52
+
53
+ TODO:
54
+ -----
55
+ * Cleanup code. Was a hack turned gem...still needs lots of refactoring, comments and tests.
56
+ * Better identifying if a search result is *actually the album* being queried for.
57
+ * Add sources other than Discogs.com.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "album_credits/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "album_credits"
7
+ s.version = AlbumCredits::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["bassnode"]
10
+ s.email = ["bassnode@gmail.com"]
11
+ s.homepage = "https://github.com/bassnode/album_credits"
12
+ s.summary = %q{Provides album engineering credits}
13
+ s.description = %q{Searches databases for a given artist + album combination and returns recording engineering information.}
14
+
15
+ s.rubyforge_project = "album_credits"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "ruby-debug"
23
+ s.add_dependency "bassnode-discogs"
24
+ end
data/bin/album_credits ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # TEMP
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ require 'ruby-debug'
7
+ require 'album_credits'
8
+
9
+ artist = ARGV[0]
10
+ album = ARGV[1]
11
+ year = ARGV[2]
12
+ ac = AlbumCredits::Finder.new
13
+
14
+ releases = ac.find_releases(artist, album, year)
15
+ raise "No releases" if releases.empty?
16
+ puts "Found #{releases.size} releases"
17
+
18
+ sorted_releases = releases.inject([]) do |rel_array, release|
19
+ unless (engineers = ac.engineers_for_release(release)).nil?
20
+ rel_array << [release, engineers]
21
+ end
22
+ rel_array
23
+ end.sort_by{|arr| arr.last.size}.reverse
24
+
25
+ raise "No engineering data though :/" if sorted_releases.empty?
26
+
27
+ best_guess = sorted_releases.shift
28
+ ac.display_release(best_guess.first, best_guess.last, :color => :green, :show_discography => true)
29
+
30
+ sorted_releases.each do |release, engineers|
31
+ ac.display_release(release, engineers)
32
+ end
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ #require 'ap'
3
+ require 'discogs'
4
+ require 'cgi'
5
+ require 'ruby-debug'
6
+ COLORS = {
7
+ :clear => "\e[0m",
8
+ :bold => "\e[1m",
9
+ :black => "\e[30m",
10
+ :white => "\e[37m",
11
+ :red => "\e[31m",
12
+ :green => "\e[32m",
13
+ :yellow => "\e[33m",
14
+ :blue => "\e[34m"
15
+ }
16
+
17
+ def color_puts(text, color=:white, bold=false)
18
+ embolden = bold ? COLORS[:bold] : ''
19
+ puts "#{embolden}#{COLORS[color]}#{text}#{COLORS[:clear]}"
20
+ end
21
+
22
+ def parse_discogs_id(search_result)
23
+ search_result.uri.split('/').last
24
+ end
25
+
26
+ def discogs
27
+ return @discogs if @discogs
28
+ @discogs = Discogs::Wrapper.new("bff9085fc7")
29
+ end
30
+
31
+ def find_releases(artist, album, year=nil)
32
+ releases = []
33
+ [nil, 'CD', 'HDCD', 'vinyl'].each do |format|
34
+ format = " AND format:#{format}" if format
35
+ query = CGI.escape("#{album} AND artist:#{artist}#{format}")
36
+ possibilities = discogs.search(query, 'releases')
37
+ if possibilities.searchresults.size > 0
38
+ possibilities.searchresults.each do |found_album|
39
+ # puts "trying #{found_album.inspect}"
40
+ release = discogs.get_release(parse_discogs_id(found_album))
41
+ # Make sure the album is actually what we think it is and that it
42
+ # is in an Accepted state (as per Discogs).
43
+ if release.title =~ /#{album}/i && release.status == 'Accepted'
44
+ releases << release
45
+ end
46
+ end
47
+ else
48
+ # puts "no results for #{query}"
49
+ end
50
+ end
51
+
52
+ # Could put this later but still trying to figure out if we want to narrow
53
+ # by year if it removes all potential results.
54
+ releases.reject!{ |r| r.released.to_s.split('-').first.to_s != year } if year
55
+
56
+ # Only return unique releases
57
+ seen = {}
58
+ uniq_releases = releases.inject([]) do |uniq, rel|
59
+ unless seen.has_key?(rel.id)
60
+ seen[rel.id] = 1
61
+ uniq << rel
62
+ end
63
+ uniq
64
+ end
65
+
66
+ # Only return with nil release date filter unless it filters out everything.
67
+ pristine_releases = uniq_releases.reject{|release| release.released.nil?}
68
+ pristine_releases.size < uniq_releases.size ? pristine_releases : uniq_releases
69
+ end
70
+
71
+ def engineers_for_release(release)
72
+ if release.extraartists && !(engineers = release.extraartists.select{|a| a.role =~ /mix|master|engineer/i}).empty?
73
+ return engineers
74
+ end
75
+ end
76
+
77
+ def discography_for_artist(artist)
78
+ debugger
79
+ discogs.get_artist(CGI.escape(artist)) rescue []
80
+ end
81
+
82
+ def image_uri_for_release(release)
83
+ return if release.images.nil?
84
+ img = release.images.detect{|i| i.type == 'primary'}
85
+ img = release.images.detect{|i| i.type == 'secondary'} if img.nil?
86
+ img.uri if img
87
+ end
88
+
89
+ def display_release(release, engineers, options={})
90
+ color = options.delete(:color) || :white
91
+ show_discography = options[:show_discography] == true
92
+
93
+ color_puts "="*40, color
94
+ color_puts "#{release.title} #{release.released} ID: #{release.id}", color
95
+ color_puts "#{release.tracklist.size} songs", color
96
+ color_puts image_uri_for_release(release), color
97
+ color_puts release.notes, color
98
+ color_puts "Engineers:", color
99
+ engineers.each do |engineer|
100
+ color_puts "#{engineer.role} #{engineer.name}", color, true
101
+ # Print the first 100 releases in the engineer's discography
102
+ if show_discography && !(artist = discogs.get_artist(CGI.escape(engineer.name))).nil?
103
+ color_puts "#{artist.aliases} #{artist.aliases} #{artist.namevariations}", color
104
+ color_puts "#{artist.releases.size} releases in discography", color
105
+ artist.releases.slice(0,99).each do |disk|
106
+ color_puts "\t* #{disk.year} #{disk.title} [#{disk.label}]", color
107
+ end
108
+ # IDEA: show a cross-section of their work.
109
+ # maybe start with around the year that current album was released if there are many.
110
+ # ALSO: could filter their discog. output by x-ref w/ the role they
111
+ # played on this album. e.g. only show Bob Ludwig's mastering work, not
112
+ # mixing. Could be ugly b/c I'll have to pull down every release
113
+ # individually.
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+ artist = ARGV[0]
120
+ album = ARGV[1]
121
+ year = ARGV[2]
122
+ releases = find_releases(artist, album, year)
123
+ raise "No releases" if releases.empty?
124
+ puts "Found #{releases.size} releases"
125
+
126
+ sorted_releases = releases.inject([]) do |rel_array, release|
127
+ unless (engineers = engineers_for_release(release)).nil?
128
+ rel_array << [release, engineers]
129
+ end
130
+ rel_array
131
+ end.sort_by{|arr| arr.last.size}.reverse
132
+
133
+ raise "No engineering data though :/" if sorted_releases.empty?
134
+
135
+ best_guess = sorted_releases.shift
136
+ display_release(best_guess.first, best_guess.last, :color => :green, :show_discography => true)
137
+
138
+ sorted_releases.each do |release, engineers|
139
+ display_release(release, engineers)
140
+ end
141
+
142
+
@@ -0,0 +1,8 @@
1
+ module Enumerable
2
+ def group_by
3
+ inject({}) do |grouped, element|
4
+ (grouped[yield(element)] ||= []) << element
5
+ grouped
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,82 @@
1
+ module AlbumCredits
2
+
3
+ module Display
4
+
5
+ COLORS = {
6
+ :clear => "\e[0m",
7
+ :bold => "\e[1m",
8
+ :black => "\e[30m",
9
+ :white => "\e[37m",
10
+ :red => "\e[31m",
11
+ :green => "\e[32m",
12
+ :yellow => "\e[33m",
13
+ :blue => "\e[34m"
14
+ }
15
+
16
+ @default_color = :white
17
+ attr_accessor :default_color
18
+
19
+ def cp(text, opts={})
20
+ embolden = opts[:bold] ? COLORS[:bold] : ''
21
+ color = opts[:color] || @default_color
22
+ puts "#{embolden}#{COLORS[color]}#{text}#{COLORS[:clear]}"
23
+ end
24
+
25
+ def image_uri_for_release(release)
26
+ return if release.images.nil?
27
+ img = release.images.detect{|i| i.type == 'primary'}
28
+ img = release.images.detect{|i| i.type == 'secondary'} if img.nil?
29
+ img.uri if img
30
+ end
31
+
32
+ def display_release(release, engineers, opts={})
33
+ @default_color = opts.delete(:color) || :white
34
+
35
+ cp "="*40
36
+ cp "#{release.title} #{release.released} ID: #{release.id}"
37
+ cp "#{release.tracklist.size} songs"
38
+ cp image_uri_for_release(release)
39
+ cp release.notes
40
+
41
+ display_engineer_data(engineers, opts)
42
+ end
43
+
44
+ # TODO: Put logical engineer sorting
45
+ # IDEA: show a cross-section of their work.
46
+ # maybe start with around the year that current album was released if there are many.
47
+ # ALSO: could filter their discog. output by x-ref w/ the role they
48
+ # played on this album. e.g. only show Bob Ludwig's mastering work, not mixing.
49
+ def display_engineer_data(engineers, opts={})
50
+ show_discography = opts[:show_discography] == true
51
+ displayed = []
52
+
53
+ cp "Engineers:", :color => :yellow
54
+ engineers.each do |engineer|
55
+ next if displayed.include? engineer.name
56
+ cp "#{engineer.role} #{engineer.name}", :bold => true
57
+
58
+ # Print the engineer's discography
59
+ if show_discography && !(artist = discogs.get_artist(CGI.escape(engineer.name))).nil?
60
+ aka = artist.aliases || []
61
+ aka << artist.namevariations || []
62
+ cp "AKA: #{aka.flatten.uniq.sort.join(', ')}"
63
+ cp "#{artist.releases.size} releases in discography"
64
+ # Don't show discog for assistants
65
+ unless engineer.role =~ /assisted|assistant|additional/i
66
+ artist.releases.group_by{ |disk| disk.artist }.sort_by{ |artist, albums| artist }.each do |artist, albums|
67
+ cp artist.to_s + " (#{albums.size} total)", :bold => true, :color => :blue
68
+ # Print the oldest version of this album
69
+ albums.group_by{ |a| a.title }.each do |title, albums|
70
+ artist_release = albums.sort_by{ |album| album.year.to_i }.first
71
+ cp "\t* #{artist_release.title} [#{artist_release.year} #{artist_release.label}]"
72
+ end
73
+ end
74
+ end
75
+
76
+ displayed << engineer.name
77
+ puts
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,73 @@
1
+ module AlbumCredits
2
+
3
+ class Finder
4
+ include Display
5
+
6
+ attr_reader :discogs
7
+
8
+ def initialize(api_key="bff9085fc7")
9
+ @discogs = Discogs::Wrapper.new(api_key)
10
+ end
11
+
12
+ def parse_discogs_id(search_result)
13
+ search_result.uri.split('/').last
14
+ end
15
+
16
+ def find_releases(artist, album, year=nil)
17
+ releases = []
18
+ [nil, 'CD', 'HDCD', 'vinyl'].each do |format|
19
+ format = " AND format:#{format}" if format
20
+ query = CGI.escape("#{album} AND artist:#{artist}#{format}")
21
+ begin
22
+ possibilities = discogs.search(query, :type => 'releases')
23
+ rescue Discogs::UnknownResource => e
24
+ puts "Not found: #{e}"
25
+ next
26
+ end
27
+ if possibilities.searchresults.size > 0
28
+ possibilities.searchresults.each do |found_album|
29
+ # puts "trying #{found_album.inspect}"
30
+ release = discogs.get_release(parse_discogs_id(found_album))
31
+ # Make sure the album is actually what we think it is and that it
32
+ # is in an Accepted state (as per Discogs).
33
+ if release.title =~ /#{album}/i && release.status == 'Accepted'
34
+ releases << release
35
+ end
36
+ end
37
+ else
38
+ # puts "no results for #{query}"
39
+ end
40
+ end
41
+
42
+ # Could put this later but still trying to figure out if we want to narrow
43
+ # by year if it removes all potential results.
44
+ releases.reject!{ |r| r.released.to_s.split('-').first.to_s != year } if year
45
+
46
+ # Sometimes Discogs returns duplicate releases so
47
+ # filter out any duplicates based on id.
48
+ seen = {}
49
+ uniq_releases = releases.inject([]) do |uniq, rel|
50
+ unless seen.has_key?(rel.id)
51
+ seen[rel.id] = 1
52
+ uniq << rel
53
+ end
54
+ uniq
55
+ end
56
+
57
+ # Only return with nil release date filter unless it filters out everything.
58
+ pristine_releases = uniq_releases.reject{|release| release.released.nil?}
59
+ pristine_releases.size < uniq_releases.size ? pristine_releases : uniq_releases
60
+ end
61
+
62
+ def engineers_for_release(release)
63
+ if release.extraartists && !(engineers = release.extraartists.select{|a| a.role =~ /mix|master|engineer/i}).empty?
64
+ return engineers
65
+ end
66
+ end
67
+
68
+ def discography_for_artist(artist)
69
+ discogs.get_artist(CGI.escape(artist)) rescue []
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module AlbumCredits
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'cgi'
3
+ require 'discogs'
4
+ require 'album_credits/core_ext'
5
+ require 'album_credits/display'
6
+ require 'album_credits/finder'
7
+
8
+ module AlbumCredits
9
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: album_credits
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - bassnode
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-12 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: ruby-debug
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: bassnode-discogs
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: Searches databases for a given artist + album combination and returns recording engineering information.
50
+ email:
51
+ - bassnode@gmail.com
52
+ executables:
53
+ - album_credits
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - README.md
62
+ - Rakefile
63
+ - album_credits.gemspec
64
+ - bin/album_credits
65
+ - engineering_data.rb
66
+ - lib/album_credits.rb
67
+ - lib/album_credits/core_ext.rb
68
+ - lib/album_credits/display.rb
69
+ - lib/album_credits/finder.rb
70
+ - lib/album_credits/version.rb
71
+ has_rdoc: true
72
+ homepage: https://github.com/bassnode/album_credits
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: album_credits
101
+ rubygems_version: 1.3.7
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Provides album engineering credits
105
+ test_files: []
106
+