shroom 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require 'rake/testtask'
7
7
 
8
8
  spec = Gem::Specification.new do |s|
9
9
  s.name = 'shroom'
10
- s.version = '0.0.5'
10
+ s.version = '0.0.6'
11
11
  s.has_rdoc = true
12
12
  s.extra_rdoc_files = ['README', 'LICENSE']
13
13
  s.summary = 'Shroom is a music player and organizer'
@@ -28,6 +28,7 @@ spec = Gem::Specification.new do |s|
28
28
  s.add_dependency('shared-mime-info', '>= 0.1')
29
29
  s.requirements << 'rake'
30
30
  s.requirements << 'libgnome2-ruby'
31
+ s.requirements << 'libgtk-mozembed-ruby'
31
32
  s.requirements << 'libsqlite3-ruby'
32
33
  s.requirements << 'libglade2-ruby'
33
34
  # Requirements for icanhasaudio
@@ -33,7 +33,7 @@ module Sh
33
33
  # Right-click
34
34
  if event.button == 3
35
35
  path = @tree.get_path_at_pos(event.x, event.y).first
36
- data = @tree.model.get_iter(path)[0]
36
+ data = @model.get_iter(path)[0]
37
37
  selection = @tree.selection
38
38
  selected_rows = selection.selected_rows
39
39
  clicked_in_selection = false
@@ -58,80 +58,26 @@ module Sh
58
58
  pbtn_lookup = MenuItem.new 'Lookup metadata automatically'
59
59
  menu.append pbtn_lookup
60
60
  pbtn_lookup.signal_connect('activate') do |widget|
61
- song.lookup!
62
- $db.save_song song
63
- @model.clear
64
- fill_model
61
+ if song.lookup!
62
+ remove_song @model.get_iter(path)
63
+ $db.save_song song
64
+ insert_song song
65
+ end
65
66
  end
66
67
 
67
68
  pbtn_reread_tags = MenuItem.new 'Reread tags'
68
69
  menu.append pbtn_reread_tags
69
70
  pbtn_reread_tags.signal_connect('activate') do |widget|
71
+ remove_song @model.get_iter(path)
70
72
  song.read_tags!(true)
71
73
  $db.save_song song
72
- @model.clear
73
- fill_model
74
+ insert_song song
74
75
  end
75
76
 
76
77
  pbtn_properties = ImageMenuItem.new Stock::PROPERTIES
77
78
  menu.append pbtn_properties
78
79
  pbtn_properties.signal_connect('activate') do |widget|
79
- glade = Global::GLADE['dlg_song']
80
- dlg_song = glade['dlg_song']
81
- txt_title = glade['txt_title']
82
- txt_title.text = song.title
83
-
84
- # Artists combo box
85
- sto_artists = ListStore.new(String, Artist)
86
- cmb_artist = glade['cmb_artist']
87
- sel = 0
88
- $db.artists.sort_by{|a| (a.name || '')}.each_with_index do |artist, i|
89
- iter = sto_artists.append
90
- iter[0] = artist.name
91
- iter[1] = artist
92
- sel = i if song.artist && artist.db_id == song.artist.db_id
93
- end
94
- cmb_artist.model = sto_artists
95
- cmb_artist.active = sel
96
-
97
- # Albums combo box
98
- sto_albums = ListStore.new(String, Album)
99
- cmb_album = glade['cmb_album']
100
- sel = 0
101
- $db.albums.sort_by{|a| (a.title || '')}.each_with_index do |album, i|
102
- iter = sto_albums.append
103
- iter[0] = album.title
104
- iter[1] = album
105
- sel = i if song.album && album.db_id == song.album.db_id
106
- end
107
- cmb_album.model = sto_albums
108
- cmb_album.active = sel
109
-
110
- glade['chk_image'].sensitive = false
111
- glade['fbtn_image'].sensitive = false
112
- glade['btn_add_artist'].sensitive = false
113
- glade['btn_add_album'].sensitive = false
114
-
115
- dlg_song.signal_connect('delete-event') do
116
- dlg_song.hide
117
- end
118
- btn_ok = glade['btn_ok']
119
- ok_handle = btn_ok.signal_connect('clicked') do
120
- song.title = txt_title.text
121
- song.artist = cmb_artist.active_iter[1]
122
- song.album = cmb_album.active_iter[1]
123
- $db.save_song song
124
- @model.clear
125
- fill_model
126
- dlg_song.hide
127
- end
128
- btn_cancel = glade['btn_cancel']
129
- close_handle = btn_cancel.signal_connect('clicked') do
130
- dlg_song.hide
131
- end
132
- dlg_song.run
133
- btn_ok.signal_handler_disconnect ok_handle
134
- btn_cancel.signal_handler_disconnect close_handle
80
+ show_song_properties_dialog(song)
135
81
  end
