shroom 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README +25 -3
- data/Rakefile +1 -1
- data/lib/sh_actions.rb +146 -0
- data/lib/sh_album.rb +1 -7
- data/lib/sh_browse.rb +41 -95
- data/lib/sh_cover_art.rb +16 -23
- data/lib/sh_cover_browse.rb +54 -0
- data/lib/sh_database.rb +4 -4
- data/lib/sh_global.rb +39 -22
- data/lib/sh_log.rb +78 -0
- data/lib/sh_lyrics.rb +2 -55
- data/lib/sh_main.rb +10 -6
- data/lib/sh_player.rb +2 -4
- data/lib/sh_playlist.rb +91 -6
- data/lib/sh_plugin.rb +31 -7
- data/lib/sh_song.rb +13 -10
- data/lib/sh_tagreader.rb +59 -32
- data/lib/sh_util.rb +33 -68
- data/lib/sh_view.rb +106 -76
- data/lib/shroom-res/plugins/lastfm_scrobbler.rb +65 -21
- data/lib/shroom-res/shroom.glade +1 -95
- metadata +5 -2
data/lib/sh_plugin.rb
CHANGED
@@ -4,8 +4,7 @@ module Sh
|
|
4
4
|
class_eval do
|
5
5
|
names.each do |name|
|
6
6
|
define_method(name) do |*args|
|
7
|
-
|
8
|
-
when 0
|
7
|
+
if args.size == 0
|
9
8
|
instance_variable_get("@#{name}")
|
10
9
|
else
|
11
10
|
instance_variable_set("@#{name}", *args)
|
@@ -18,27 +17,52 @@ module Sh
|
|
18
17
|
|
19
18
|
class Plugin
|
20
19
|
@registered_plugins = {}
|
20
|
+
@active_plugins = {}
|
21
|
+
|
22
|
+
def self.init
|
23
|
+
begin
|
24
|
+
@plugin_prefs = YAML.load_file(Sh::Global::PATHS[:plugin_prefs_file])
|
25
|
+
rescue
|
26
|
+
@plugin_prefs = {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
21
30
|
class << self
|
22
|
-
attr_reader :registered_plugins
|
31
|
+
attr_reader :registered_plugins, :active_plugins
|
23
32
|
private :new
|
24
33
|
end
|
25
34
|
|
26
35
|
def self.define(id_name, &block)
|
27
36
|
p = new
|
37
|
+
p.id_name id_name
|
28
38
|
p.instance_eval(&block)
|
29
|
-
|
39
|
+
p.init @plugin_prefs[id_name] if p.respond_to? :init
|
40
|
+
registered_plugins[id_name] = p
|
41
|
+
active_plugins[id_name] = p
|
30
42
|
end
|
31
43
|
|
32
44
|
def self.broadcast(method_name, *args)
|
33
|
-
|
45
|
+
active_plugins.each_value do |plugin|
|
34
46
|
if plugin.respond_to? method_name
|
35
47
|
Thread.new {plugin.send method_name, *args}
|
36
48
|
end
|
37
49
|
end
|
38
50
|
end
|
39
51
|
|
52
|
+
def self.save_prefs
|
53
|
+
prefs = {}
|
54
|
+
registered_plugins.each do |id, plugin|
|
55
|
+
if plugin.respond_to? :preferences
|
56
|
+
prefs[id] = plugin.preferences
|
57
|
+
end
|
58
|
+
end
|
59
|
+
open(Sh::Global::PATHS[:plugin_prefs_file], "w") do |f|
|
60
|
+
f.write prefs.to_yaml
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
40
64
|
extend PluginSugar
|
41
65
|
|
42
|
-
def_field :name, :description, :author, :version
|
66
|
+
def_field :name, :description, :author, :version, :id_name
|
43
67
|
end
|
44
|
-
end
|
68
|
+
end
|
data/lib/sh_song.rb
CHANGED
@@ -3,9 +3,10 @@ require 'sh_tagreader'
|
|
3
3
|
require 'sh_album'
|
4
4
|
require 'sh_artist'
|
5
5
|
require 'cgi'
|
6
|
-
|
7
|
-
|
8
|
-
include MusicBrainz
|
6
|
+
try_require 'earworm'
|
7
|
+
if try_require 'rbrainz'
|
8
|
+
include MusicBrainz
|
9
|
+
end
|
9
10
|
|
10
11
|
module Sh
|
11
12
|
class Song
|
@@ -21,12 +22,10 @@ module Sh
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def album
|
24
|
-
#@album = $db.albums(:id => @album.db_id).first if @album and @album.db_id
|
25
25
|
return @album
|
26
26
|
end
|
27
27
|
|
28
28
|
def artist
|
29
|
-
#@artist = $db.artists(:id => @artist.db_id).first if @artist and @artist.db_id
|
30
29
|
return @artist
|
31
30
|
end
|
32
31
|
|
@@ -35,7 +34,7 @@ module Sh
|
|
35
34
|
end
|
36
35
|
|
37
36
|
def duration
|
38
|
-
@duration ||= Sh::TagReader.read(path)[:duration]
|
37
|
+
return (@duration ||= Sh::TagReader.read(path)[:duration])
|
39
38
|
end
|
40
39
|
|
41
40
|
def lookup!
|
@@ -55,10 +54,13 @@ module Sh
|
|
55
54
|
album.mbid = rel.id.to_mbid.uuid
|
56
55
|
# Determine track number
|
57
56
|
query = Webservice::Query.new
|
58
|
-
filter = Webservice::TrackFilter.new(:artistid => artist.mbid,
|
57
|
+
filter = Webservice::TrackFilter.new(:artistid => artist.mbid,
|
58
|
+
:releaseid => album.mbid)
|
59
59
|
tracks = query.get_tracks(filter).entities
|
60
60
|
tracks.to_a.each_with_index do |t, i|
|
61
|
-
|
61
|
+
if t.title == track.title and t.duration == track.duration
|
62
|
+
self.track_num = i + 1
|
63
|
+
end
|
62
64
|
end
|
63
65
|
rescue Exception
|
64
66
|
return false
|
@@ -73,7 +75,7 @@ module Sh
|
|
73
75
|
ew = Earworm::Client.new(Sh::KEYS[:music_dns])
|
74
76
|
begin
|
75
77
|
info = ew.identify(:file => path)
|
76
|
-
rescue
|
78
|
+
rescue Exception
|
77
79
|
printf "Couldn't generate fingerprint for %s\n> %s\n\n", path, $!
|
78
80
|
return
|
79
81
|
end
|
@@ -84,7 +86,8 @@ module Sh
|
|
84
86
|
return if not title or not artist.name
|
85
87
|
# Get more information from MusicBrainz
|
86
88
|
query = Webservice::Query.new
|
87
|
-
filter = Webservice::TrackFilter.new(:artist => artist.name,
|
89
|
+
filter = Webservice::TrackFilter.new(:artist => artist.name,
|
90
|
+
:title => title, :puid => puids.first)
|
88
91
|
tracks = query.get_tracks(filter).entities
|
89
92
|
@matches = (tracks || [])
|
90
93
|
return @matches
|
data/lib/sh_tagreader.rb
CHANGED
@@ -1,22 +1,42 @@
|
|
1
|
+
require 'sh_util'
|
2
|
+
require 'sh_log'
|
3
|
+
|
1
4
|
module Sh
|
2
5
|
class TagReader
|
3
|
-
def
|
6
|
+
def self.read path, &block
|
7
|
+
# Ensure that the path is absolute
|
4
8
|
@path = File.expand_path path
|
5
|
-
metadata = {}
|
6
9
|
|
10
|
+
# Use shared-mime-info to determine MIME types if
|
11
|
+
# it is available.
|
7
12
|
if try_require 'shared-mime-info'
|
8
|
-
type = MIME.check(path)
|
13
|
+
type = MIME.check(@path)
|
9
14
|
@mime = type.to_s if type.media == 'audio'
|
15
|
+
else
|
16
|
+
# Attempt to use the `file` command
|
17
|
+
begin
|
18
|
+
type = `file --mime-type -b #{@path}`
|
19
|
+
@mime = type if type[0..5] == 'audio/'
|
20
|
+
rescue Exception
|
21
|
+
@mime = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# As a fallback, guess MIME type from file extension.
|
25
|
+
if not @mime or @mime.empty?
|
26
|
+
@mime = {
|
27
|
+
'.mp3' => 'audio/mpeg',
|
28
|
+
'.m4a' => 'audio/mp4',
|
29
|
+
'.flac' => 'audio/x-flac',
|
30
|
+
'.ogg' => 'audio/ogg',
|
31
|
+
'.wav' => 'audio/x-wav',
|
32
|
+
'.wma' => 'audio/x-ms-wma'
|
33
|
+
}[File.extname @path]
|
34
|
+
end
|
10
35
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
'.ogg' => 'audio/ogg',
|
16
|
-
'.wav' => 'audio/x-wav',
|
17
|
-
'.wma' => 'audio/x-ms-wma'
|
18
|
-
}[File.extname path]
|
19
|
-
|
36
|
+
|
37
|
+
metadata = {}
|
38
|
+
|
39
|
+
# Choose appropriate method for MIME type
|
20
40
|
begin
|
21
41
|
case @mime
|
22
42
|
when 'audio/mpeg'
|
@@ -33,14 +53,16 @@ module Sh
|
|
33
53
|
metadata = TagReader.read_wave path
|
34
54
|
end
|
35
55
|
rescue Exception
|
36
|
-
|
56
|
+
Log.warning "Couldn't parse tags for " + path
|
37
57
|
end
|
38
|
-
|
58
|
+
|
59
|
+
# Report on our metadata findings
|
39
60
|
yield metadata if block
|
40
61
|
return metadata
|
41
62
|
end
|
42
|
-
|
43
|
-
|
63
|
+
|
64
|
+
# Read metadata from MP3 file
|
65
|
+
def self.read_mp3 path, &block
|
44
66
|
metadata = {}
|
45
67
|
if try_require 'mp3info'
|
46
68
|
Mp3Info.open path do |mp3|
|
@@ -53,13 +75,14 @@ module Sh
|
|
53
75
|
metadata[:duration] = mp3.length
|
54
76
|
end
|
55
77
|
else
|
56
|
-
|
78
|
+
Log.info 'Please install the "mp3info" gem in order to read MP3 info'
|
57
79
|
end
|
58
80
|
yield metadata if block
|
59
81
|
return metadata
|
60
82
|
end
|
61
|
-
|
62
|
-
|
83
|
+
|
84
|
+
# Read metadata from M4A file
|
85
|
+
def self.read_m4a path, &block
|
63
86
|
metadata = {}
|
64
87
|
if try_require 'mp4info'
|
65
88
|
mp4 = MP4Info.open path
|
@@ -70,13 +93,14 @@ module Sh
|
|
70
93
|
metadata[:track_num] = mp4.TRKN.first.to_i
|
71
94
|
metadata[:duration] = mp4.SECS
|
72
95
|
else
|
73
|
-
|
96
|
+
Log.info 'Please install the "mp4info" gem in order to read M4A info'
|
74
97
|
end
|
75
98
|
yield metadata if block
|
76
99
|
return metadata
|
77
100
|
end
|
78
|
-
|
79
|
-
|
101
|
+
|
102
|
+
# Read metadata from OGG Vorbis file
|
103
|
+
def self.read_ogg path, &block
|
80
104
|
metadata = {}
|
81
105
|
if try_require 'ogginfo'
|
82
106
|
OggInfo.open path do |ogg|
|
@@ -89,13 +113,14 @@ module Sh
|
|
89
113
|
metadata[:duration] = ogg.length
|
90
114
|
end rescue Exception
|
91
115
|
else
|
92
|
-
|
116
|
+
Log.info 'Please install the "ogginfo" gem in order to read OGG info'
|
93
117
|
end
|
94
118
|
yield metadata if block
|
95
119
|
return metadata
|
96
120
|
end
|
97
|
-
|
98
|
-
|
121
|
+
|
122
|
+
# Read metadata from FLAC file
|
123
|
+
def self.read_flac path, &block
|
99
124
|
metadata = {}
|
100
125
|
if try_require 'flacinfo'
|
101
126
|
flac = FlacInfo.new(path)
|
@@ -118,13 +143,14 @@ module Sh
|
|
118
143
|
end
|
119
144
|
end
|
120
145
|
else
|
121
|
-
|
146
|
+
Log.info 'Please install the "flacinfo" gem in order to read FLAC info'
|
122
147
|
end
|
123
148
|
yield metadata if block
|
124
149
|
return metadata
|
125
150
|
end
|
126
|
-
|
127
|
-
|
151
|
+
|
152
|
+
# Read metadata from WMA file
|
153
|
+
def self.read_wma path, &block
|
128
154
|
metadata = {}
|
129
155
|
if try_require 'wmainfo'
|
130
156
|
wma = WmaInfo.new(path)
|
@@ -146,19 +172,20 @@ module Sh
|
|
146
172
|
end
|
147
173
|
metadata[:duration] = wma.info["playtime_seconds"]
|
148
174
|
else
|
149
|
-
|
175
|
+
Log.info 'Please install the "wmainfo" gem in order to read WMA info'
|
150
176
|
end
|
151
177
|
yield metadata if block
|
152
178
|
return metadata
|
153
179
|
end
|
154
|
-
|
155
|
-
|
180
|
+
|
181
|
+
# Read metadata from Wave file
|
182
|
+
def self.read_wave path, &block
|
156
183
|
metadata = {}
|
157
184
|
if try_require 'waveinfo'
|
158
185
|
wave = WaveInfo.new path
|
159
186
|
metadata[:duration] = wave.duration
|
160
187
|
else
|
161
|
-
|
188
|
+
Log.info 'Please install the "waveinfo" gem in order to read WAV info'
|
162
189
|
end
|
163
190
|
yield metadata if block
|
164
191
|
return metadata
|
data/lib/sh_util.rb
CHANGED
@@ -21,82 +21,47 @@ class Object
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
require '
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
begin
|
32
|
-
raise "Binary data!" if is_binary_data?
|
33
|
-
utf8 = isutf8
|
34
|
-
if utf8
|
35
|
-
utf8 = utf8.to_s
|
36
|
-
else
|
37
|
-
utf8 = unpack('C*').pack('U*')
|
38
|
-
end
|
39
|
-
return utf8
|
40
|
-
rescue Exception
|
41
|
-
return nil
|
24
|
+
require 'open-uri'
|
25
|
+
module Rest
|
26
|
+
class Get
|
27
|
+
def initialize(url, params={})
|
28
|
+
@url = url.dup
|
29
|
+
@query = ''
|
30
|
+
@query = build_query params
|
42
31
|
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class File
|
47
|
-
def empty? path
|
48
|
-
return size? path
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class StringMatcher
|
53
|
-
def initialize str1, str2
|
54
|
-
@str1, @str2 = str1, str2
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.compare(str1, str2)
|
58
|
-
return StringMatcher.new(str1, str2).compare
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.compare_ignore_case(str1, str2)
|
62
|
-
return StringMatcher.new(str1, str2).compare_ignore_case
|
63
|
-
end
|
64
32
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
pairs2 = word_letter_pairs @str2
|
69
|
-
union = pairs1.size + pairs2.size
|
70
|
-
intersection = 0
|
71
|
-
pairs1.size.times do
|
72
|
-
intersection += 1 if pairs2.delete pairs1.pop
|
33
|
+
def [](params={})
|
34
|
+
query = build_query params
|
35
|
+
open(@url + query).read
|
73
36
|
end
|
74
|
-
return (2 * intersection).to_f / union
|
75
|
-
end
|
76
37
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
38
|
+
private
|
39
|
+
def build_query(params)
|
40
|
+
query = @query
|
41
|
+
params.each do |key, value|
|
42
|
+
query << (query.empty? ? '?' : '&')
|
43
|
+
query << CGI.escape(key.to_s)
|
44
|
+
query << '='
|
45
|
+
query << CGI.escape(value.to_s)
|
46
|
+
end
|
47
|
+
return query
|
48
|
+
end
|
81
49
|
end
|
50
|
+
end
|
82
51
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
n_pairs.times do |i|
|
88
|
-
pairs[i] = str.slice(i, 2)
|
89
|
-
end
|
90
|
-
return pairs
|
52
|
+
require 'digest/md5'
|
53
|
+
class String
|
54
|
+
def to_md5
|
55
|
+
return (Digest::MD5.new << self).to_s
|
91
56
|
end
|
92
57
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
58
|
+
def to_u
|
59
|
+
if is_binary_data?
|
60
|
+
return isutf8 ? self : nil
|
61
|
+
else
|
62
|
+
utf8 = isutf8
|
63
|
+
return utf8 ? utf8.to_s : unpack('C*').pack('U*')
|
98
64
|
end
|
99
|
-
return pairs
|
100
65
|
end
|
101
66
|
end
|
102
67
|
|
@@ -175,7 +140,7 @@ module Kelp
|
|
175
140
|
end
|
176
141
|
end
|
177
142
|
|
178
|
-
def
|
143
|
+
def self.process_events
|
179
144
|
Gtk.main_iteration while Gtk.events_pending?
|
180
145
|
end
|
181
146
|
end
|
data/lib/sh_view.rb
CHANGED
@@ -1,23 +1,32 @@
|
|
1
1
|
require 'sh_browse'
|
2
|
+
require 'sh_cover_browse'
|
2
3
|
require 'sh_queue'
|
3
4
|
require 'sh_playlist'
|
4
5
|
require 'sh_lyrics'
|
5
6
|
require 'sh_cover_art'
|
6
7
|
require 'sh_mmkeys'
|
8
|
+
require 'sh_actions'
|
7
9
|
require 'gtk2'
|
8
10
|
require 'gtkhtml2'
|
9
11
|
include Gtk
|
10
12
|
|
11
13
|
module Sh
|
12
14
|
class View
|
13
|
-
TAB_QUEUE, TAB_BROWSE, TAB_LYRICS, NUM_TABS =
|
15
|
+
TAB_QUEUE, TAB_BROWSE, TAB_LYRICS, TAB_COVER_BROWSE, TAB_PLAYLISTS, NUM_TABS = (0..5).to_a
|
14
16
|
TAB_NAMES = {
|
15
17
|
TAB_QUEUE => 'Queue',
|
16
18
|
TAB_BROWSE => 'Browse',
|
17
|
-
TAB_LYRICS => 'Lyrics'
|
19
|
+
TAB_LYRICS => 'Lyrics',
|
20
|
+
TAB_COVER_BROWSE => 'Cover Browse',
|
21
|
+
TAB_PLAYLISTS => 'Playlists'
|
18
22
|
}
|
23
|
+
|
24
|
+
@@instance = nil
|
19
25
|
|
20
26
|
def initialize
|
27
|
+
Sh::Log.warning 'Instance of Browse already created!' if @@instance
|
28
|
+
@@instance = self
|
29
|
+
|
21
30
|
# Initialize GTK
|
22
31
|
Gtk.init
|
23
32
|
|
@@ -52,16 +61,17 @@ module Sh
|
|
52
61
|
new_songs.each do |song|
|
53
62
|
# Show path to song in the dialog
|
54
63
|
dialog.message = song.path
|
55
|
-
|
64
|
+
|
56
65
|
begin
|
57
66
|
# Read metadata from song tags
|
58
67
|
song.read_tags!
|
59
68
|
# Save song to the database
|
60
69
|
$db.save_song song
|
61
|
-
#
|
70
|
+
Sh::Log.debug "Added #{song.path} to database"
|
62
71
|
rescue
|
63
|
-
|
72
|
+
Sh::Log.info "Failed to add ${song.path} to database", $!
|
64
73
|
end
|
74
|
+
# Increment progress bar
|
65
75
|
dialog.fraction += inc
|
66
76
|
# Make sure that the GUI is updated
|
67
77
|
Kelp.process_events
|
@@ -80,6 +90,8 @@ module Sh
|
|
80
90
|
# Clean up
|
81
91
|
new_songs = dialog = nil
|
82
92
|
end
|
93
|
+
|
94
|
+
Sh::Log.debug 'Synchronized library dir with database'
|
83
95
|
|
84
96
|
# Load small 16x16 pixel Shroom logo
|
85
97
|
icon = Gdk::Pixbuf.new Global.locate('icon_16x16.png')
|
@@ -218,9 +230,14 @@ module Sh
|
|
218
230
|
scr_lyrics = ScrolledWindow.new(nil, nil)
|
219
231
|
scr_lyrics.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
|
220
232
|
scr_lyrics.add @html_lyrics
|
221
|
-
|
222
|
-
|
233
|
+
box_lyrics = VBox.new false, 0
|
234
|
+
box_lyrics.add scr_lyrics
|
235
|
+
@btn_refresh_lyrics = Button.new Stock::REFRESH
|
236
|
+
box_lyrics.pack_start @btn_refresh_lyrics, false, false
|
223
237
|
queue = Sh::Queue.new self
|
238
|
+
browse = Sh::Browse.new self
|
239
|
+
cover_browse = Sh::CoverBrowse.new self
|
240
|
+
playlists = Sh::Playlists.new self
|
224
241
|
lst_tabs.selection.signal_connect('changed') do |selection|
|
225
242
|
frm_content.remove frm_content.child if frm_content.child
|
226
243
|
case selection.selected.path.indices[0]
|
@@ -230,11 +247,15 @@ module Sh
|
|
230
247
|
when TAB_BROWSE
|
231
248
|
frm_content.child = browse.widget
|
232
249
|
when TAB_LYRICS
|
233
|
-
frm_content.child =
|
250
|
+
frm_content.child = box_lyrics
|
251
|
+
when TAB_COVER_BROWSE
|
252
|
+
frm_content.child = cover_browse.widget
|
253
|
+
when TAB_PLAYLISTS
|
254
|
+
frm_content.child = playlists.widget
|
234
255
|
end
|
235
256
|
frm_content.show_all
|
236
257
|
end
|
237
|
-
# Select 'Browse' tab
|
258
|
+
# Select 'Browse' tab on startup
|
238
259
|
i = 0
|
239
260
|
lst_tabs.model.each do |row|
|
240
261
|
lst_tabs.selection.select_path row[1] if i == TAB_BROWSE
|
@@ -250,13 +271,7 @@ module Sh
|
|
250
271
|
next_song
|
251
272
|
when 'Previous'
|
252
273
|
prev_song
|
253
|
-
when 'Play'
|
254
|
-
if playing?
|
255
|
-
pause
|
256
|
-
else
|
257
|
-
play
|
258
|
-
end
|
259
|
-
when 'PlayPause'
|
274
|
+
when 'Play', 'PlayPause'
|
260
275
|
if playing?
|
261
276
|
pause
|
262
277
|
else
|
@@ -266,10 +281,16 @@ module Sh
|
|
266
281
|
pause
|
267
282
|
end
|
268
283
|
end
|
284
|
+
|
285
|
+
ShroomActions.init
|
269
286
|
|
270
287
|
# Show everything
|
271
288
|
@window.show_all
|
272
289
|
end
|
290
|
+
|
291
|
+
def self.instance
|
292
|
+
return @@instance
|
293
|
+
end
|
273
294
|
|
274
295
|
private
|
275
296
|
def create_menu
|
@@ -333,6 +354,21 @@ module Sh
|
|
333
354
|
else
|
334
355
|
sto_plugins = lst_plugins.model = ListStore.new(Object, Plugin)
|
335
356
|
ren_enabled = CellRendererToggle.new
|
357
|
+
ren_enabled.activatable = true
|
358
|
+
ren_enabled.signal_connect('toggled') do |w, path|
|
359
|
+
iter = sto_plugins.get_iter(path)
|
360
|
+
iter[0] = !iter[0]
|
361
|
+
plugin = iter[1]
|
362
|
+
if iter[0]
|
363
|
+
unless Plugin.active_plugins[plugin.id_name]
|
364
|
+
Plugin.active_plugins[plugin.id_name] = plugin
|
365
|
+
end
|
366
|
+
else
|
367
|
+
if Plugin.active_plugins[plugin.id_name]
|
368
|
+
Plugin.active_plugins.delete plugin.id_name
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
336
372
|
col_enabled = TreeViewColumn.new('Enabled',
|
337
373
|
ren_enabled,
|
338
374
|
'active' => 0)
|
@@ -351,19 +387,26 @@ module Sh
|
|
351
387
|
lbl_name.markup = "<b>#{plugin.name}</b>"
|
352
388
|
lbl_version.markup = "<i>#{plugin.version}</i>"
|
353
389
|
txt_description.buffer.text = plugin.description
|
354
|
-
btn_preferences.sensitive = plugin.respond_to? :
|
390
|
+
btn_preferences.sensitive = plugin.respond_to? :edit_preferences
|
391
|
+
|
392
|
+
btn_preferences.signal_handler_disconnect @prefs_hnd_id if @prefs_hnd_id
|
393
|
+
@prefs_hnd_id = btn_preferences.signal_connect('clicked') do |*args|
|
394
|
+
plugin.edit_preferences
|
395
|
+
end
|
355
396
|
end
|
356
397
|
end
|
357
398
|
end
|
358
399
|
|
359
400
|
Plugin.registered_plugins.each do |name, plugin|
|
360
401
|
iter = sto_plugins.append
|
361
|
-
iter[0] =
|
402
|
+
iter[0] = Plugin.active_plugins[plugin.id_name] == plugin
|
362
403
|
iter[1] = plugin
|
363
404
|
lst_plugins.selection.select_iter iter if lst_plugins.selection.count_selected_rows == 0
|
364
405
|
end
|
365
406
|
|
366
407
|
dlg_plugins.run
|
408
|
+
|
409
|
+
Plugin.save_prefs
|
367
410
|
end
|
368
411
|
|
369
412
|
def show_preferences
|
@@ -374,18 +417,9 @@ module Sh
|
|
374
417
|
end
|
375
418
|
fbtn_library = glade['fbtn_library']
|
376
419
|
fbtn_library.current_folder = Global.prefs[:library_dir]
|
377
|
-
chk_lastfm = glade['chk_lastfm']
|
378
|
-
chk_lastfm.active = Global.prefs[:lastfm]
|
379
|
-
txt_lastfm_user = glade['txt_lastfm_user']
|
380
|
-
txt_lastfm_user.text = Global.prefs[:lastfm_user]
|
381
|
-
txt_lastfm_pass = glade['txt_lastfm_pass']
|
382
|
-
txt_lastfm_pass.text = Global.prefs[:lastfm_password]
|
383
420
|
btn_ok = glade['btn_ok']
|
384
421
|
ok_handle = btn_ok.signal_connect('clicked') do
|
385
422
|
Global.prefs[:library_dir] = fbtn_library.filename
|
386
|
-
Global.prefs[:lastfm] = chk_lastfm.active?
|
387
|
-
Global.prefs[:lastfm_user] = txt_lastfm_user.text
|
388
|
-
Global.prefs[:lastfm_password] = txt_lastfm_pass.text
|
389
423
|
Sh::Global.save_prefs
|
390
424
|
dlg_prefs.hide
|
391
425
|
end
|
@@ -428,6 +462,7 @@ module Sh
|
|
428
462
|
def on_song_changed
|
429
463
|
if @player
|
430
464
|
song = @player.song
|
465
|
+
Sh::Log.debug "Playback started for \"#{song.title}\""
|
431
466
|
@note.close if @note
|
432
467
|
@note = nil
|
433
468
|
if @rnotify
|
@@ -436,42 +471,8 @@ module Sh
|
|
436
471
|
@note = Notify::Notification.new(song.title || 'Unknown track', msg, nil, @status_icon)
|
437
472
|
end
|
438
473
|
# Cover art
|
439
|
-
|
440
|
-
|
441
|
-
begin
|
442
|
-
image_path = song.album.image_path
|
443
|
-
@pixbuf = Gdk::Pixbuf.new(image_path) unless File.empty? image_path
|
444
|
-
rescue
|
445
|
-
@pixbuf = nil
|
446
|
-
end
|
447
|
-
|
448
|
-
if @pixbuf
|
449
|
-
@note.pixbuf_icon = @pixbuf.scale(48, 48) if @rnotify
|
450
|
-
@img_cover.pixbuf = @pixbuf.scale(128, 128)
|
451
|
-
else
|
452
|
-
@img_cover.pixbuf = Gdk::Pixbuf.new(Global.locate("cover_unavailable.png")).scale(132, 132)
|
453
|
-
Thread.new do
|
454
|
-
song.album.image_path = Sh::CoverArt.get_cover(song)
|
455
|
-
if song.album.image_path
|
456
|
-
$db.save_song song
|
457
|
-
# Show cover unless requests have been shuffled
|
458
|
-
if song == @player.song
|
459
|
-
@pixbuf = nil
|
460
|
-
(@pixbuf = Gdk::Pixbuf.new(song.album.image_path)) rescue Exception
|
461
|
-
if @pixbuf
|
462
|
-
if @rnotify
|
463
|
-
@note.close
|
464
|
-
@note.pixbuf_icon = @pixbuf.scale(48, 48)
|
465
|
-
@note.show
|
466
|
-
end
|
467
|
-
@img_cover.pixbuf = @pixbuf.scale(128, 128)
|
468
|
-
plasmarize_background @pixbuf
|
469
|
-
end
|
470
|
-
end
|
471
|
-
end
|
472
|
-
end
|
473
|
-
end
|
474
|
-
end
|
474
|
+
@pixbuf = nil
|
475
|
+
Thread.new {update_image song} if Global.prefs[:cover_art]
|
475
476
|
# Lyrics
|
476
477
|
if Global.prefs[:lyrics]
|
477
478
|
@html_lyrics.set_html 'Loading...'
|
@@ -504,26 +505,54 @@ module Sh
|
|
504
505
|
%{
|
505
506
|
<h2>#{song.title}</h2>
|
506
507
|
<pre style='font-family: sans-serif;'>#{lyrics}</pre>
|
507
|
-
<p></p><a href="#reload">Reload lyrics</a>
|
508
508
|
}
|
509
509
|
end
|
510
|
-
|
511
|
-
|
512
|
-
doc.signal_handler_disconnect(@html_signal_id)
|
510
|
+
if @html_signal_id and @btn_refresh_lyrics.signal_handler_is_connected?(@html_signal_id)
|
511
|
+
@btn_refresh_lyrics.signal_handler_disconnect(@html_signal_id)
|
513
512
|
end
|
514
|
-
@html_signal_id =
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
513
|
+
@html_signal_id = @btn_refresh_lyrics.signal_connect('pressed') do |*args|
|
514
|
+
@html_lyrics.set_html 'Loading...'
|
515
|
+
Thread.new do
|
516
|
+
song.lyrics = Sh::Lyrics.get_lyrics(song)
|
517
|
+
$db.save_song song
|
518
|
+
show_lyrics song if @player and @player.song == song
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def update_image song
|
524
|
+
image_path = song.album.image_path
|
525
|
+
# Use image path in database if valid
|
526
|
+
if image_path and File.size? image_path
|
527
|
+
(@pixbuf = Gdk::Pixbuf.new(image_path)) rescue Exception
|
528
|
+
end
|
529
|
+
# Lookup cover art on Internet if necessary
|
530
|
+
unless @pixbuf
|
531
|
+
@img_cover.pixbuf = Gdk::Pixbuf.new(Global.locate("cover_unavailable.png")).scale(132, 132)
|
532
|
+
image_path = Sh::CoverArt.get_cover(song)
|
533
|
+
if image_path
|
534
|
+
begin
|
535
|
+
@pixbuf = Gdk::Pixbuf.new(image_path)
|
536
|
+
song.album.image_path = image_path
|
519
537
|
$db.save_song song
|
520
|
-
|
538
|
+
rescue
|
521
539
|
end
|
522
540
|
end
|
523
541
|
end
|
542
|
+
# Show cover image unless requests have been shuffled
|
543
|
+
if @pixbuf and song == @player.song
|
544
|
+
if @rnotify
|
545
|
+
@note.close
|
546
|
+
@note.pixbuf_icon = @pixbuf.scale(48, 48)
|
547
|
+
@note.show
|
548
|
+
end
|
549
|
+
@img_cover.pixbuf = @pixbuf.scale(128, 128)
|
550
|
+
plasmarize_background @pixbuf
|
551
|
+
end
|
524
552
|
end
|
525
553
|
|
526
554
|
def plasmarize_background image_path
|
555
|
+
return unless image_path
|
527
556
|
begin
|
528
557
|
pixbuf = nil
|
529
558
|
if image_path.is_a? Gdk::Pixbuf
|
@@ -545,14 +574,14 @@ module Sh
|
|
545
574
|
if color != @plasma_color
|
546
575
|
@plasma_color = color.dup
|
547
576
|
require 'sh_plasma'
|
548
|
-
pixmap = Plasma.new(
|
577
|
+
pixmap = Plasma.new(512, 512, 3).generate_pixmap(color)
|
549
578
|
style = @window.style.copy
|
550
579
|
style.set_bg_pixmap(Gtk::STATE_NORMAL, pixmap)
|
551
580
|
@window.set_style style
|
552
581
|
end
|
553
582
|
end
|
554
583
|
rescue
|
555
|
-
|
584
|
+
Sh::Log.info "Plasma generation failed", $!
|
556
585
|
end
|
557
586
|
end
|
558
587
|
|
@@ -632,6 +661,7 @@ module Sh
|
|
632
661
|
end
|
633
662
|
|
634
663
|
def show
|
664
|
+
Sh::Log.debug 'Starting GTK main loop'
|
635
665
|
Gtk.main
|
636
666
|
end
|
637
667
|
|
@@ -639,4 +669,4 @@ module Sh
|
|
639
669
|
Gtk.main_quit
|
640
670
|
end
|
641
671
|
end
|
642
|
-
end
|
672
|
+
end
|