voluntary_music_metadata_enrichment 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +15 -0
  2. data/app/assets/javascripts/voluntary_music_metadata_enrichment/groups/show.js.coffee +2 -1
  3. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/index.js.coffee +3 -2
  4. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/year_in_review_releases/index_view.js.coffee +94 -15
  5. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/year_in_review_releases/new_view.js.coffee +37 -2
  6. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/year_in_review_tracks/index_view.js.coffee +94 -15
  7. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/year_in_review_tracks/new_view.js.coffee +37 -2
  8. data/app/assets/javascripts/voluntary_music_metadata_enrichment/library/years_in_review/index_view.js.coffee +6 -0
  9. data/app/assets/javascripts/voluntary_music_metadata_enrichment/videos/show.js.coffee +2 -0
  10. data/app/controllers/concerns/music_metadata_enrichment/artist_confirmation.rb +98 -132
  11. data/app/controllers/concerns/music_metadata_enrichment/track_confirmation.rb +45 -24
  12. data/app/controllers/library/music/year_in_review_release_flops_controller.rb +31 -0
  13. data/app/controllers/library/music/year_in_review_releases_controller.rb +24 -4
  14. data/app/controllers/library/music/year_in_review_track_flops_controller.rb +31 -0
  15. data/app/controllers/library/music/year_in_review_tracks_controller.rb +55 -24
  16. data/app/controllers/library/music/years_in_review_controller.rb +25 -3
  17. data/app/controllers/music_metadata_enrichment/application_controller.rb +4 -0
  18. data/app/controllers/music_metadata_enrichment/artists_controller.rb +14 -12
  19. data/app/controllers/music_metadata_enrichment/group_memberships_controller.rb +20 -0
  20. data/app/controllers/music_metadata_enrichment/group_year_in_review_releases_controller.rb +29 -0
  21. data/app/controllers/music_metadata_enrichment/group_year_in_review_tracks_controller.rb +27 -0
  22. data/app/controllers/music_metadata_enrichment/group_year_in_reviews_controller.rb +18 -0
  23. data/app/controllers/music_metadata_enrichment/groups_controller.rb +2 -0
  24. data/app/controllers/music_metadata_enrichment/releases_controller.rb +59 -57
  25. data/app/controllers/music_metadata_enrichment/tracks_controller.rb +37 -7
  26. data/app/controllers/music_metadata_enrichment/videos_controller.rb +4 -4
  27. data/app/controllers/voluntary/api/v1/music/releases_controller.rb +17 -0
  28. data/app/controllers/voluntary/api/v1/music/tracks_controller.rb +17 -0
  29. data/app/controllers/voluntary/api/v1/music/videos_controller.rb +31 -0
  30. data/app/controllers/voluntary/api/v1/music/year_in_reviews_controller.rb +94 -0
  31. data/app/helpers/library/music/year_in_review_releases_helper.rb +14 -0
  32. data/app/helpers/library/music/year_in_review_tracks_helper.rb +13 -0
  33. data/app/helpers/library/music/year_in_reviews_helper.rb +22 -0
  34. data/app/helpers/music_metadata_enrichment/lastfm_helper.rb +19 -0
  35. data/app/models/concerns/lastfm_request.rb +54 -0
  36. data/app/models/concerns/music_metadata_enrichment/group_year_in_review_music_entry.rb +14 -0
  37. data/app/models/concerns/year_in_review_music_entry.rb +3 -1
  38. data/app/models/concerns/year_in_review_music_release_base.rb +35 -0
  39. data/app/models/concerns/year_in_review_music_track_base.rb +37 -0
  40. data/app/models/music_artist.rb +106 -27
  41. data/app/models/music_metadata_enrichment/group.rb +14 -13
  42. data/app/models/music_metadata_enrichment/group_membership.rb +13 -0
  43. data/app/models/music_metadata_enrichment/group_year_in_review.rb +72 -0
  44. data/app/models/music_metadata_enrichment/group_year_in_review_release.rb +8 -0
  45. data/app/models/music_metadata_enrichment/group_year_in_review_track.rb +8 -0
  46. data/app/models/music_release.rb +224 -35
  47. data/app/models/music_track.rb +140 -28
  48. data/app/models/year_in_review_music.rb +193 -101
  49. data/app/models/year_in_review_music_release.rb +2 -20
  50. data/app/models/year_in_review_music_release_flop.rb +4 -0
  51. data/app/models/year_in_review_music_track.rb +2 -24
  52. data/app/models/year_in_review_music_track_flop.rb +4 -0
  53. data/app/views/library/music/index.html.erb +2 -2
  54. data/app/views/library/music/year_in_review_release_flops/create.js.erb +5 -0
  55. data/app/views/library/music/year_in_review_releases/_collection.html.erb +55 -41
  56. data/app/views/library/music/year_in_review_releases/_form.html.erb +2 -1
  57. data/app/views/library/music/year_in_review_releases/_multiple_form.html.erb +20 -6
  58. data/app/views/library/music/year_in_review_releases/_year_in_review_release.html.erb +35 -0
  59. data/app/views/library/music/year_in_review_releases/create.js.erb +9 -4
  60. data/app/views/library/music/year_in_review_releases/destroy.js.erb +2 -2
  61. data/app/views/library/music/year_in_review_releases/export.html.erb +2 -1
  62. data/app/views/library/music/year_in_review_releases/index.html.erb +4 -4
  63. data/app/views/library/music/year_in_review_track_flops/create.js.erb +5 -0
  64. data/app/views/library/music/year_in_review_tracks/_collection.html.erb +49 -38
  65. data/app/views/library/music/year_in_review_tracks/_form.html.erb +2 -1
  66. data/app/views/library/music/year_in_review_tracks/_multiple_form.html.erb +69 -42
  67. data/app/views/library/music/year_in_review_tracks/_year_in_review_track.html.erb +29 -0
  68. data/app/views/library/music/year_in_review_tracks/create.js.erb +9 -4
  69. data/app/views/library/music/year_in_review_tracks/destroy.js.erb +2 -2
  70. data/app/views/library/music/year_in_review_tracks/export.html.erb +1 -1
  71. data/app/views/library/music/year_in_review_tracks/index.html.erb +3 -3
  72. data/app/views/library/music/year_in_review_tracks/multiple_new.js.erb +5 -2
  73. data/app/views/library/music/years_in_review/_collection.html.erb +23 -6
  74. data/app/views/library/music/years_in_review/destroy.js.erb +1 -0
  75. data/app/views/library/music/years_in_review/index.html.erb +2 -2
  76. data/app/views/library/music/years_in_review/publish.js.erb +1 -0
  77. data/app/views/library/music/years_in_review/show.html.erb +3 -3
  78. data/app/views/music_metadata_enrichment/artists/_collection.html.erb +3 -3
  79. data/app/views/music_metadata_enrichment/group_artist_connections/import.html.erb +1 -1
  80. data/app/views/music_metadata_enrichment/group_memberships/_create_or_destroy.html.erb +9 -0
  81. data/app/views/music_metadata_enrichment/group_memberships/create.js.erb +5 -0
  82. data/app/views/music_metadata_enrichment/group_memberships/destroy.js.erb +5 -0
  83. data/app/views/music_metadata_enrichment/group_year_in_review_releases/index.html.erb +16 -0
  84. data/app/views/music_metadata_enrichment/group_year_in_review_tracks/index.html.erb +16 -0
  85. data/app/views/music_metadata_enrichment/group_year_in_reviews/index.html.erb +18 -0
  86. data/app/views/music_metadata_enrichment/group_year_in_reviews/show.html.erb +20 -0
  87. data/app/views/music_metadata_enrichment/groups/show.html.erb +7 -1
  88. data/app/views/music_metadata_enrichment/releases/_spotify_album_player.html.erb +3 -0
  89. data/app/views/music_metadata_enrichment/releases/announce.html.erb +3 -1
  90. data/app/views/music_metadata_enrichment/releases/by_name.html.erb +1 -1
  91. data/app/views/music_metadata_enrichment/releases/name.html.erb +2 -1
  92. data/app/views/music_metadata_enrichment/releases/name_confirmation.html.erb +2 -1
  93. data/app/views/music_metadata_enrichment/releases/show.html.erb +24 -0
  94. data/app/views/music_metadata_enrichment/shared/_artist_confirmation_form.html.erb +2 -1
  95. data/app/views/music_metadata_enrichment/shared/_new_artist_form.html.erb +2 -1
  96. data/app/views/music_metadata_enrichment/shared/_new_track_form.html.erb +2 -1
  97. data/app/views/music_metadata_enrichment/shared/_track_confirmation_form.html.erb +3 -2
  98. data/app/views/music_metadata_enrichment/tracks/show.html.erb +20 -2
  99. data/app/views/music_metadata_enrichment/videos/artist_confirmation.html.erb +1 -1
  100. data/app/views/music_metadata_enrichment/videos/show.html.erb +7 -1
  101. data/config/locales/resources/music_metadata_enrichment_group_memberships/en.yml +7 -0
  102. data/config/locales/resources/music_releases/en.yml +4 -0
  103. data/config/locales/resources/music_tracks/en.yml +8 -3
  104. data/config/locales/resources/year_in_review_music_release_flops/en.yml +4 -0
  105. data/config/locales/resources/year_in_review_music_releases/en.yml +6 -1
  106. data/config/locales/resources/year_in_review_music_track_flops/en.yml +4 -0
  107. data/config/locales/resources/year_in_review_music_tracks/en.yml +4 -0
  108. data/config/locales/resources/years_in_review_music/en.yml +8 -3
  109. data/config/routes.rb +30 -2
  110. data/config/routes/api.rb +32 -0
  111. data/db/migrate/20150106183434_add_product_music_metadata_enrichment.rb +3 -1
  112. data/db/migrate/20150120091801_create_music_metadata_enrichment_groups.rb +2 -2
  113. data/db/migrate/20150208120722_add_is_lp_to_music_releases.rb +5 -0
  114. data/db/migrate/20150209091856_year_in_review_music_releases_and_tracks_flops.rb +34 -0
  115. data/db/migrate/20150209151056_add_is_ambiguous_to_music_artists.rb +5 -0
  116. data/db/migrate/20150213170604_create_group_memberships.rb +11 -0
  117. data/db/migrate/20150214083714_create_group_year_in_review_music_releases_and_tracks.rb +77 -0
  118. data/db/migrate/20150215120545_add_matches_to_year_in_reviews_music.rb +6 -0
  119. data/db/migrate/20150215135051_add_spotify_album_id_to_music_releases.rb +8 -0
  120. data/db/migrate/20150220084746_add_state_to_year_in_review_music_entries.rb +7 -0
  121. data/lib/voluntary_music_metadata_enrichment/ability.rb +12 -3
  122. data/lib/voluntary_music_metadata_enrichment/concerns/model/user/has_music_library.rb +69 -34
  123. data/lib/voluntary_music_metadata_enrichment/version.rb +1 -1
  124. metadata +63 -64
  125. data/MIT-LICENSE +0 -20
  126. data/README.rdoc +0 -3