136
82
 
137
83
  menu.show_all
@@ -172,6 +118,125 @@ module Sh
172
118
  @scroll.add @tree
173
119
  end
174
120
 
121
+ def show_song_properties_dialog song
122
+ glade = Global::GLADE['dlg_song']
123
+ dlg_song = glade['dlg_song']
124
+ txt_title = glade['txt_title']
125
+ txt_title.text = song.title
126
+
127
+ # Artists combo box
128
+ sto_artists = ListStore.new(String, Artist)
129
+ cmb_artist = glade['cmb_artist']
130
+ sel = 0
131
+ $db.artists.sort_by{|a| (a.name || '')}.each_with_index do |artist, i|
132
+ iter = sto_artists.append
133
+ iter[0] = artist.name
134
+ iter[1] = artist
135
+ sel = i if song.artist && artist.db_id == song.artist.db_id
136
+ end
137
+ cmb_artist.model = sto_artists
138
+ cmb_artist.active = sel
139
+
140
+ # Albums combo box
141
+ sto_albums = ListStore.new(String, Album)
142
+ cmb_album = glade['cmb_album']
143
+ sel = 0
144
+ $db.albums.sort_by{|a| (a.title || '')}.each_with_index do |album, i|
145
+ iter = sto_albums.append
146
+ iter[0] = album.title
147
+ iter[1] = album
148
+ sel = i if song.album && album.db_id == song.album.db_id
149
+ end
150
+ cmb_album.model = sto_albums
151
+ cmb_album.active = sel
152
+
153
+ glade['chk_image'].sensitive = false
154
+ glade['fbtn_image'].sensitive = false
155
+ glade['btn_add_artist'].sensitive = false
156
+ glade['btn_add_album'].sensitive = false
157
+
158
+ dlg_song.signal_connect('delete-event') do
159
+ dlg_song.hide
160
+ end
161
+ btn_ok = glade['btn_ok']
162
+ ok_handle = btn_ok.signal_connect('clicked') do
163
+ song.title = txt_title.text
164
+ song.artist = cmb_artist.active_iter[1]
165
+ song.album = cmb_album.active_iter[1]
166
+ remove_song song
167
+ $db.save_song song
168
+ insert_song song
169
+ dlg_song.hide
170
+ end
171
+ btn_cancel = glade['btn_cancel']
172
+ close_handle = btn_cancel.signal_connect('clicked') do
173
+ dlg_song.hide
174
+ end
175
+ dlg_song.run
176
+ btn_ok.signal_handler_disconnect ok_handle
177
+ btn_cancel.signal_handler_disconnect close_handle
178
+ end
179
+
180
+ def remove_song song
181
+ song_node = nil
182
+ if song.is_a? TreeIter
183
+ song_node = song
184
+ else
185
+ @model.each do |model, path, iter|
186
+ if iter[0] == song
187
+ song_node = iter
188
+ end
189
+ end
190
+ end
191
+
192
+ if song_node
193
+ album_node = song_node.parent
194
+ artist_node = album_node.parent
195
+ @model.remove song_node
196
+ album_node[1].delete song
197
+ @model.remove album_node unless album_node.has_child?
198
+ artist_node[1].delete song
199
+ @model.remove artist_node unless artist_node.has_child?
200
+ end
201
+ end
202
+
203
+ def insert_song song
204
+ artist_node = album_node = prev_song_node = nil
205
+ @model.each do |model, path, iter|
206
+ data = iter[0]
207
+
208
+ if data.is_a? Artist
209
+ artist_node = iter if data.db_id == song.artist.db_id
210
+ elsif artist_node and data.is_a? Album
211
+ album_node = iter if iter.parent == artist_node and data.db_id == song.album.db_id
212
+ end
213
+
214
+ if album_node and iter.parent == album_node and data.is_a? Song
215
+ if data.track_num < song.track_num
216
+ prev_song_node = iter
217
+ end
218
+ end
219
+ end
220
+ # Artist node
221
+ unless artist_node
222
+ artist_node = @model.append nil
223
+ artist_node[0] = song.artist
224
+ artist_node[1] = []
225
+ end
226
+ artist_node[1] << song
227
+ # Album node
228
+ unless album_node
229
+ album_node = @model.append artist_node
230
+ album_node[0] = song.album
231
+ album_node[1] = []
232
+ end
233
+ album_node[1] << song
234
+ # Song node
235
+ song_node = @model.insert_after(album_node, prev_song_node)
236
+ song_node[0] = song
237
+ song_node[1] = [song]
238
+ end
239
+
175
240
  def fill_model
176
241
  artist_node = album_node = track_node = nil
177
242
  $db.songs.sort_by {|a| (a.to_s || '')}.each do |song|
@@ -196,6 +261,8 @@ module Sh
196
261
  track_node = @model.append album_node
