shroom 0.0.10 → 0.0.11
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/LICENSE +626 -629
- data/README +25 -32
- data/Rakefile +2 -4
- data/lib/sh_actions.rb +25 -19
- data/lib/sh_browse.rb +84 -54
- data/lib/sh_cover_art.rb +3 -6
- data/lib/sh_cover_browse.rb +10 -6
- data/lib/sh_database.rb +149 -160
- data/lib/sh_fingerprint.rb +64 -0
- data/lib/sh_main.rb +13 -11
- data/lib/sh_playlist.rb +15 -11
- data/lib/sh_util.rb +0 -17
- data/lib/sh_view.rb +45 -33
- data/lib/shroom-res/cover_unavailable.png +0 -0
- data/lib/shroom-res/icon_128x128.png +0 -0
- data/lib/shroom-res/plugins/lastfm_scrobbler.rb +32 -30
- data/lib/shroom-res/shroom.glade +13 -1
- metadata +4 -19
- data/lib/sh_album.rb +0 -14
- data/lib/sh_artist.rb +0 -14
- data/lib/sh_song.rb +0 -135
- data/lib/shroom-res/cover_unavailable.svg +0 -152
- data/lib/shroom-res/icon.svg +0 -206
data/README
CHANGED
@@ -1,38 +1,31 @@
|
|
1
|
-
Shroom
|
2
|
-
======
|
1
|
+
== Shroom
|
3
2
|
|
4
|
-
Dependencies
|
5
|
-
------------
|
3
|
+
=== Dependencies
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
'build-essential'
|
5
|
+
* rake
|
6
|
+
* libwxgtk2.8-0
|
7
|
+
* libgnome2-ruby
|
8
|
+
* libglade2-ruby
|
9
|
+
* libsqlite3-ruby
|
10
|
+
* ruby-dev
|
11
|
+
* libopenssl-ruby
|
12
|
+
* libmp3lame-dev
|
13
|
+
* libvorbis-dev
|
14
|
+
* build-essential
|
18
15
|
|
19
|
-
Gem dependencies
|
20
|
-
----------------
|
16
|
+
=== Gem dependencies
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
('shared-mime-info', '>= 0.1')
|
18
|
+
* ruby-mp3info (>= 0.6.13)
|
19
|
+
* MP4Info (>= 0.3.3)
|
20
|
+
* ruby-ogginfo (>= 0.3.2)
|
21
|
+
* flacinfo-rb (>= 0.4)
|
22
|
+
* earworm (>= 0.0.2)
|
23
|
+
* sequel (>= 3.2.0)
|
24
|
+
* wxruby (>= 2.0.0)
|
25
|
+
* rbrainz (>= 0.5.0)
|
26
|
+
* scrobbler (>= 0.2.3)
|
32
27
|
|
33
|
-
Repository
|
34
|
-
----------
|
35
|
-
|
36
|
-
The latest source code should be available at:
|
37
|
-
http://bitbucket.org/dismal_denizen/shroom/
|
28
|
+
=== Repository
|
38
29
|
|
30
|
+
The latest source code should be available at
|
31
|
+
http://bitbucket.org/dismal_denizen/shroom
|
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.
|
10
|
+
s.version = '0.0.11'
|
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'
|
@@ -25,16 +25,14 @@ spec = Gem::Specification.new do |s|
|
|
25
25
|
s.add_dependency('wxruby', '>= 2.0.0')
|
26
26
|
s.add_dependency('rbrainz', '>= 0.5.0')
|
27
27
|
s.add_dependency('scrobbler', '>= 0.2.3')
|
28
|
-
s.add_dependency('shared-mime-info', '>= 0.1')
|
29
28
|
s.requirements << 'rake'
|
30
29
|
s.requirements << 'libwxgtk2.8-0'
|
31
30
|
s.requirements << 'libgnome2-ruby'
|
32
31
|
s.requirements << 'libglade2-ruby'
|
33
|
-
s.requirements << 'libgtkhtml2-ruby'
|
34
32
|
s.requirements << 'libsqlite3-ruby'
|
33
|
+
s.requirements << 'libopenssl-ruby'
|
35
34
|
# Requirements for icanhasaudio
|
36
35
|
s.requirements << 'ruby-dev'
|
37
|
-
s.requirements << 'libopenssl-ruby'
|
38
36
|
s.requirements << 'libmp3lame-dev'
|
39
37
|
s.requirements << 'libvorbis-dev'
|
40
38
|
s.requirements << 'build-essential'
|
data/lib/sh_actions.rb
CHANGED
@@ -51,25 +51,28 @@ module Sh
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
action[:stock_image] = Stock::ADD
|
54
|
-
|
54
|
+
|
55
55
|
action = Actions.new 'Reread tags', :song do |song|
|
56
56
|
browse = Sh::Browse.instance
|
57
57
|
browse.remove_song song if browse
|
58
|
-
song.
|
59
|
-
|
58
|
+
path = song.path
|
59
|
+
song.delete
|
60
|
+
song = Database.add_song path
|
60
61
|
browse.insert_song song if browse
|
61
62
|
end
|
62
63
|
action[:stock_image] = Stock::HARDDISK
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
64
|
+
|
65
|
+
if try_require 'earworm' and try_require 'rbrainz'
|
66
|
+
action = Actions.new 'Lookup metadata automatically', :song do |song|
|
67
|
+
if song.lookup!
|
68
|
+
browse = Sh::Browse.instance
|
69
|
+
browse.remove_song song if browse
|
70
|
+
song.save
|
71
|
+
browse.insert_song song if browse
|
72
|
+
end
|
70
73
|
end
|
71
|
-
|
72
|
-
|
74
|
+
action[:stock_image] = Stock::FIND
|
75
|
+
end
|
73
76
|
|
74
77
|
action = Actions.new 'Add to playlist', :song, :playlist do |song, playlist|
|
75
78
|
playlist << song
|
@@ -87,17 +90,19 @@ module Sh
|
|
87
90
|
glade = Global::GLADE['dlg_song']
|
88
91
|
dlg_song = glade['dlg_song']
|
89
92
|
txt_title = glade['txt_title']
|
90
|
-
txt_title.text = song.title
|
93
|
+
txt_title.text = song.title || File.basename(song.path)
|
94
|
+
spn_track_num = glade['spn_track_num']
|
95
|
+
spn_track_num.value = song.track_num || 0
|
91
96
|
|
92
97
|
# Artists combo box
|
93
98
|
sto_artists = ListStore.new(String, Artist)
|
94
99
|
cmb_artist = glade['cmb_artist']
|
95
100
|
sel = 0
|
96
|
-
|
101
|
+
Artist.order(:name).each_with_index do |artist, i|
|
97
102
|
iter = sto_artists.append
|
98
103
|
iter[0] = artist.name
|
99
104
|
iter[1] = artist
|
100
|
-
sel = i if song.artist && artist.
|
105
|
+
sel = i if song.artist && artist.id == song.artist.id
|
101
106
|
end
|
102
107
|
cmb_artist.model = sto_artists
|
103
108
|
cmb_artist.active = sel
|
@@ -106,11 +111,11 @@ module Sh
|
|
106
111
|
sto_albums = ListStore.new(String, Album)
|
107
112
|
cmb_album = glade['cmb_album']
|
108
113
|
sel = 0
|
109
|
-
|
114
|
+
Album.order(:title).each_with_index do |album, i|
|
110
115
|
iter = sto_albums.append
|
111
116
|
iter[0] = album.title
|
112
117
|
iter[1] = album
|
113
|
-
sel = i if song.album && album.
|
118
|
+
sel = i if song.album && album.id == song.album.id
|
114
119
|
end
|
115
120
|
cmb_album.model = sto_albums
|
116
121
|
cmb_album.active = sel
|
@@ -125,12 +130,13 @@ module Sh
|
|
125
130
|
end
|
126
131
|
btn_ok = glade['btn_ok']
|
127
132
|
ok_handle = btn_ok.signal_connect('clicked') do
|
128
|
-
browse =
|
133
|
+
browse = Browse.instance
|
129
134
|
song.title = txt_title.text
|
135
|
+
song.track_num = spn_track_num.value
|
130
136
|
song.artist = cmb_artist.active_iter[1]
|
131
137
|
song.album = cmb_album.active_iter[1]
|
132
138
|
browse.remove_song song if browse
|
133
|
-
|
139
|
+
song.save
|
134
140
|
browse.insert_song song if browse
|
135
141
|
dlg_song.hide
|
136
142
|
end
|
data/lib/sh_browse.rb
CHANGED
@@ -15,9 +15,9 @@ module Sh
|
|
15
15
|
data = iter[0]
|
16
16
|
if data.is_a? Sh::Song
|
17
17
|
if data.title
|
18
|
-
cell.text =
|
18
|
+
cell.text = "%.2d\t%s" % [data.track_num, data.title]
|
19
19
|
else
|
20
|
-
cell.text = data.path
|
20
|
+
cell.text = File.basename(data.path)
|
21
21
|
end
|
22
22
|
else
|
23
23
|
cell.text = data.to_s
|
@@ -26,13 +26,12 @@ module Sh
|
|
26
26
|
@tree.append_column col_artist
|
27
27
|
|
28
28
|
@model = TreeStore.new(Object, Array)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# Gtk::SORT_ASCENDING)
|
29
|
+
@model.set_default_sort_func do |a, b|
|
30
|
+
oa, ob = a[0], b[0]
|
31
|
+
oa.to_s <=> ob.to_s
|
32
|
+
end
|
33
|
+
@model.set_sort_column_id(TreeSortable::DEFAULT_SORT_COLUMN_ID,
|
34
|
+
Gtk::SORT_ASCENDING)
|
36
35
|
@tree.model = @model
|
37
36
|
|
38
37
|
GLib::Timeout.add(1000) do
|
@@ -100,8 +99,7 @@ module Sh
|
|
100
99
|
@tree.signal_connect('row_activated') do |widget, path, col|
|
101
100
|
iter = @tree.model.get_iter(path)
|
102
101
|
parent = iter.parent
|
103
|
-
|
104
|
-
if song.is_a? Sh::Song
|
102
|
+
if iter[0].is_a? Song
|
105
103
|
view.stop
|
106
104
|
queue = []
|
107
105
|
while iter.parent == parent
|
@@ -118,7 +116,7 @@ module Sh
|
|
118
116
|
view.queue = queue
|
119
117
|
view.queue_pos = prev.size
|
120
118
|
view.play
|
121
|
-
|
119
|
+
elsif not iter[1].empty?
|
122
120
|
view.stop
|
123
121
|
view.queue = iter[1].dup
|
124
122
|
view.play
|
@@ -131,7 +129,11 @@ module Sh
|
|
131
129
|
end
|
132
130
|
|
133
131
|
def self.instance
|
134
|
-
|
132
|
+
return @@instance
|
133
|
+
end
|
134
|
+
|
135
|
+
def widget
|
136
|
+
return @scroll
|
135
137
|
end
|
136
138
|
|
137
139
|
def remove_song song
|
@@ -158,74 +160,102 @@ module Sh
|
|
158
160
|
end
|
159
161
|
|
160
162
|
def insert_song song
|
161
|
-
artist_node = album_node =
|
162
|
-
@model.each do |model, path, iter|
|
163
|
-
data = iter[0]
|
163
|
+
artist_node = album_node = nil
|
164
164
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
165
|
+
if song.album.special_case?
|
166
|
+
artist_node = @artist_nodes[song.artist.id]
|
167
|
+
else
|
168
|
+
artist_node = @artist_nodes[song.album.artist.id]
|
169
|
+
end
|
170
170
|
|
171
|
-
|
172
|
-
|
173
|
-
|
171
|
+
if artist_node and artist_node.has_child?
|
172
|
+
iter = artist_node.first_child
|
173
|
+
loop do
|
174
|
+
if iter[0].id == song.album.id
|
175
|
+
album_node = iter
|
176
|
+
break
|
177
|
+
else
|
178
|
+
break unless iter.next!
|
174
179
|
end
|
175
180
|
end
|
176
181
|
end
|
182
|
+
|
177
183
|
# Artist node
|
178
184
|
unless artist_node
|
179
|
-
artist_node =
|
180
|
-
artist_node[0] = song.artist
|
181
|
-
artist_node[1] = []
|
185
|
+
artist_node = create_artist_node song.artist
|
182
186
|
end
|
183
|
-
|
187
|
+
|
184
188
|
# Album node
|
185
189
|
unless album_node
|
186
|
-
album_node =
|
187
|
-
album_node[0] = song.album
|
188
|
-
album_node[1] = []
|
190
|
+
album_node = create_album_node artist_node, song.album, []
|
189
191
|
end
|
192
|
+
|
193
|
+
artist_node[1] << song
|
194
|
+
artist_node[1] = artist_node[1].sort_by{|s| s.to_s}
|
190
195
|
album_node[1] << song
|
196
|
+
album_node[1] = album_node[1].sort_by{|s| s.to_s}
|
197
|
+
|
191
198
|
# Song node
|
192
|
-
song_node = @model.
|
199
|
+
song_node = @model.append album_node
|
193
200
|
song_node[0] = song
|
194
201
|
song_node[1] = [song]
|
195
202
|
end
|
196
203
|
|
197
204
|
def fill_model
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
205
|
+
@artist_nodes = {}
|
206
|
+
@model.clear
|
207
|
+
|
208
|
+
Artist.each do |artist|
|
209
|
+
artist_node = create_artist_node artist
|
210
|
+
|
211
|
+
unknown_album_songs = []
|
212
|
+
non_album_songs = []
|
213
|
+
artist.songs.each do |song|
|
214
|
+
if song.album.id == Album::UNKNOWN
|
215
|
+
unknown_album_songs << song
|
216
|
+
elsif song.album.id == Album::NON_ALBUM
|
217
|
+
non_album_songs << song
|
218
|
+
end
|
207
219
|
end
|
208
|
-
artist_node[1] ||= []
|
209
|
-
artist_node[1] << song
|
210
220
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
221
|
+
unless unknown_album_songs.empty?
|
222
|
+
create_album_node artist_node, Album[Album::UNKNOWN], unknown_album_songs
|
223
|
+
end
|
224
|
+
unless non_album_songs.empty?
|
225
|
+
create_album_node artist_node, Album[Album::NON_ALBUM], non_album_songs
|
215
226
|
end
|
216
|
-
album_node[1] ||= []
|
217
|
-
album_node[1] << song
|
218
227
|
|
219
|
-
|
220
|
-
|
221
|
-
|
228
|
+
artist.albums.each do |album|
|
229
|
+
create_album_node artist_node, album, album.songs
|
230
|
+
end
|
231
|
+
|
232
|
+
artist_node[1] = artist_node[1].sort_by {|s| s.to_s}
|
222
233
|
|
223
234
|
Kelp.process_events
|
224
235
|
end
|
225
236
|
end
|
226
237
|
|
227
|
-
|
228
|
-
|
238
|
+
private
|
239
|
+
def create_artist_node artist
|
240
|
+
artist_node = @model.append nil
|
241
|
+
@artist_nodes[artist.id] = artist_node
|
242
|
+
artist_node[0] = artist
|
243
|
+
artist_node[1] = []
|
244
|
+
return artist_node
|
245
|
+
end
|
246
|
+
|
247
|
+
def create_album_node artist_node, album, songs
|
248
|
+
songs = songs.sort_by {|s| s.to_s}
|
249
|
+
album_node = @model.append artist_node
|
250
|
+
album_node[0] = album
|
251
|
+
album_node[1] = songs
|
252
|
+
songs.each do |song|
|
253
|
+
song_node = @model.append album_node
|
254
|
+
song_node[0] = song
|
255
|
+
song_node[1] = [song]
|
256
|
+
artist_node[1] << song
|
257
|
+
end
|
258
|
+
return album_node
|
229
259
|
end
|
230
260
|
end
|
231
261
|
end
|
data/lib/sh_cover_art.rb
CHANGED
@@ -7,17 +7,15 @@ module Sh
|
|
7
7
|
if artist and album
|
8
8
|
@lastfm_rest ||= Rest::Get.new("http://ws.audioscrobbler.com/2.0",
|
9
9
|
:api_key => Sh::KEYS[:lastfm], :method => "album.getInfo")
|
10
|
-
path = "#{
|
10
|
+
path = "#{Global::PATHS[:cover_dir]}/#{artist.to_md5}_#{album.to_md5}"
|
11
11
|
if File.exists? path and File.size?(path) > 0
|
12
12
|
return path
|
13
|
-
|
13
|
+
else
|
14
14
|
doc = REXML::Document.new(@lastfm_rest[:artist => artist, :album => album])
|
15
15
|
img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
|
16
16
|
if img_url
|
17
17
|
open(path, 'w') do |output|
|
18
|
-
open(img_url)
|
19
|
-
output << input.read
|
20
|
-
end
|
18
|
+
output << open(img_url).read
|
21
19
|
end
|
22
20
|
end
|
23
21
|
end
|
@@ -26,4 +24,3 @@ module Sh
|
|
26
24
|
end
|
27
25
|
end
|
28
26
|
end
|
29
|
-
|
data/lib/sh_cover_browse.rb
CHANGED
@@ -5,7 +5,12 @@ module Sh
|
|
5
5
|
def initialize view
|
6
6
|
@model = ListStore.new(Gdk::Pixbuf, Object)
|
7
7
|
@model.set_default_sort_func do |a, b|
|
8
|
-
a[COL_ALBUM]
|
8
|
+
album_a, album_b = a[COL_ALBUM], b[COL_ALBUM]
|
9
|
+
comparison = album_a.artist.name <=> album_b.artist.name
|
10
|
+
if comparison == 0
|
11
|
+
comparison = album_a.title <=> album_b.title
|
12
|
+
end
|
13
|
+
comparison
|
9
14
|
end
|
10
15
|
@model.set_sort_column_id(TreeSortable::DEFAULT_SORT_COLUMN_ID,
|
11
16
|
Gtk::SORT_ASCENDING)
|
@@ -17,8 +22,7 @@ module Sh
|
|
17
22
|
album = iter[COL_ALBUM]
|
18
23
|
if album
|
19
24
|
view.stop
|
20
|
-
|
21
|
-
view.queue = songs.sort_by {|s| (s.to_s || '')}
|
25
|
+
view.queue = album.songs.sort_by {|s| (s.to_s || '')}
|
22
26
|
view.play
|
23
27
|
end
|
24
28
|
end
|
@@ -35,15 +39,15 @@ module Sh
|
|
35
39
|
|
36
40
|
def fill_model
|
37
41
|
@model.clear
|
38
|
-
|
42
|
+
Album.each do |album|
|
39
43
|
begin
|
40
44
|
pixbuf = Gdk::Pixbuf.new(album.image_path).scale(128, 128)
|
41
45
|
iter = @model.append
|
42
46
|
iter[COL_ALBUM] = album
|
43
47
|
iter[COL_PIXBUF] = pixbuf
|
44
|
-
rescue
|
48
|
+
rescue
|
45
49
|
# Insufficient data about album
|
46
|
-
end
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
53
|
|