shroom 0.0.9 → 0.0.10
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/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
|