197
262
  track_node[0] = song
198
263
  track_node[1] = [song]
264
+
265
+ Kelp.process_events
199
266
  end
200
267
  end
201
268
 
@@ -1,5 +1,5 @@
1
1
  require 'open-uri'
2
- require 'rexml/document'
2
+ require 'hpricot'
3
3
  require 'cgi'
4
4
 
5
5
  module Sh
@@ -15,8 +15,15 @@ module Sh
15
15
  url = "http://lyricwiki.org/api.php?func=getSong&fmt=xml" +
16
16
  "&artist=#{CGI.escape(artist)}" +
17
17
  "&song=#{CGI.escape(title)}"
18
- doc = REXML::Document.new(open(url).read)
19
- REXML::XPath.first(doc, '//lyrics').text
18
+ doc = Hpricot(open(url))
19
+ page_url = (doc/'url').inner_text
20
+ lyrics = nil
21
+ unless page_url.end_with? ';action=edit'
22
+ doc = Hpricot(open(page_url).read.gsub(/<[\s]*br[\s]*\/?>/, "\n"))
23
+ #TODO: Choose most appropriate lyricbox, not just the first
24
+ lyrics = (doc/'.lyricbox').first.inner_text.strip
25
+ end
26
+ return lyrics
20
27
  end
21
28
  end
22
29
  end
@@ -5,21 +5,21 @@ module Sh
5
5
  @supported = try_require('rubygems') and try_require('rbus')
6
6
 
7
7
  if supported?
8
- bus = RBus.session_bus
9
-
10
8
  begin
11
- @mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
12
- @mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
13
- @mmkeys.GrabMediaPlayerKeys('Shroom', 0)
14
- rescue
9
+ bus = RBus.session_bus
10
+
15
11
  begin
16
12
  @mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
17
13
  @mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
18
14
  @mmkeys.GrabMediaPlayerKeys('Shroom', 0)
19
15
  rescue
20
- # MediaKeys are not supported after all
21
- @supported = false
16
+ @mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
17
+ @mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
18
+ @mmkeys.GrabMediaPlayerKeys('Shroom', 0)
22
19
  end
20
+ rescue
21
+ # MediaKeys are not supported after all
22
+ @supported = false
23
23
  end
24
24
  end
25
25
 
@@ -87,7 +87,7 @@ module Sh
87
87
  end
88
88
  end
89
89
 
90
- @@scrobble_queue << Scrobbler::Scrobble.new(
90
+ @@scrobble_queue << {
91
91
  :session_id => auth.session_id,
92
92
  :submission_url => auth.submission_url,
93
93
  :artist => song.artist.name,
@@ -95,7 +95,7 @@ module Sh
95
95
  :album => song.album.title,
96
96
  :time => Time.new,
97
97
  :length => duration,
98
- :track_number => song.track_num)
98
+ :track_number => song.track_num}
99
99
 
100
100
  # No point attempting to scrobble without authentication
101
101
  return if auth.status.strip.upcase != 'OK'
@@ -103,10 +103,15 @@ module Sh
103
103
  Thread.new do
104
104
  failed_scrobbles = []
105
105
  until @@scrobble_queue.empty?
106
- scrobble = @@scrobble_queue.pop
107
- scrobble.submit! rescue Exception
106
+ scrobble = nil
107
+ begin
108
+ scrobble = Scrobbler::Scrobble.new(@@scrobble_queue.pop)
109
+ scrobble.submit!
110
+ rescue
111
+ scrobble = nil
112
+ end
108
113
 
109
- if scrobble.status.strip.upcase == 'OK'
114
+ if scrobble and scrobble.status.strip.upcase == 'OK'
110
115
  puts "Scrobble for '#{scrobble.track}' succeeded"
111
116
  else
112
117
  puts "Warning: Scrobble for '#{scrobble.track}' failed"
@@ -9,8 +9,8 @@ include MusicBrainz
9
9
  module Sh
10
10
  class Song
11
11
  attr_reader :path, :mime, :matches
12
- attr_writer :album, :artist
13
- attr_accessor :title, :mbid, :lyrics, :track_num
12
+ attr_writer :album, :artist, :track_num
13
+ attr_accessor :title, :mbid, :lyrics
14
14
  attr_accessor :db_id
15
15
 
16
16
  def initialize path
@@ -29,6 +29,10 @@ module Sh
29
29
  return @artist
30
30
  end
31
31
 
32
+ def track_num
33
+ return (@track_num || 0).to_i
34
+ end
35
+
32
36
  def duration
33
37
  @duration ||= Sh::TagReader.read(path)[:duration]
34
38
  end
@@ -36,11 +40,6 @@ module Sh
36
40
  def lookup!
37
41
  begin
38
42
  track = lookup_multiple.first
