shroom 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/lib/sh_browse.rb +71 -1
- data/lib/sh_cover_art.rb +1 -1
- data/lib/sh_global.rb +13 -1
- data/lib/sh_mmkeys.rb +44 -0
- data/lib/sh_playlist.rb +36 -0
- data/lib/sh_song.rb +8 -2
- data/lib/sh_tagreader.rb +18 -13
- data/lib/sh_util.rb +50 -2
- data/lib/sh_view.rb +115 -58
- data/lib/shroom-res/shroom.glade +339 -2
- metadata +5 -2
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.5'
|
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'
|
@@ -26,6 +26,7 @@ spec = Gem::Specification.new do |s|
|
|
26
26
|
s.add_dependency('rbrainz', '>= 0.5.0')
|
27
27
|
s.add_dependency('scrobbler', '>= 0.2.3')
|
28
28
|
s.add_dependency('shared-mime-info', '>= 0.1')
|
29
|
+
s.requirements << 'rake'
|
29
30
|
s.requirements << 'libgnome2-ruby'
|
30
31
|
s.requirements << 'libsqlite3-ruby'
|
31
32
|
s.requirements << 'libglade2-ruby'
|
data/lib/sh_browse.rb
CHANGED
@@ -63,6 +63,76 @@ module Sh
|
|
63
63
|
@model.clear
|
64
64
|
fill_model
|
65
65
|
end
|
66
|
+
|
67
|
+
pbtn_reread_tags = MenuItem.new 'Reread tags'
|
68
|
+
menu.append pbtn_reread_tags
|
69
|
+
pbtn_reread_tags.signal_connect('activate') do |widget|
|
70
|
+
song.read_tags!(true)
|
71
|
+
$db.save_song song
|
72
|
+
@model.clear
|
73
|
+
fill_model
|
74
|
+
end
|
75
|
+
|
76
|
+
pbtn_properties = ImageMenuItem.new Stock::PROPERTIES
|
77
|
+
menu.append pbtn_properties
|
78
|
+
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
|
135
|
+
end
|
66
136
|
|
67
137
|
menu.show_all
|
68
138
|
menu.popup(nil, nil, event.button, event.time)
|
@@ -104,7 +174,7 @@ module Sh
|
|
104
174
|
|
105
175
|
def fill_model
|
106
176
|
artist_node = album_node = track_node = nil
|
107
|
-
$db.songs.sort_by {|a| a.to_s}.each do |song|
|
177
|
+
$db.songs.sort_by {|a| (a.to_s || '')}.each do |song|
|
108
178
|
artist = song.artist
|
109
179
|
if not artist_node or artist_node[0].db_id != artist.db_id
|
110
180
|
artist_node = @model.append nil
|
data/lib/sh_cover_art.rb
CHANGED
@@ -7,7 +7,7 @@ module Sh
|
|
7
7
|
def CoverArt.get_cover song
|
8
8
|
if $prefs[:cover_art]
|
9
9
|
artist, album = song.artist.name, song.album.title
|
10
|
-
path = "#{$cover_dir}/#{artist}
|
10
|
+
path = "#{$cover_dir}/#{artist.to_md5}_#{album.to_md5}"
|
11
11
|
return path if File.exists? path
|
12
12
|
doc = lastfm("album.getInfo", {:artist => artist, :album => album})
|
13
13
|
img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
|
data/lib/sh_global.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'yaml'
|
3
|
+
require 'libglade2'
|
3
4
|
|
4
5
|
module Sh
|
5
6
|
KEYS = {
|
@@ -8,8 +9,10 @@ module Sh
|
|
8
9
|
}
|
9
10
|
|
10
11
|
class Global
|
12
|
+
SUPPORTED_EXTENSIONS = ['.mp3', '.m4a', '.ogg', '.flac', '.wma', '.wav']
|
13
|
+
GLADE = {}
|
14
|
+
|
11
15
|
def initialize
|
12
|
-
#$KCODE = 'u'
|
13
16
|
# Won't work on Windows
|
14
17
|
$home = ENV['HOME']
|
15
18
|
$config_dir = "#{$home}/.shroom"
|
@@ -28,6 +31,10 @@ module Sh
|
|
28
31
|
}
|
29
32
|
|
30
33
|
Global.load_prefs
|
34
|
+
|
35
|
+
add_toplevel_glade('dlg_preferences')
|
36
|
+
add_toplevel_glade('dlg_song')
|
37
|
+
GLADE.freeze
|
31
38
|
end
|
32
39
|
|
33
40
|
def Global.load_prefs
|
@@ -48,6 +55,11 @@ module Sh
|
|
48
55
|
def Global.locate(file)
|
49
56
|
find_in_load_path("shroom-res/#{file}") || "shroom-res/#{file}"
|
50
57
|
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def add_toplevel_glade name
|
61
|
+
GLADE[name] = GladeXML.new(Global.locate('shroom.glade'), name)
|
62
|
+
end
|
51
63
|
end
|
52
64
|
end
|
53
65
|
|
data/lib/sh_mmkeys.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Sh
|
2
|
+
class MMKeys
|
3
|
+
def initialize &block
|
4
|
+
raise(ArgumentError, "No callback block provided") unless block
|
5
|
+
@supported = try_require('rubygems') and try_require('rbus')
|
6
|
+
|
7
|
+
if supported?
|
8
|
+
bus = RBus.session_bus
|
9
|
+
|
10
|
+
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
|
15
|
+
begin
|
16
|
+
@mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
|
17
|
+
@mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
|
18
|
+
@mmkeys.GrabMediaPlayerKeys('Shroom', 0)
|
19
|
+
rescue
|
20
|
+
# MediaKeys are not supported after all
|
21
|
+
@supported = false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Do we still think that MediaKeys are supported?
|
27
|
+
if supported? and @mmkeys
|
28
|
+
@mmkeys.connect!(:MediaPlayerKeyPressed) do |app, key|
|
29
|
+
if app.downcase == 'shroom'
|
30
|
+
block.call key
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def supported?
|
37
|
+
return @supported
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
@mmkeys.ReleaseMediaPlayerKeys('Shroom')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/sh_playlist.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sh
|
2
|
+
class Playlist
|
3
|
+
def initialize dir
|
4
|
+
@dir = dir
|
5
|
+
@songs = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def Playlist.parse(path)
|
9
|
+
list = Playlist.new(File.dirname(path))
|
10
|
+
open(path) do |f|
|
11
|
+
while line = f.gets
|
12
|
+
line.strip!
|
13
|
+
unless line[0] == ?# or line.empty?
|
14
|
+
ref = File.expand_path(line.gsub('\\', '/'), File.dirname(path))
|
15
|
+
if File.exists? ref
|
16
|
+
if Sh::Global::SUPPORTED_EXTENSIONS.include? File.extname(ref)
|
17
|
+
list << ($db.songs(:path => ref).first || Sh::Song.new(ref).read_tags!)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
puts "Not found: " + ref
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return list
|
26
|
+
end
|
27
|
+
|
28
|
+
def <<(song)
|
29
|
+
@songs << song
|
30
|
+
end
|
31
|
+
|
32
|
+
def songs
|
33
|
+
return @songs.dup
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/sh_song.rb
CHANGED
@@ -34,7 +34,12 @@ module Sh
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def lookup!
|
37
|
-
|
37
|
+
begin
|
38
|
+
track = lookup_multiple.first
|
39
|
+
rescue Exception
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
38
43
|
if track
|
39
44
|
# Don't distinguish between bands with the same name by adding to the name
|
40
45
|
track.artist.disambiguation = false
|
@@ -74,7 +79,8 @@ module Sh
|
|
74
79
|
query = Webservice::Query.new
|
75
80
|
filter = Webservice::TrackFilter.new(:artist => artist.name, :title => title, :puid => puids.first)
|
76
81
|
tracks = query.get_tracks(filter).entities
|
77
|
-
@matches = tracks || []
|
82
|
+
@matches = (tracks || [])
|
83
|
+
return @matches
|
78
84
|
end
|
79
85
|
|
80
86
|
public
|
data/lib/sh_tagreader.rb
CHANGED
@@ -17,20 +17,25 @@ module Sh
|
|
17
17
|
'.wma' => 'audio/x-ms-wma'
|
18
18
|
}[File.extname path]
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
begin
|
21
|
+
case @mime
|
22
|
+
when 'audio/mpeg'
|
23
|
+
metadata = TagReader.read_mp3 path
|
24
|
+
when 'audio/mp4'
|
25
|
+
metadata = TagReader.read_m4a path
|
26
|
+
when 'audio/ogg'
|
27
|
+
metadata = TagReader.read_ogg path
|
28
|
+
when 'audio/x-flac'
|
29
|
+
metadata = TagReader.read_flac path
|
30
|
+
when 'audio/x-ms-wma'
|
31
|
+
metadata = TagReader.read_wma path
|
32
|
+
when 'audio/x-wav'
|
33
|
+
metadata = TagReader.read_wave path
|
34
|
+
end
|
35
|
+
rescue Exception
|
36
|
+
puts "Error parsing tags for " + path
|
33
37
|
end
|
38
|
+
|
34
39
|
yield metadata if block
|
35
40
|
return metadata
|
36
41
|
end
|
data/lib/sh_util.rb
CHANGED
@@ -21,6 +21,54 @@ class Object
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
require 'digest/md5'
|
25
|
+
|
26
|
+
class String
|
27
|
+
def to_md5
|
28
|
+
return (Digest::MD5.new << self).to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class StringMatcher
|
33
|
+
def initialize str1, str2
|
34
|
+
@str1, @str2 = str1, str2
|
35
|
+
end
|
36
|
+
|
37
|
+
def compare
|
38
|
+
pairs1 = word_letter_pairs @str1
|
39
|
+
pairs2 = word_letter_pairs @str2
|
40
|
+
union = pairs1.size + pairs2.size
|
41
|
+
intersection = 0
|
42
|
+
pairs1.size.times do
|
43
|
+
intersection += 1 if pairs2.delete pairs1.pop
|
44
|
+
end
|
45
|
+
return (2 * intersection).to_f / union
|
46
|
+
end
|
47
|
+
|
48
|
+
def compare_ignore_case
|
49
|
+
compare @str1.upcase, @str2.upcase
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def letter_pairs str
|
54
|
+
n_pairs = str.length - 1
|
55
|
+
pairs = []
|
56
|
+
n_pairs.times do |i|
|
57
|
+
pairs[i] = str.slice(i, 2)
|
58
|
+
end
|
59
|
+
return pairs
|
60
|
+
end
|
61
|
+
|
62
|
+
def word_letter_pairs str
|
63
|
+
pairs = []
|
64
|
+
words = str.split(/\s/)
|
65
|
+
words.each do |word|
|
66
|
+
pairs.concat letter_pairs(word)
|
67
|
+
end
|
68
|
+
return pairs
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
24
72
|
module Kelp
|
25
73
|
class ProgressDialog < Gtk::Dialog
|
26
74
|
def initialize(title)
|
@@ -66,8 +114,8 @@ module Kelp
|
|
66
114
|
@image = Image.new(pixbuf)
|
67
115
|
vbox.pack_start @image, true, true, 8
|
68
116
|
if pixbuf
|
69
|
-
|
70
|
-
|
117
|
+
self.width_request = pixbuf.width + 16
|
118
|
+
self.height_request = size[1] + pixbuf.height + 16
|
71
119
|
end
|
72
120
|
vbox.show_all
|
73
121
|
signal_connect('response') { destroy }
|
data/lib/sh_view.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'sh_browse'
|
2
2
|
require 'sh_queue'
|
3
|
+
require 'sh_playlist'
|
3
4
|
require 'sh_lyrics'
|
4
5
|
require 'sh_cover_art'
|
5
|
-
require '
|
6
|
+
require 'sh_mmkeys'
|
6
7
|
require 'gtk2'
|
7
8
|
include Gtk
|
8
9
|
|
@@ -22,8 +23,6 @@ module Sh
|
|
22
23
|
|
23
24
|
Notify.init 'Shroom' if @rnotify
|
24
25
|
|
25
|
-
@glade= GladeXML.new(Global.locate('shroom.glade'))
|
26
|
-
|
27
26
|
if File.exists? $prefs[:library_dir]
|
28
27
|
cancelled = false
|
29
28
|
dialog = Kelp::ProgressDialog.new("Updating database...")
|
@@ -31,7 +30,7 @@ module Sh
|
|
31
30
|
new_songs = []
|
32
31
|
Dir[$prefs[:library_dir]+'/**/*'].each do |path|
|
33
32
|
ext = File.extname path
|
34
|
-
if
|
33
|
+
if Sh::Global::SUPPORTED_EXTENSIONS.include? ext
|
35
34
|
new_songs << Song.new(path) unless $db.contains? path
|
36
35
|
end
|
37
36
|
end
|
@@ -66,7 +65,7 @@ module Sh
|
|
66
65
|
@window.signal_connect('destroy') do
|
67
66
|
quit
|
68
67
|
end
|
69
|
-
|
68
|
+
|
70
69
|
@status_icon = StatusIcon.new
|
71
70
|
@status_icon.pixbuf = icon
|
72
71
|
@status_icon.visible = true
|
@@ -136,34 +135,10 @@ module Sh
|
|
136
135
|
end if @player
|
137
136
|
end
|
138
137
|
btn_next.signal_connect('clicked') do |btn|
|
139
|
-
|
140
|
-
playing = @player.playing?
|
141
|
-
@player.stop
|
142
|
-
@queue_pos += 1
|
143
|
-
if @queue_pos < @queue.size
|
144
|
-
prepare_song
|
145
|
-
play if playing
|
146
|
-
else
|
147
|
-
@queue_pos = 0
|
148
|
-
prepare_song
|
149
|
-
@btn_play.active = false
|
150
|
-
end
|
151
|
-
end
|
138
|
+
next_song
|
152
139
|
end
|
153
140
|
btn_prev.signal_connect('clicked') do |btn|
|
154
|
-
|
155
|
-
playing = @player.playing?
|
156
|
-
@player.stop
|
157
|
-
@queue_pos -= 1
|
158
|
-
if @queue_pos >= 0
|
159
|
-
prepare_song
|
160
|
-
play if playing
|
161
|
-
else
|
162
|
-
@queue_pos = 0
|
163
|
-
prepare_song
|
164
|
-
@btn_play.active = false
|
165
|
-
end
|
166
|
-
end
|
141
|
+
prev_song
|
167
142
|
end
|
168
143
|
|
169
144
|
# Horizontally split area
|
@@ -232,6 +207,32 @@ module Sh
|
|
232
207
|
i += 1
|
233
208
|
end
|
234
209
|
|
210
|
+
# Multimedia keys
|
211
|
+
MMKeys.new do |key|
|
212
|
+
case key
|
213
|
+
when 'Stop'
|
214
|
+
stop
|
215
|
+
when 'Next'
|
216
|
+
next_song
|
217
|
+
when 'Previous'
|
218
|
+
prev_song
|
219
|
+
when 'Play'
|
220
|
+
if playing?
|
221
|
+
pause
|
222
|
+
else
|
223
|
+
play
|
224
|
+
end
|
225
|
+
when 'PlayPause'
|
226
|
+
if playing?
|
227
|
+
pause
|
228
|
+
else
|
229
|
+
play
|
230
|
+
end
|
231
|
+
when 'Pause'
|
232
|
+
pause
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
235
236
|
# Show everything
|
236
237
|
@window.show_all
|
237
238
|
end
|
@@ -240,6 +241,7 @@ module Sh
|
|
240
241
|
def create_menu
|
241
242
|
menu_items = [
|
242
243
|
['/_File'],
|
244
|
+
['/File/Import playlist', '<StockItem>', '<control>I', Stock::OPEN, lambda {import_playlist}],
|
243
245
|
['/File/sep1', '<Separator>', nil, nil, lambda {}],
|
244
246
|
['/File/Quit', '<StockItem>', '<control>Q', Stock::QUIT, lambda {Gtk.main_quit}],
|
245
247
|
['/_Edit'],
|
@@ -254,20 +256,37 @@ module Sh
|
|
254
256
|
item_factory.get_widget('<main>')
|
255
257
|
end
|
256
258
|
|
259
|
+
def import_playlist
|
260
|
+
dialog = Gtk::FileChooserDialog.new("Open File",
|
261
|
+
@window,
|
262
|
+
Gtk::FileChooser::ACTION_OPEN,
|
263
|
+
nil,
|
264
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
265
|
+
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
266
|
+
|
267
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
268
|
+
stop
|
269
|
+
self.queue = Sh::Playlist.parse(dialog.filename).songs
|
270
|
+
play
|
271
|
+
end
|
272
|
+
dialog.destroy
|
273
|
+
end
|
274
|
+
|
257
275
|
def show_preferences
|
258
|
-
|
276
|
+
glade = Sh::Global::GLADE['dlg_preferences']
|
277
|
+
dlg_prefs = glade['dlg_preferences']
|
259
278
|
dlg_prefs.signal_connect('delete-event') do
|
260
279
|
dlg_prefs.hide
|
261
280
|
end
|
262
|
-
fbtn_library =
|
281
|
+
fbtn_library = glade['fbtn_library']
|
263
282
|
fbtn_library.current_folder = $prefs[:library_dir]
|
264
|
-
chk_lastfm =
|
283
|
+
chk_lastfm = glade['chk_lastfm']
|
265
284
|
chk_lastfm.active = $prefs[:lastfm]
|
266
|
-
txt_lastfm_user =
|
285
|
+
txt_lastfm_user = glade['txt_lastfm_user']
|
267
286
|
txt_lastfm_user.text = $prefs[:lastfm_user]
|
268
|
-
txt_lastfm_pass =
|
287
|
+
txt_lastfm_pass = glade['txt_lastfm_pass']
|
269
288
|
txt_lastfm_pass.text = $prefs[:lastfm_password]
|
270
|
-
btn_ok =
|
289
|
+
btn_ok = glade['btn_ok']
|
271
290
|
ok_handle = btn_ok.signal_connect('clicked') do
|
272
291
|
$prefs[:library_dir] = fbtn_library.filename
|
273
292
|
$prefs[:lastfm] = chk_lastfm.active?
|
@@ -276,16 +295,13 @@ module Sh
|
|
276
295
|
Sh::Global.save_prefs
|
277
296
|
dlg_prefs.hide
|
278
297
|
end
|
279
|
-
|
280
|
-
close_handle =
|
298
|
+
btn_cancel = glade['btn_cancel']
|
299
|
+
close_handle = btn_cancel.signal_connect('clicked') do
|
281
300
|
dlg_prefs.hide
|
282
301
|
end
|
283
|
-
dlg_prefs.
|
284
|
-
while dlg_prefs.visible?
|
285
|
-
Gtk.main_iteration
|
286
|
-
end
|
302
|
+
dlg_prefs.run
|
287
303
|
btn_ok.signal_handler_disconnect ok_handle
|
288
|
-
|
304
|
+
btn_cancel.signal_handler_disconnect close_handle
|
289
305
|
end
|
290
306
|
|
291
307
|
def show_about
|
@@ -305,18 +321,7 @@ module Sh
|
|
305
321
|
@player.demolish if @player
|
306
322
|
@player = Sh::Player.new @queue[@queue_pos]
|
307
323
|
@player.on_finished do |player|
|
308
|
-
|
309
|
-
@player.stop
|
310
|
-
@queue_pos += 1
|
311
|
-
if @queue_pos < @queue.size
|
312
|
-
prepare_song
|
313
|
-
play
|
314
|
-
else
|
315
|
-
@queue_pos = 0
|
316
|
-
prepare_song
|
317
|
-
@btn_play.active = false
|
318
|
-
end
|
319
|
-
end
|
324
|
+
next_song
|
320
325
|
end
|
321
326
|
on_song_changed
|
322
327
|
end
|
@@ -346,7 +351,13 @@ module Sh
|
|
346
351
|
# Cover art
|
347
352
|
if $prefs[:cover_art]
|
348
353
|
@pixbuf = nil
|
349
|
-
|
354
|
+
begin
|
355
|
+
if File.exists? song.album.image_path
|
356
|
+
@pixbuf = Gdk::Pixbuf.new(song.album.image_path)
|
357
|
+
end
|
358
|
+
rescue
|
359
|
+
@pixbuf = nil
|
360
|
+
end
|
350
361
|
if @pixbuf
|
351
362
|
@note.pixbuf_icon = @pixbuf.scale(48, 48) if @rnotify
|
352
363
|
@img_cover.pixbuf = @pixbuf.scale(132, 132)
|
@@ -411,18 +422,64 @@ module Sh
|
|
411
422
|
end
|
412
423
|
|
413
424
|
def play
|
414
|
-
stop
|
415
425
|
if @player
|
416
426
|
@btn_play.active = true
|
417
427
|
@player.play
|
418
428
|
end
|
419
429
|
end
|
420
430
|
|
431
|
+
def pause
|
432
|
+
if @player
|
433
|
+
@btn_play.active = false
|
434
|
+
@player.pause
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def playing?
|
439
|
+
return @player && @player.playing?
|
440
|
+
end
|
441
|
+
|
442
|
+
def paused?
|
443
|
+
return @player && @player.paused?
|
444
|
+
end
|
445
|
+
|
421
446
|
def stop
|
422
447
|
@btn_play.active = false
|
423
448
|
@player.stop if @player
|
424
449
|
end
|
425
450
|
|
451
|
+
def next_song
|
452
|
+
if @queue
|
453
|
+
playing = @player.playing?
|
454
|
+
@player.stop
|
455
|
+
@queue_pos += 1
|
456
|
+
if @queue_pos < @queue.size
|
457
|
+
prepare_song
|
458
|
+
play if playing
|
459
|
+
else
|
460
|
+
@queue_pos = 0
|
461
|
+
prepare_song
|
462
|
+
@btn_play.active = false
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def prev_song
|
468
|
+
if @queue
|
469
|
+
playing = @player.playing?
|
470
|
+
@player.stop
|
471
|
+
@queue_pos -= 1
|
472
|
+
if @queue_pos >= 0
|
473
|
+
prepare_song
|
474
|
+
play if playing
|
475
|
+
else
|
476
|
+
@queue_pos = 0
|
477
|
+
prepare_song
|
478
|
+
@btn_play.active = false
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
426
483
|
def show
|
427
484
|
Gtk.main
|
428
485
|
end
|
data/lib/shroom-res/shroom.glade
CHANGED
@@ -87,6 +87,7 @@
|
|
87
87
|
<packing>
|
88
88
|
<property name="left_attach">1</property>
|
89
89
|
<property name="right_attach">2</property>
|
90
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
90
91
|
</packing>
|
91
92
|
</child>
|
92
93
|
<child>
|
@@ -101,6 +102,7 @@
|
|
101
102
|
<property name="right_attach">2</property>
|
102
103
|
<property name="top_attach">1</property>
|
103
104
|
<property name="bottom_attach">2</property>
|
105
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
104
106
|
</packing>
|
105
107
|
</child>
|
106
108
|
<child>
|
@@ -152,6 +154,20 @@
|
|
152
154
|
<widget class="GtkHButtonBox" id="dialog-action_area1">
|
153
155
|
<property name="visible">True</property>
|
154
156
|
<property name="layout_style">end</property>
|
157
|
+
<child>
|
158
|
+
<widget class="GtkButton" id="btn_cancel">
|
159
|
+
<property name="label">gtk-cancel</property>
|
160
|
+
<property name="visible">True</property>
|
161
|
+
<property name="can_focus">True</property>
|
162
|
+
<property name="receives_default">True</property>
|
163
|
+
<property name="use_stock">True</property>
|
164
|
+
</widget>
|
165
|
+
<packing>
|
166
|
+
<property name="expand">False</property>
|
167
|
+
<property name="fill">False</property>
|
168
|
+
<property name="position">0</property>
|
169
|
+
</packing>
|
170
|
+
</child>
|
155
171
|
<child>
|
156
172
|
<widget class="GtkButton" id="btn_ok">
|
157
173
|
<property name="label">gtk-ok</property>
|
@@ -160,6 +176,235 @@
|
|
160
176
|
<property name="receives_default">True</property>
|
161
177
|
<property name="use_stock">True</property>
|
162
178
|
</widget>
|
179
|
+
<packing>
|
180
|
+
<property name="expand">False</property>
|
181
|
+
<property name="fill">False</property>
|
182
|
+
<property name="position">1</property>
|
183
|
+
</packing>
|
184
|
+
</child>
|
185
|
+
</widget>
|
186
|
+
<packing>
|
187
|
+
<property name="expand">False</property>
|
188
|
+
<property name="pack_type">end</property>
|
189
|
+
<property name="position">0</property>
|
190
|
+
</packing>
|
191
|
+
</child>
|
192
|
+
</widget>
|
193
|
+
</child>
|
194
|
+
</widget>
|
195
|
+
<widget class="GtkDialog" id="dlg_song">
|
196
|
+
<property name="border_width">5</property>
|
197
|
+
<property name="type_hint">normal</property>
|
198
|
+
<property name="has_separator">False</property>
|
199
|
+
<child internal-child="vbox">
|
200
|
+
<widget class="GtkVBox" id="dialog-vbox1">
|
201
|
+
<property name="visible">True</property>
|
202
|
+
<property name="orientation">vertical</property>
|
203
|
+
<property name="spacing">2</property>
|
204
|
+
<child>
|
205
|
+
<widget class="GtkTable" id="table1">
|
206
|
+
<property name="visible">True</property>
|
207
|
+
<property name="n_rows">5</property>
|
208
|
+
<property name="n_columns">3</property>
|
209
|
+
<property name="row_spacing">4</property>
|
210
|
+
<child>
|
211
|
+
<widget class="GtkLabel" id="lbl_title">
|
212
|
+
<property name="visible">True</property>
|
213
|
+
<property name="label" translatable="yes">Title:</property>
|
214
|
+
</widget>
|
215
|
+
<packing>
|
216
|
+
<property name="x_options"></property>
|
217
|
+
<property name="y_options"></property>
|
218
|
+
</packing>
|
219
|
+
</child>
|
220
|
+
<child>
|
221
|
+
<widget class="GtkEntry" id="txt_title">
|
222
|
+
<property name="visible">True</property>
|
223
|
+
<property name="can_focus">True</property>
|
224
|
+
<property name="invisible_char">●</property>
|
225
|
+
</widget>
|
226
|
+
<packing>
|
227
|
+
<property name="left_attach">1</property>
|
228
|
+
<property name="right_attach">3</property>
|
229
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
230
|
+
<property name="y_options"></property>
|
231
|
+
</packing>
|
232
|
+
</child>
|
233
|
+
<child>
|
234
|
+
<widget class="GtkButton" id="btn_add_album">
|
235
|
+
<property name="label" translatable="yes">gtk-add</property>
|
236
|
+
<property name="visible">True</property>
|
237
|
+
<property name="can_focus">True</property>
|
238
|
+
<property name="receives_default">True</property>
|
239
|
+
<property name="use_stock">True</property>
|
240
|
+
</widget>
|
241
|
+
<packing>
|
242
|
+
<property name="left_attach">2</property>
|
243
|
+
<property name="right_attach">3</property>
|
244
|
+
<property name="top_attach">2</property>
|
245
|
+
<property name="bottom_attach">3</property>
|
246
|
+
<property name="x_options">GTK_FILL</property>
|
247
|
+
<property name="y_options"></property>
|
248
|
+
</packing>
|
249
|
+
</child>
|
250
|
+
<child>
|
251
|
+
<widget class="GtkComboBox" id="cmb_album">
|
252
|
+
<property name="visible">True</property>
|
253
|
+
<property name="active">0</property>
|
254
|
+
<property name="items" translatable="yes">Unknown</property>
|
255
|
+
</widget>
|
256
|
+
<packing>
|
257
|
+
<property name="left_attach">1</property>
|
258
|
+
<property name="right_attach">2</property>
|
259
|
+
<property name="top_attach">2</property>
|
260
|
+
<property name="bottom_attach">3</property>
|
261
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
262
|
+
<property name="y_options"></property>
|
263
|
+
</packing>
|
264
|
+
</child>
|
265
|
+
<child>
|
266
|
+
<widget class="GtkLabel" id="lbl_album">
|
267
|
+
<property name="visible">True</property>
|
268
|
+
<property name="label" translatable="yes">Album:</property>
|
269
|
+
</widget>
|
270
|
+
<packing>
|
271
|
+
<property name="top_attach">2</property>
|
272
|
+
<property name="bottom_attach">3</property>
|
273
|
+
<property name="x_options"></property>
|
274
|
+
<property name="y_options"></property>
|
275
|
+
</packing>
|
276
|
+
</child>
|
277
|
+
<child>
|
278
|
+
<widget class="GtkLabel" id="lbl_artist">
|
279
|
+
<property name="visible">True</property>
|
280
|
+
<property name="label" translatable="yes">Artist:</property>
|
281
|
+
</widget>
|
282
|
+
<packing>
|
283
|
+
<property name="top_attach">1</property>
|
284
|
+
<property name="bottom_attach">2</property>
|
285
|
+
<property name="x_options"></property>
|
286
|
+
<property name="y_options"></property>
|
287
|
+
</packing>
|
288
|
+
</child>
|
289
|
+
<child>
|
290
|
+
<widget class="GtkComboBox" id="cmb_artist">
|
291
|
+
<property name="visible">True</property>
|
292
|
+
<property name="active">0</property>
|
293
|
+
<property name="items" translatable="yes">Unknown</property>
|
294
|
+
</widget>
|
295
|
+
<packing>
|
296
|
+
<property name="left_attach">1</property>
|
297
|
+
<property name="right_attach">2</property>
|
298
|
+
<property name="top_attach">1</property>
|
299
|
+
<property name="bottom_attach">2</property>
|
300
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
301
|
+
<property name="y_options"></property>
|
302
|
+
</packing>
|
303
|
+
</child>
|
304
|
+
<child>
|
305
|
+
<widget class="GtkButton" id="btn_add_artist">
|
306
|
+
<property name="label" translatable="yes">gtk-add</property>
|
307
|
+
<property name="visible">True</property>
|
308
|
+
<property name="can_focus">True</property>
|
309
|
+
<property name="receives_default">True</property>
|
310
|
+
<property name="use_stock">True</property>
|
311
|
+
</widget>
|
312
|
+
<packing>
|
313
|
+
<property name="left_attach">2</property>
|
314
|
+
<property name="right_attach">3</property>
|
315
|
+
<property name="top_attach">1</property>
|
316
|
+
<property name="bottom_attach">2</property>
|
317
|
+
<property name="x_options">GTK_FILL</property>
|
318
|
+
<property name="y_options"></property>
|
319
|
+
</packing>
|
320
|
+
</child>
|
321
|
+
<child>
|
322
|
+
<widget class="GtkLabel" id="lbl_image">
|
323
|
+
<property name="visible">True</property>
|
324
|
+
<property name="label" translatable="yes">Image:</property>
|
325
|
+
</widget>
|
326
|
+
<packing>
|
327
|
+
<property name="top_attach">3</property>
|
328
|
+
<property name="bottom_attach">4</property>
|
329
|
+
<property name="x_options"></property>
|
330
|
+
<property name="y_options"></property>
|
331
|
+
</packing>
|
332
|
+
</child>
|
333
|
+
<child>
|
334
|
+
<widget class="GtkImage" id="img_image">
|
335
|
+
<property name="visible">True</property>
|
336
|
+
<property name="stock">gtk-missing-image</property>
|
337
|
+
</widget>
|
338
|
+
<packing>
|
339
|
+
<property name="left_attach">1</property>
|
340
|
+
<property name="right_attach">2</property>
|
341
|
+
<property name="top_attach">3</property>
|
342
|
+
<property name="bottom_attach">4</property>
|
343
|
+
</packing>
|
344
|
+
</child>
|
345
|
+
<child>
|
346
|
+
<widget class="GtkVBox" id="vbox1">
|
347
|
+
<property name="visible">True</property>
|
348
|
+
<property name="orientation">vertical</property>
|
349
|
+
<child>
|
350
|
+
<widget class="GtkCheckButton" id="chk_image">
|
351
|
+
<property name="label" translatable="yes">Separate image</property>
|
352
|
+
<property name="visible">True</property>
|
353
|
+
<property name="can_focus">True</property>
|
354
|
+
<property name="receives_default">False</property>
|
355
|
+
<property name="draw_indicator">True</property>
|
356
|
+
</widget>
|
357
|
+
<packing>
|
358
|
+
<property name="expand">False</property>
|
359
|
+
<property name="fill">False</property>
|
360
|
+
<property name="position">0</property>
|
361
|
+
</packing>
|
362
|
+
</child>
|
363
|
+
<child>
|
364
|
+
<widget class="GtkFileChooserButton" id="fbtn_image">
|
365
|
+
<property name="visible">True</property>
|
366
|
+
<property name="title" translatable="yes">Select A Image</property>
|
367
|
+
</widget>
|
368
|
+
<packing>
|
369
|
+
<property name="expand">False</property>
|
370
|
+
<property name="fill">False</property>
|
371
|
+
<property name="position">1</property>
|
372
|
+
</packing>
|
373
|
+
</child>
|
374
|
+
</widget>
|
375
|
+
<packing>
|
376
|
+
<property name="left_attach">2</property>
|
377
|
+
<property name="right_attach">3</property>
|
378
|
+
<property name="top_attach">3</property>
|
379
|
+
<property name="bottom_attach">4</property>
|
380
|
+
</packing>
|
381
|
+
</child>
|
382
|
+
<child>
|
383
|
+
<placeholder/>
|
384
|
+
</child>
|
385
|
+
<child>
|
386
|
+
<placeholder/>
|
387
|
+
</child>
|
388
|
+
<child>
|
389
|
+
<placeholder/>
|
390
|
+
</child>
|
391
|
+
</widget>
|
392
|
+
<packing>
|
393
|
+
<property name="position">1</property>
|
394
|
+
</packing>
|
395
|
+
</child>
|
396
|
+
<child internal-child="action_area">
|
397
|
+
<widget class="GtkHButtonBox" id="dialog-action_area1">
|
398
|
+
<property name="visible">True</property>
|
399
|
+
<property name="layout_style">end</property>
|
400
|
+
<child>
|
401
|
+
<widget class="GtkButton" id="btn_cancel">
|
402
|
+
<property name="label" translatable="yes">gtk-cancel</property>
|
403
|
+
<property name="visible">True</property>
|
404
|
+
<property name="can_focus">True</property>
|
405
|
+
<property name="receives_default">True</property>
|
406
|
+
<property name="use_stock">True</property>
|
407
|
+
</widget>
|
163
408
|
<packing>
|
164
409
|
<property name="expand">False</property>
|
165
410
|
<property name="fill">False</property>
|
@@ -167,8 +412,8 @@
|
|
167
412
|
</packing>
|
168
413
|
</child>
|
169
414
|
<child>
|
170
|
-
<widget class="GtkButton" id="
|
171
|
-
<property name="label">gtk-
|
415
|
+
<widget class="GtkButton" id="btn_ok">
|
416
|
+
<property name="label" translatable="yes">gtk-ok</property>
|
172
417
|
<property name="visible">True</property>
|
173
418
|
<property name="can_focus">True</property>
|
174
419
|
<property name="receives_default">True</property>
|
@@ -190,4 +435,96 @@
|
|
190
435
|
</widget>
|
191
436
|
</child>
|
192
437
|
</widget>
|
438
|
+
<widget class="GtkDialog" id="dlg_artist">
|
439
|
+
<property name="border_width">5</property>
|
440
|
+
<property name="type_hint">normal</property>
|
441
|
+
<property name="has_separator">False</property>
|
442
|
+
<child internal-child="vbox">
|
443
|
+
<widget class="GtkVBox" id="dialog-vbox3">
|
444
|
+
<property name="visible">True</property>
|
445
|
+
<property name="orientation">vertical</property>
|
446
|
+
<property name="spacing">2</property>
|
447
|
+
<child>
|
448
|
+
<widget class="GtkTable" id="table1">
|
449
|
+
<property name="visible">True</property>
|
450
|
+
<property name="n_rows">2</property>
|
451
|
+
<property name="n_columns">3</property>
|
452
|
+
<property name="row_spacing">4</property>
|
453
|
+
<child>
|
454
|
+
<widget class="GtkLabel" id="lbl_name">
|
455
|
+
<property name="visible">True</property>
|
456
|
+
<property name="label" translatable="yes">Name:</property>
|
457
|
+
</widget>
|
458
|
+
<packing>
|
459
|
+
<property name="x_options"></property>
|
460
|
+
<property name="y_options"></property>
|
461
|
+
</packing>
|
462
|
+
</child>
|
463
|
+
<child>
|
464
|
+
<widget class="GtkEntry" id="entry1">
|
465
|
+
<property name="visible">True</property>
|
466
|
+
<property name="can_focus">True</property>
|
467
|
+
<property name="invisible_char">●</property>
|
468
|
+
</widget>
|
469
|
+
<packing>
|
470
|
+
<property name="left_attach">1</property>
|
471
|
+
<property name="right_attach">3</property>
|
472
|
+
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
|
473
|
+
<property name="y_options"></property>
|
474
|
+
</packing>
|
475
|
+
</child>
|
476
|
+
<child>
|
477
|
+
<placeholder/>
|
478
|
+
</child>
|
479
|
+
<child>
|
480
|
+
<placeholder/>
|
481
|
+
</child>
|
482
|
+
<child>
|
483
|
+
<placeholder/>
|
484
|
+
</child>
|
485
|
+
</widget>
|
486
|
+
<packing>
|
487
|
+
<property name="position">1</property>
|
488
|
+
</packing>
|
489
|
+
</child>
|
490
|
+
<child internal-child="action_area">
|
491
|
+
<widget class="GtkHButtonBox" id="dialog-action_area3">
|
492
|
+
<property name="visible">True</property>
|
493
|
+
<property name="layout_style">end</property>
|
494
|
+
<child>
|
495
|
+
<widget class="GtkButton" id="button2">
|
496
|
+
<property name="label" translatable="yes">button</property>
|
497
|
+
<property name="visible">True</property>
|
498
|
+
<property name="can_focus">True</property>
|
499
|
+
<property name="receives_default">True</property>
|
500
|
+
</widget>
|
501
|
+
<packing>
|
502
|
+
<property name="expand">False</property>
|
503
|
+
<property name="fill">False</property>
|
504
|
+
<property name="position">0</property>
|
505
|
+
</packing>
|
506
|
+
</child>
|
507
|
+
<child>
|
508
|
+
<widget class="GtkButton" id="button1">
|
509
|
+
<property name="label" translatable="yes">button</property>
|
510
|
+
<property name="visible">True</property>
|
511
|
+
<property name="can_focus">True</property>
|
512
|
+
<property name="receives_default">True</property>
|
513
|
+
</widget>
|
514
|
+
<packing>
|
515
|
+
<property name="expand">False</property>
|
516
|
+
<property name="fill">False</property>
|
517
|
+
<property name="position">1</property>
|
518
|
+
</packing>
|
519
|
+
</child>
|
520
|
+
</widget>
|
521
|
+
<packing>
|
522
|
+
<property name="expand">False</property>
|
523
|
+
<property name="pack_type">end</property>
|
524
|
+
<property name="position">0</property>
|
525
|
+
</packing>
|
526
|
+
</child>
|
527
|
+
</widget>
|
528
|
+
</child>
|
529
|
+
</widget>
|
193
530
|
</glade-interface>
|
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.
|
4
|
+
version: 0.0.5
|
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-
|
12
|
+
date: 2009-07-31 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- lib/sh_main.rb
|
132
132
|
- lib/sh_queue.rb
|
133
133
|
- lib/sh_album.rb
|
134
|
+
- lib/sh_playlist.rb
|
134
135
|
- lib/sh_song.rb
|
135
136
|
- lib/sh_artist.rb
|
136
137
|
- lib/sh_cover_art.rb
|
@@ -138,6 +139,7 @@ files:
|
|
138
139
|
- lib/sh_lyrics.rb
|
139
140
|
- lib/sh_database.rb
|
140
141
|
- lib/sh_util.rb
|
142
|
+
- lib/sh_mmkeys.rb
|
141
143
|
- lib/shroom-res
|
142
144
|
- lib/shroom-res/icon_16x16.png
|
143
145
|
- lib/shroom-res/icon.svg
|
@@ -165,6 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
167
|
version: "0"
|
166
168
|
version:
|
167
169
|
requirements:
|
170
|
+
- rake
|
168
171
|
- libgnome2-ruby
|
169
172
|
- libsqlite3-ruby
|
170
173
|
- libglade2-ruby
|