album_credits 0.0.2

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/.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
+