39
- rescue Exception
40
- return
41
- end
42
-
43
- if track
44
43
  # Don't distinguish between bands with the same name by adding to the name
45
44
  track.artist.disambiguation = false
46
45
  # Pull data from response
@@ -57,7 +56,11 @@ module Sh
57
56
  tracks.to_a.each_with_index do |t, i|
58
57
  self.track_num = i + 1 if t.title == track.title and t.duration == track.duration
59
58
  end
59
+ rescue Exception
60
+ return false
60
61
  end
62
+
63
+ return true
61
64
  end
62
65
 
63
66
  private
@@ -69,6 +69,23 @@ class StringMatcher
69
69
  end
70
70
  end
71
71
 
72
+ require 'thread'
73
+ require 'gtkhtml2'
74
+ module Gtk
75
+ class HtmlView
76
+ def set_html content=nil
77
+ @doc_mutex ||= Mutex.new
78
+ doc = self.document ||= HtmlDocument.new
79
+ @doc_mutex.synchronize do
80
+ doc.open_stream 'text/html'
81
+ content ||= (yield || '')
82
+ doc.write_stream content
83
+ doc.close_stream
84
+ end
85
+ end
86
+ end
87
+ end
88
+
72
89
  module Kelp
73
90
  class ProgressDialog < Gtk::Dialog
74
91
  def initialize(title)
@@ -5,6 +5,7 @@ require 'sh_lyrics'
5
5
  require 'sh_cover_art'
6
6
  require 'sh_mmkeys'
7
7
  require 'gtk2'
8
+ require 'gtkhtml2'
8
9
  include Gtk
9
10
 
10
11
  module Sh
@@ -173,17 +174,16 @@ module Sh
173
174
  @img_cover = Image.new
174
175
  event_box = EventBox.new
175
176
  event_box.add @img_cover
176
- event_box.signal_connect("button-press-event") do |w, event|
177
+ event_box.signal_connect('button-press-event') do |w, event|
177
178
  Kelp::ImageDialog.new(@pixbuf, @player.song.album.title).show if @pixbuf and @player
178
179
  end
179
180
  @img_cover.height_request = 150
180
181
  sidebar.pack_start event_box, false, false, 0
181
182
  # Lyrics text view
182
- @txt_lyrics = TextView.new
183
- @txt_lyrics.editable = false
183
+ @html_lyrics = HtmlView.new
184
184
  scr_lyrics = ScrolledWindow.new(nil, nil)
185
185
  scr_lyrics.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
186
- scr_lyrics.add @txt_lyrics
186
+ scr_lyrics.add @html_lyrics
187
187
  # Browse area
188
188
  browse = Sh::Browse.new self
189
189
  queue = Sh::Queue.new self
@@ -384,14 +384,16 @@ module Sh
384
384
  end
385
385
  # Lyrics
386
386
  if $prefs[:lyrics]
387
- @txt_lyrics.buffer.text = "Loading..."
387
+ @html_lyrics.set_html 'Loading...'
388
388
  Thread.new do
389
389
  if not song.lyrics
390
390
  song.lyrics = Sh::Lyrics.get_lyrics(song)
391
391
  $db.save_song song
392
392
  end
393
393
  # Show lyrics unless requests have been shuffled
394
- @txt_lyrics.buffer.text = song.lyrics if @player.song == song
394
+ if @player.song == song
395
+ show_lyrics song
396
+ end
395
397
  end
396
398
  end
397
399
  if @rnotify
@@ -404,6 +406,27 @@ module Sh
404
406
  end
405
407
  end
406
408
 
409
+ def show_lyrics song
410
+ lyrics = song.lyrics || "[Lyrics not found]"
411
+ @html_lyrics.set_html do
412
+ %{
413
+ <h2>#{song.title}</h2>
414
+ <pre style='font-family: sans-serif;'>#{lyrics}</pre>
415
+ <p></p><a href="#reload">Reload lyrics</a>
416
+ }
417
+ end
418
+ @html_lyrics.document.signal_connect('link-clicked') do |doc, loc|
419
+ if loc == '#reload'
420
+ @html_lyrics.set_html 'Loading...'
421
+ Thread.new do
422
+ song.lyrics = Sh::Lyrics.get_lyrics(song)
423
+ $db.save_song song
424
+ show_lyrics song if @player and @player.song == song
425
+ end
426
+ end
427
+ end
428
+ end
429
+
407
430
  public
408
431
  def queue
409
432
  return @queue
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shroom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aiden Nibali
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-31 00:00:00 +10:00
12
+ date: 2009-08-08 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -169,6 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - rake
171
171
  - libgnome2-ruby
172
+ - libgtk-mozembed-ruby
172
173
  - libsqlite3-ruby
173
174
  - libglade2-ruby
174
175
  - ruby-dev