@@ -0,0 +1,17 @@
1
+ module Voluntary
2
+ module Api
3
+ module V1
4
+ module Music
5
+ class TracksController < Voluntary::Api::V1::BaseController
6
+ respond_to :json
7
+
8
+ def bulk
9
+ respond_to do |format|
10
+ format.json { render json: MusicTrack.enrich_metadata(params[:tracks]).to_json }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Voluntary
2
+ module Api
3
+ module V1
4
+ module Music
5
+ class VideosController < Voluntary::Api::V1::BaseController
6
+ respond_to :json
7
+
8
+ def index
9
+ params[:page] = nil if params[:page] == ''
10
+ params[:per_page] = nil if params[:per_page] == ''
11
+ videos = MusicVideo.liked_by(params[:user_id]).order('likes.created_at DESC').paginate(per_page: params[:per_page] || 10, page: params[:page] || 1)
12
+
13
+ respond_to do |f|
14
+ f.json {
15
+ render json: {
16
+ current_page: videos.current_page, per_page: videos.per_page, total_entries: videos.total_entries, total_pages: videos.total_pages,
17
+ entries: videos.map do |v|
18
+ {
19
+ id: v.id, status: v.status, artist_id: v.artist_id, artist_name: v.artist_name,
20
+ track_id: v.track_id, track_name: v.track_name, url: v.url, liked_at: v.liked_at.iso8601
21
+ }
22
+ end,
23
+ }.to_json
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,94 @@
1
+ module Voluntary
2
+ module Api
3
+ module V1
4
+ module Music
5
+ class YearInReviewsController < Voluntary::Api::V1::BaseController
6
+ before_filter :find_user
7
+
8
+ respond_to :json
9
+
10
+ def index
11
+ years_in_review = @user.years_in_review_music.published
12
+ params[:page] = nil if params[:page] == ''
13
+ params[:per_page] = nil if params[:per_page] == ''
14
+ years_in_review = years_in_review.order('year DESC').paginate(per_page: params[:per_page] || 10, page: params[:page] || 1)
15
+
16
+ respond_to do |f|
17
+ f.json {
18
+ render json: {
19
+ current_page: years_in_review.current_page, per_page: years_in_review.per_page, total_entries: years_in_review.total_entries,
20
+ total_pages: years_in_review.total_pages,
21
+ entries: years_in_review.map{|r| { year: r.year } },
22
+ }.to_json
23
+ }
24
+ end
25
+ end
26
+
27
+ def show
28
+ respond_to {|f| f.json { render json: { year: year_in_review.year }.to_json } }
29
+ end
30
+
31
+ def top_releases
32
+ releases = year_in_review.releases.order('position ASC')
33
+ tracks = year_in_review.tracks.order('position ASC').group_by(&:release_id)
34
+ tracks_count = tracks.values.flatten.length
35
+
36
+ respond_to do |f|
37
+ f.json {
38
+ render json: releases.map {|year_in_review_release|
39
+ {
40
+ position: year_in_review_release.position,
41
+ artist_name: year_in_review_release.artist_name,
42
+ artist_id: year_in_review_release.artist_id,
43
+ release_id: year_in_review_release.release_id,
44
+ release_name: year_in_review_release.release_name,
45
+ top_track_positions_sum: (
46
+ (tracks[year_in_review_release.release_id] || []).sum{|t| tracks_count - t.position + 1 }
47
+ ),
48
+ top_tracks: (tracks[year_in_review_release.release_id] || []).map do |t|
49
+ { position: t.position, track_name: t.track_name, track_id: t.track_id }
50
+ end
51
+ }
52
+ }.to_json
53
+ }
54
+ end
55
+ end
56
+
57
+ def top_tracks
58
+ tracks = year_in_review.tracks.order('position ASC')
59
+
60
+ respond_to do |f|
61
+ f.json {
62
+ render json: tracks.map {|year_in_review_track|
63
+ {
64
+ position: year_in_review_track.position,
65
+ artist_name: year_in_review_track.artist_name,
66
+ artist_id: year_in_review_track.artist_id,
67
+ release_id: year_in_review_track.release_id,
68
+ release_name: year_in_review_track.release_name,
69
+ track_id: year_in_review_track.track_id,
70
+ track_name: year_in_review_track.track_name
71
+ }
72
+ }.to_json
73
+ }
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def find_user
80
+ @user = User.find(params[:user_id])
81
+ end
82
+
83
+ def year_in_review
84
+ @year_in_review ||= @user.years_in_review_music.published.where(year: params[:id]).first
85
+
86
+ raise ActiveRecord::RecordNotFound if @year_in_review.nil?
87
+
88
+ @year_in_review
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,14 @@
1
+ module Library
2
+ module Music
3
+ module YearInReviewReleasesHelper
4
+ def year_in_review_release_top_track_lastfm_links(year_in_review_tracks)
5
+ if year_in_review_tracks.any?
6
+ "(#{year_in_review_top_track_positions_sum(year_in_review_tracks)}: " +
7
+ year_in_review_tracks.map{|t| "#{t.position}. #{lastfm_track_text(t, short: true)}" }.join(', ') + ')'
8
+ else
9
+ ''
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Library
2
+ module Music
3
+ module YearInReviewTracksHelper
4
+ def year_in_review_track_links(year_in_review_tracks)
5
+ year_in_review_tracks.map{|t| "#{t.position}. " + link_to(t.track_name, music_track_path(t.track_id)) }.join(', ')
6
+ end
7
+
8
+ def year_in_review_top_track_positions_sum(year_in_review_tracks)
9
+ year_in_review_tracks.sum{|t| @year_in_review_tracks_count - t.position + 1 }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module Library
2
+ module Music
3
+ module YearInReviewsHelper
4
+ def group_or_user_path(path, param = nil)
5
+ prefix = @group.present? ? 'group' : 'user'
6
+ full_path = "#{prefix}_#{path}_path"
7
+
8
+ case path
9
+ when 'music_years_in_review'
10
+ send(full_path, @group || @user)
11
+ when 'export_music_year_in_review_top_tracks', 'export_music_year_in_review_top_releases'
12
+ full_path = full_path.gsub("#{prefix}_", '') if @group.blank?
13
+
14
+ @group.blank? ? send(full_path, param) : send(full_path, @group, param)
15
+ else
16
+ id = @group.present? ? param.group_id : param.user_id
17
+ send(full_path, id, param.year)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module MusicMetadataEnrichment
3
+ module LastfmHelper
4
+ def lastfm_track_text(track, options = {})
5
+ short = options.has_key?(:short) ? options[:short] : false
6
+
7
+ artist, name = if track.is_a? MusicTrack
8
+ [track.artist_name, track.name]
9
+ elsif track.is_a?(YearInReviewMusicTrack) || track.is_a?(MusicMetadataEnrichment::GroupYearInReviewTrack)
10
+ [track.artist_name, track.track_name]
11
+ end
12
+
13
+ text = []
14
+ text << "[artist]#{artist}[/artist]" unless short
15
+ text << "[track artist=#{artist}]#{name}[/track]"
16
+ text.join(' – ')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ module LastfmRequest
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ def lastfm_request(lastfm, resource, method, error_message_reqexp, params = {})
7
+ self.class.lastfm_request_class_method(lastfm, resource, method, error_message_reqexp, params)
8
+ end
9
+
10
+ module ClassMethods
11
+ def lastfm_request_class_method(lastfm, resource, method, error_message_reqexp, params = {})
12
+ response = nil
13
+ raise_if_response_is_just_nil = params.delete(:raise_if_response_is_just_nil)
14
+ raise_parse_exception = params.delete(:raise_parse_exception)
15
+
16
+ i = 1
17
+
18
+ 3.times do
19
+ begin
20
+ response = lastfm.send(resource).send(method, params)
21
+
22
+ raise 'last.fm response is just nil without exceptions' if response.nil? && raise_if_response_is_just_nil
23
+
24
+ break
25
+ rescue Timeout::Error, EOFError => e
26
+ puts "lastfm: #{e.class.name} for ##{i} time"
27
+ sleep 60
28
+ rescue ArgumentError => e
29
+ if e.message.match('File does not exist')
30
+ puts "lastfm: 'File does not exist' sleep 60 seconds for ##{i} time ..."
31
+ sleep 60
32
+ else
33
+ raise e
34
+ end
35
+ rescue Lastfm::ApiError => e
36
+ if error_message_reqexp.nil? || e.message.match(error_message_reqexp).nil?
37
+ if e.message.match(/Operation failed - Something else went wrong/)
38
+ puts "lastfm: operation failed - something else went wrong for ##{i} time ..."
39
+ sleep 60
40
+ else
41
+ raise e
42
+ end
43
+ end
44
+ rescue REXML::ParseException => e
45
+ raise e if raise_parse_exception
46
+ end
47
+
48
+ i += 1
49
+ end
50
+
51
+ response
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,14 @@
1
+ module MusicMetadataEnrichment
2
+ module GroupYearInReviewMusicEntry
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ belongs_to :year_in_review_music, class_name: 'MusicMetadataEnrichment::GroupYearInReviewMusic'
7
+ belongs_to :group, class_name: 'MusicMetadataEnrichment::Group'
8
+
9
+ validates :year_in_review_music_id, presence: true
10
+
11
+ attr_accessible :year_in_review_music_id, :group_id, :year, :position, :score
12
+ end
13
+ end
14
+ end
@@ -5,8 +5,10 @@ module YearInReviewMusicEntry
5
5
  belongs_to :year_in_review_music
6
6
  belongs_to :user
7
7
 
8
+ scope :published, -> { where(state: 'published') }
9
+
8
10
  validates :year_in_review_music_id, presence: true
9
11
 
10
- acts_as_list scope: :year_in_review_music_id
12
+ attr_accessible :year_in_review_music_id, :user_id, :year
11
13
  end
12
14
  end
@@ -0,0 +1,35 @@
1
+ module YearInReviewMusicReleaseBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :artist, class_name: 'MusicArtist'
6
+ belongs_to :release, class_name: 'MusicRelease'
7
+
8
+ validates :release_id, presence: true, uniqueness: { scope: :year_in_review_music_id }
9
+ validates :spotify_album_id, length: { is: 22 }, allow_blank: true
10
+
11
+ attr_accessible :artist_id, :artist_name, :release_id, :release_name, :spotify_album_id
12
+
13
+ before_create :set_cache_columns
14
+
15
+ private
16
+
17
+ def set_cache_columns
18
+ unless !respond_to?(:user_id) || user_id.present?
19
+ self.user_id = year_in_review_music.user_id
20
+ end
21
+
22
+ unless !respond_to?(:group_id) || group_id.present?
23
+ self.group_id = year_in_review_music.group_id
24
+ end
25
+
26
+ self.year = year_in_review_music.year unless year.present?
27
+ self.artist_id = release.artist_id unless artist_id.present?
28
+ self.artist_id = release.artist_id unless artist_id.present?
29
+ self.artist_name = release.artist_name unless artist_name.present?
30
+ self.release_name = release.name unless release_name.present?
31
+ self.spotify_album_id = release.spotify_album_id unless spotify_album_id.present?
32
+ self.released_at = release.released_at unless released_at.present?
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ module YearInReviewMusicTrackBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :artist, class_name: 'MusicArtist'
6
+ belongs_to :release, class_name: 'MusicRelease'
7
+ belongs_to :track, class_name: 'MusicTrack'
8
+
9
+ validates :track_id, presence: true, uniqueness: { scope: :year_in_review_music_id }
10
+ validates :spotify_track_id, length: { is: 22 }, allow_blank: true
11
+
12
+ attr_accessible :artist_id, :artist_name, :release_id, :release_name, :track_id, :spotify_track_id, :track_name
13
+
14
+ before_save :set_cache_columns
15
+
16
+ private
17
+
18
+ def set_cache_columns
19
+ unless !respond_to?(:user_id) || user_id.present?
20
+ self.user_id = year_in_review_music.user_id
21
+ end
22
+
23
+ unless !respond_to?(:group_id) || group_id.present?
24
+ self.group_id = year_in_review_music.group_id
25
+ end
26
+
27
+ self.year = year_in_review_music.year unless year.present?
28
+ self.artist_id = track.artist_id unless artist_id.present?
29
+ self.artist_name = track.artist_name unless artist_name.present? && !track_id_changed?
30
+ self.release_id = track.release_id unless release_id.present? && !track_id_changed?
31
+ self.release_name = track.release_name unless release_name.present? && !track_id_changed?
32
+ self.track_name = track.name unless track_name.present? && !track_id_changed?
33
+ self.spotify_track_id = track.spotify_track_id unless spotify_track_id.present?
34
+ self.released_at = track.released_at unless released_at.present?
35
+ end
36
+ end
37
+ end
@@ -1,8 +1,11 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class MusicArtist < ActiveRecord::Base
3
+ include LastfmRequest
4
+
3
5
  has_many :releases, class_name: 'MusicRelease', foreign_key: 'artist_id', dependent: :destroy
4
6
  has_many :tracks, class_name: 'MusicTrack', foreign_key: 'artist_id'
5
7
  has_many :videos, class_name: 'MusicVideo', foreign_key: 'artist_id'
8
+ has_many :music_library_artists, dependent: :destroy, foreign_key: 'artist_id'
6
9
 
7
10
  scope :name_like, ->(name) do
8
11
  where(MusicArtist.arel_table[:name].matches("%#{name}%"))
@@ -14,49 +17,125 @@ class MusicArtist < ActiveRecord::Base
14
17
  after_update :synchronize_artist_name
15
18
  after_create :create_bonustracks_release
16
19
 
17
- attr_accessible :name, :mbid, :disambiguation, :founded_at, :dissolved_at, :listeners, :plays
20
+ attr_accessible :name, :is_ambiguous, :mbid, :disambiguation, :founded_at, :dissolved_at, :listeners, :plays
18
21
 
19
22
  state_machine :state, initial: :without_metadata do
20
23
  event :import_metadata do transition :without_metadata => :active; end
21
24
 
22
25
  before_transition :without_metadata => :active do |artist, transition|
23
26
  musicbrainz_artist = MusicBrainz::Artist.find(artist.mbid)
24
-
27
+ artist.update_attribute(:mbid, musicbrainz_artist.id)
28
+
29
+ is_ambiguous = if artist.is_ambiguous.nil?
30
+ MusicBrainz::Artist.search(artist.name).select{|a| a[:name].downcase == artist.name.downcase}.length > 1
31
+ else
32
+ artist.is_ambiguous
33
+ end
34
+
25
35
  artist.update_attributes(
26
36
  disambiguation: musicbrainz_artist.disambiguation,
27
37
  founded_at: artist.musicbrainz_date_to_iso_date(musicbrainz_artist.begin),
28
- dissolved_at: artist.musicbrainz_date_to_iso_date(musicbrainz_artist.end)
38
+ dissolved_at: artist.musicbrainz_date_to_iso_date(musicbrainz_artist.end),
39
+ is_ambiguous: is_ambiguous
29
40
  )
30
41
 
31
- lastfm = Lastfm.new(LastfmApiKey, LastfmApiSecret)
32
- lastfm_artist = lastfm.artist.get_info(mbid: artist.mbid)
33
- artist.update_attributes(listeners: lastfm_artist['stats']['listeners'], plays: lastfm_artist['stats']['playcount'])
42
+ unless artist.listeners.present? && artist.plays.present?
43
+ lastfm = Lastfm.new(LastfmApiKey, LastfmApiSecret)
44
+ lastfm_artist = artist.lastfm_request(lastfm, :artist, :get_info, 'The artist you supplied could not be found', artist: artist.name)
45
+ artist.update_attributes(listeners: lastfm_artist['stats']['listeners'], plays: lastfm_artist['stats']['playcount']) unless lastfm_artist.nil?
46
+ end
47
+
48
+ unless artist.is_classical?(lastfm)
49
+ artist.import_releases(musicbrainz_artist)
50
+ #artist.import_bonus_tracks
51
+ #artist.import_music_videos_from_tapetv
52
+ end
53
+ end
54
+ end
55
+
56
+ def is_classical?(lastfm = nil)
57
+ lastfm ||= Lastfm.new(LastfmApiKey, LastfmApiSecret)
58
+
59
+ begin
60
+ lastfm_artist_tags = lastfm_request(
61
+ lastfm, :artist, :get_top_tags, 'The artist you supplied could not be found', artist: name, raise_if_response_is_just_nil: true
62
+ )
63
+
64
+ if lastfm_artist_tags.nil?
65
+ raise 'lastfm failed: ' + [:artist, :get_top_tags, 'The artist you supplied could not be found', { artist: name }].inspect
66
+ end
67
+
68
+ tags = lastfm_artist_tags.map{|t| t['name'].downcase }[0..9] rescue []
69
+ tags.select{|t| ['classic', 'classical'].include?(t) }.any? && tags.select{|t| ['pop', 'rock', 'crossover', 'alternative'].include?(t) }.none?
70
+ rescue StandardError => e
71
+ if e.message.match('last.fm response is just nil without exceptions')
72
+ false
73
+ else
74
+ raise e
75
+ end
76
+ end
77
+ end
78
+
79
+ def import_releases(musicbrainz_artist = nil)
80
+ musicbrainz_artist = MusicBrainz::Artist.find(mbid) unless musicbrainz_artist
81
+ offset, count, voluntary_releases = 0, 100, []
34
82
 
35
- offset, count = 0, 100
83
+ begin
84
+ count, working_release_groups = release_groups(musicbrainz_artist, voluntary_releases, offset, false, true)
36
85
 
37
- begin
38
- release_groups = musicbrainz_artist.release_groups(offset: offset)
39
- count = release_groups.total_count
86
+ working_release_groups.each do |array|
87
+ musicbrainz_release_group, musicbrainz_releases = array
88
+ release = releases.create(
89
+ artist_name: name, name: musicbrainz_release_group.title,
90
+ is_lp: musicbrainz_release_group.type == 'Album'
91
+ )
40
92
 
41
- release_groups.select{|r| ['Album', 'EP'].include?(r.type) && r.secondary_types.select{|st| ['Audiobook', 'Compilation', 'Live', 'Remix'].include?(st)}.none? && r.artists.length == 1}.each do |musicbrainz_release_group|
42
- releases = musicbrainz_release_group.releases
43
-
44
- next if releases.select{|r| r.status == 'Official' && (r.media.map(&:format).none? || r.media.map(&:format).select{|f| !['DVD-Video', 'DVD'].include?(f) }.any?) }.none?
45
-
46
- release = MusicRelease.create(artist_id: artist.id, artist_name: artist.name, name: musicbrainz_release_group.title)
47
-
48
- next unless release.persisted?
49
-
50
- release.releases = releases
51
- release.import_metadata!
93
+ unless release.persisted? && release.valid?
94
+ raise [release.errors.full_messages, release].inspect
52
95
  end
53
-
54
- offset += 100
55
- end while offset < count
96
+
97
+ release.releases = musicbrainz_releases
98
+ release.import_metadata!
99
+ end
56
100
 
57
- #artist.import_bonus_tracks
58
- #artist.import_music_videos_from_tapetv
101
+ offset += 100
102
+ end while offset < count
103
+ end
104
+
105
+ def release_groups(musicbrainz_artist, voluntary_releases, offset, without_limitation, with_releases = false)
106
+ musicbrainz_artist = MusicBrainz::Artist.find(mbid) unless musicbrainz_artist
107
+ count = 100
108
+ working_release_groups = musicbrainz_artist.release_groups(extra_query: 'AND (type:album OR type:ep OR type:soundtrack)', offset: offset)
109
+ count = working_release_groups.total_count
110
+ working_release_groups = working_release_groups.select{|r| ['Album', 'Soundtrack', 'EP'].include?(r.type) && r.secondary_types.select{|st| MusicRelease::SECONDARY_TYPES_BLACKLIST.include?(st)}.none? && r.artists.length == 1}
111
+
112
+ voluntary_releases += if working_release_groups.none?
113
+ []
114
+ else
115
+ releases.where('LOWER(music_releases.name) IN (?)', working_release_groups.map(&:title).uniq.map(&:downcase)).map{|r| "#{(r.is_lp ? 1 : 0)};#{r.name.downcase}"}
59
116
  end
117
+
118
+ voluntary_releases.uniq!
119
+ list = []
120
+
121
+ working_release_groups = working_release_groups.select{|r| !voluntary_releases.include?("#{r.type == 'Album' ? 1 : 0};#{r.title.downcase}")}.each do |musicbrainz_release_group|
122
+ release_is_lp_plus_name = "#{musicbrainz_release_group.type == 'Album' ? 1 : 0};#{musicbrainz_release_group.title.downcase}"
123
+
124
+ next if voluntary_releases.include?(release_is_lp_plus_name)
125
+
126
+ voluntary_releases << release_is_lp_plus_name
127
+
128
+ unless without_limitation
129
+ musicbrainz_release_group.releases = nil
130
+ musicbrainz_releases = musicbrainz_release_group.releases
131
+
132
+ next if musicbrainz_releases.select{|r| r.status == 'Official' && (r.media.map(&:format).none? || r.media.map(&:format).select{|f| !['DVD-Video', 'DVD'].include?(f) }.any?) }.none?
133
+ end
134
+
135
+ list << (with_releases ? [musicbrainz_release_group, musicbrainz_releases] : musicbrainz_release_group)
136
+ end.select{|r| !r.nil?}
137
+
138
+ [count, list]
60
139
  end
61
140
 
62
141
  def import_bonus_tracks
@@ -141,7 +220,7 @@ class MusicArtist < ActiveRecord::Base
141
220
  private
142
221
 
143
222
  def create_bonustracks_release
144
- release = releases.create(name: '[Bonus Tracks]')
223
+ release = releases.create(name: '[Bonus Tracks]', is_lp: true)
145
224
  release.update_attribute(:state, 'active')
146
225
  end
147
226