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 CHANGED
@@ -4,9 +4,31 @@ Shroom
4
4
  Dependencies
5
5
  ------------
6
6
 
7
- ruby-gnome2
8
- ruby-gstreamer
9
- ruby-sqlite3
7
+ 'rake'
8
+ 'libwxgtk2.8-0'
9
+ 'libgnome2-ruby'
10
+ 'libglade2-ruby'
11
+ 'libgtkhtml2-ruby'
12
+ 'libsqlite3-ruby'
13
+ 'ruby-dev'
14
+ 'libopenssl-ruby'
15
+ 'libmp3lame-dev'
16
+ 'libvorbis-dev'
17
+ 'build-essential'
18
+
19
+ Gem dependencies
20
+ ----------------
21
+
22
+ ('ruby-mp3info', '>= 0.6.13')
23
+ ('MP4Info', '>= 0.3.3')
24
+ ('ruby-ogginfo', '>= 0.3.2')
25
+ ('flacinfo-rb', '>= 0.4')
26
+ ('earworm', '>= 0.0.2')
27
+ ('sequel', '>= 3.2.0')
28
+ ('wxruby', '>= 2.0.0')
29
+ ('rbrainz', '>= 0.5.0')
30
+ ('scrobbler', '>= 0.2.3')
31
+ ('shared-mime-info', '>= 0.1')
10
32
 
11
33
  Repository
12
34
  ----------
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.9'
10
+ s.version = '0.0.10'
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'
@@ -0,0 +1,146 @@
1
+ module Sh
2
+ class Actions
3
+ @@actions = []
4
+ attr_reader :name, :tags, :user_data
5
+
6
+ def initialize name, *tags, &block
7
+ @@actions << self
8
+ @name = name
9
+ @tags = tags
10
+ @block = block
11
+ @user_data = {}
12
+ end
13
+
14
+ def []=(key, value)
15
+ @user_data[key] = value
16
+ end
17
+
18
+ def [](key)
19
+ return @user_data[key]
20
+ end
21
+
22
+ def execute *args
23
+ @block.call *args
24
+ end
25
+
26
+ def self.get *tags
27
+ matches = []
28
+ @@actions.each do |action|
29
+ subset = true
30
+ tags.each do |tag|
31
+ subset = false unless action.tags.include? tag
32
+ end
33
+ matches << action if subset
34
+ end
35
+ return matches
36
+ end
37
+ end
38
+
39
+ class ShroomActions
40
+ def self.init
41
+ action = Actions.new 'Add to queue', :song do |song|
42
+ view = Sh::View.instance
43
+ if view
44
+ queue = view.queue
45
+ if queue
46
+ queue << song
47
+ else
48
+ view.queue = [song]
49
+ view.queue_pos = 0
50
+ end
51
+ end
52
+ end
53
+ action[:stock_image] = Stock::ADD
54
+
55
+ action = Actions.new 'Reread tags', :song do |song|
56
+ browse = Sh::Browse.instance
57
+ browse.remove_song song if browse
58
+ song.read_tags!(true)
59
+ $db.save_song song
60
+ browse.insert_song song if browse
61
+ end
62
+ action[:stock_image] = Stock::HARDDISK
63
+
64
+ action = Actions.new 'Lookup metadata automatically', :song do |song|
65
+ if song.lookup!
66
+ browse = Sh::Browse.instance
67
+ browse.remove_song song if browse
68
+ $db.save_song song
69
+ browse.insert_song song if browse
70
+ end
71
+ end
72
+ action[:stock_image] = Stock::FIND
73
+
74
+ action = Actions.new 'Add to playlist', :song, :playlist do |song, playlist|
75
+ playlist << song
76
+ playlist.save!
77
+ end
78
+ action[:stock_image] = Stock::ADD
79
+
80
+ action = Actions.new 'Properties', :song do |song|
81
+ show_song_properties_dialog(song)
82
+ end
83
+ action[:stock_image] = Stock::PROPERTIES
84
+ end
85
+
86
+ def self.show_song_properties_dialog song
87
+ glade = Global::GLADE['dlg_song']
88
+ dlg_song = glade['dlg_song']
89
+ txt_title = glade['txt_title']
90
+ txt_title.text = song.title
91
+
92
+ # Artists combo box
93
+ sto_artists = ListStore.new(String, Artist)
94
+ cmb_artist = glade['cmb_artist']
95
+ sel = 0
96
+ $db.artists.sort_by{|a| (a.name || '')}.each_with_index do |artist, i|
97
+ iter = sto_artists.append
98
+ iter[0] = artist.name
99
+ iter[1] = artist
100
+ sel = i if song.artist && artist.db_id == song.artist.db_id
101
+ end
102
+ cmb_artist.model = sto_artists
103
+ cmb_artist.active = sel
104
+
105
+ # Albums combo box
106
+ sto_albums = ListStore.new(String, Album)
107
+ cmb_album = glade['cmb_album']
108
+ sel = 0
109
+ $db.albums.sort_by{|a| (a.title || '')}.each_with_index do |album, i|
110
+ iter = sto_albums.append
111
+ iter[0] = album.title
112
+ iter[1] = album
113
+ sel = i if song.album && album.db_id == song.album.db_id
114
+ end
115
+ cmb_album.model = sto_albums
116
+ cmb_album.active = sel
117
+
118
+ glade['chk_image'].sensitive = false
119
+ glade['fbtn_image'].sensitive = false
120
+ glade['btn_add_artist'].sensitive = false
121
+ glade['btn_add_album'].sensitive = false
122
+
123
+ dlg_song.signal_connect('delete-event') do
124
+ dlg_song.hide
125
+ end
126
+ btn_ok = glade['btn_ok']
127
+ ok_handle = btn_ok.signal_connect('clicked') do
128
+ browse = Sh::Browse.instance
129
+ song.title = txt_title.text
130
+ song.artist = cmb_artist.active_iter[1]
131
+ song.album = cmb_album.active_iter[1]
132
+ browse.remove_song song if browse
133
+ $db.save_song song
134
+ browse.insert_song song if browse
135
+ dlg_song.hide
136
+ end
137
+ btn_cancel = glade['btn_cancel']
138
+ close_handle = btn_cancel.signal_connect('clicked') do
139
+ dlg_song.hide
140
+ end
141
+ dlg_song.run
142
+ btn_ok.signal_handler_disconnect ok_handle
143
+ btn_cancel.signal_handler_disconnect close_handle
144
+ end
145
+ end
146
+ end
@@ -1,6 +1,5 @@
1
1
  module Sh
2
2
  class Album
3
- attr_writer :artist
4
3
  attr_accessor :title, :mbid, :image_path, :info, :date
5
4
  attr_accessor :db_id
6
5
 
@@ -8,13 +7,8 @@ module Sh
8
7
  @songs = []
9
8
  end
10
9
 
11
- def artist
12
- #@artist = $db.artists(:id => @artist.db_id).first if @artist and @artist.db_id
13
- return @artist
14
- end
15
-
16
10
  def to_s
17
11
  return self.title
18
12
  end
19
13
  end
20
- end
14
+ end
@@ -1,6 +1,10 @@
1
1
  module Sh
2
2
  class Browse
3
+ @@instance = nil
4
+
3
5
  def initialize view
6
+ Sh::Log.warning 'Instance of Browse already created!' if @@instance
7
+ @@instance = self
4
8
  @tree = TreeView.new
5
9
  @tree.selection.mode = Gtk::SELECTION_MULTIPLE
6
10
 
@@ -22,6 +26,13 @@ module Sh
22
26
  @tree.append_column col_artist
23
27
 
24
28
  @model = TreeStore.new(Object, Array)
29
+ # TODO: use this
30
+ # @model.set_default_sort_func do |a, b|
31
+ # oa, ob = a[0], b[0]
32
+ # oa.to_s <=> ob.to_s
33
+ # end
34
+ # @model.set_sort_column_id(TreeSortable::DEFAULT_SORT_COLUMN_ID,
35
+ # Gtk::SORT_ASCENDING)
25
36
  @tree.model = @model
26
37
 
27
38
  GLib::Timeout.add(1000) do
@@ -54,42 +65,31 @@ module Sh
54
65
  if data.is_a? Sh::Song
55
66
  song = data
56
67
  menu = Menu.new
57
-
58
- pbtn_add_to_queue = ImageMenuItem.new Stock::ADD
59
- menu.append pbtn_add_to_queue
60
- pbtn_add_to_queue.signal_connect('activate') do |widget|
61
- queue = view.queue
62
- if queue
63
- queue << song
64
- else
65
- view.queue = [song]
66
- view.queue_pos = 0
67
- end
68
- end
69
-
70
- pbtn_lookup = MenuItem.new 'Lookup metadata automatically'
71
- menu.append pbtn_lookup
72
- pbtn_lookup.signal_connect('activate') do |widget|
73
- if song.lookup!
74
- remove_song @model.get_iter(path)
75
- $db.save_song song
76
- insert_song song
77
- end
78
- end
79
-
80
- pbtn_reread_tags = MenuItem.new 'Reread tags'
81
- menu.append pbtn_reread_tags
82
- pbtn_reread_tags.signal_connect('activate') do |widget|
83
- remove_song @model.get_iter(path)
84
- song.read_tags!(true)
85
- $db.save_song song
86
- insert_song song
87
- end
88
68
 
89
- pbtn_properties = ImageMenuItem.new Stock::PROPERTIES
90
- menu.append pbtn_properties
91
- pbtn_properties.signal_connect('activate') do |widget|
92
- show_song_properties_dialog(song)
69
+ Actions.get(:song).each do |action|
70
+ pbtn = nil
71
+ if action[:stock_image]
72
+ pbtn = ImageMenuItem.new(action.name)
73
+ pbtn.image = Image.new(action[:stock_image], IconSize::MENU)
74
+ else
75
+ pbtn = MenuItem.new(action.name)
76
+ end
77
+ menu.append pbtn
78
+ if action.tags.include? :playlist
79
+ pmnu = Gtk::Menu.new
80
+ Sh::Playlists.load_playlists.each do |playlist|
81
+ pbtn_playlist = Gtk::MenuItem.new playlist.name
82
+ pbtn_playlist.signal_connect('activate') do |widget|
83
+ action.execute song, playlist
84
+ end
85
+ pmnu.append pbtn_playlist
86
+ end
87
+ pbtn.submenu = pmnu
88
+ else
89
+ pbtn.signal_connect('activate') do |widget|
90
+ action.execute song
91
+ end
92
+ end
93
93
  end
94
94
 
95
95
  menu.show_all
@@ -129,64 +129,9 @@ module Sh
129
129
  @scroll.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
130
130
  @scroll.add @tree
131
131
  end
132
-
133
- def show_song_properties_dialog song
134
- glade = Global::GLADE['dlg_song']
135
- dlg_song = glade['dlg_song']
136
- txt_title = glade['txt_title']
137
- txt_title.text = song.title
138
-
139
- # Artists combo box
140
- sto_artists = ListStore.new(String, Artist)
141
- cmb_artist = glade['cmb_artist']
142
- sel = 0
143
- $db.artists.sort_by{|a| (a.name || '')}.each_with_index do |artist, i|
144
- iter = sto_artists.append
145
- iter[0] = artist.name
146
- iter[1] = artist
147
- sel = i if song.artist && artist.db_id == song.artist.db_id
148
- end
149
- cmb_artist.model = sto_artists
150
- cmb_artist.active = sel
151
-
152
- # Albums combo box
153
- sto_albums = ListStore.new(String, Album)
154
- cmb_album = glade['cmb_album']
155
- sel = 0
156
- $db.albums.sort_by{|a| (a.title || '')}.each_with_index do |album, i|
157
- iter = sto_albums.append
158
- iter[0] = album.title
159
- iter[1] = album
160
- sel = i if song.album && album.db_id == song.album.db_id
161
- end
162
- cmb_album.model = sto_albums
163
- cmb_album.active = sel
164
-
165
- glade['chk_image'].sensitive = false
166
- glade['fbtn_image'].sensitive = false
167
- glade['btn_add_artist'].sensitive = false
168
- glade['btn_add_album'].sensitive = false
169
-
170
- dlg_song.signal_connect('delete-event') do
171
- dlg_song.hide
172
- end
173
- btn_ok = glade['btn_ok']
174
- ok_handle = btn_ok.signal_connect('clicked') do
175
- song.title = txt_title.text
176
- song.artist = cmb_artist.active_iter[1]
177
- song.album = cmb_album.active_iter[1]
178
- remove_song song
179
- $db.save_song song
180
- insert_song song
181
- dlg_song.hide
182
- end
183
- btn_cancel = glade['btn_cancel']
184
- close_handle = btn_cancel.signal_connect('clicked') do
185
- dlg_song.hide
186
- end
187
- dlg_song.run
188
- btn_ok.signal_handler_disconnect ok_handle
189
- btn_cancel.signal_handler_disconnect close_handle
132
+
133
+ def self.instance
134
+ return @@instance
190
135
  end
191
136
 
192
137
  def remove_song song
@@ -250,6 +195,7 @@ module Sh
250
195
  end
251
196
 
252
197
  def fill_model
198
+ @model.clear
253
199
  artist_node = album_node = track_node = nil
254
200
  $db.songs.sort_by {|a| (a.to_s || '')}.each do |song|
255
201
  artist = song.artist
@@ -279,7 +225,7 @@ module Sh
279
225
  end
280
226
 
281
227
  def widget
282
- @scroll
228
+ return @scroll
283
229
  end
284
230
  end
285
- end
231
+ end
@@ -1,35 +1,28 @@
1
- require 'open-uri'
2
1
  require 'rexml/document'
3
- require 'cgi'
4
2
 
5
3
  module Sh
6
4
  class CoverArt
7
- def CoverArt.get_cover song
5
+ def self.get_cover song
8
6
  artist, album = song.artist.name, song.album.title
9
- path = "#{$cover_dir}/#{artist.to_md5}_#{album.to_md5}"
10
- if Ping.pingecho("audioscrobbler.com", 5) and not File.exists?(path)
11
- doc = lastfm("album.getInfo", {:artist => artist, :album => album})
12
- img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
13
- if img_url
14
- open(path, 'w') do |output|
15
- open(img_url) do |input|
16
- output << input.read
7
+ if artist and album
8
+ @lastfm_rest ||= Rest::Get.new("http://ws.audioscrobbler.com/2.0",
9
+ :api_key => Sh::KEYS[:lastfm], :method => "album.getInfo")
10
+ path = "#{$cover_dir}/#{artist.to_md5}_#{album.to_md5}"
11
+ if File.exists? path and File.size?(path) > 0
12
+ return path
13
+ elsif Ping.pingecho("audioscrobbler.com", 5)
14
+ doc = REXML::Document.new(@lastfm_rest[:artist => artist, :album => album])
15
+ img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
16
+ if img_url
17
+ open(path, 'w') do |output|
18
+ open(img_url) do |input|
19
+ output << input.read
20
+ end
17
21
  end
18
22
  end
19
23
  end
24
+ return path if File.exists? path
20
25
  end
21
- if File.exists? path
22
- return path
23
- else
24
- return nil
25
- end
26
- end
27
-
28
- private
29
- def CoverArt.lastfm(method, arg_map = {}.freeze)
30
- args = arg_map.collect { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}"}.join('&')
31
- url = "http://ws.audioscrobbler.com/2.0/?method=#{method}&api_key=#{Sh::KEYS[:lastfm]}&#{args}"
32
- return REXML::Document.new(open(url).read)
33
26
  end
34
27
  end
35
28
  end
@@ -0,0 +1,54 @@
1
+ module Sh
2
+ class CoverBrowse
3
+ COL_PIXBUF, COL_ALBUM = (0..1).to_a
4
+
5
+ def initialize view
6
+ @model = ListStore.new(Gdk::Pixbuf, Object)
7
+ @model.set_default_sort_func do |a, b|
8
+ a[COL_ALBUM].title <=> b[COL_ALBUM].title
9
+ end
10
+ @model.set_sort_column_id(TreeSortable::DEFAULT_SORT_COLUMN_ID,
11
+ Gtk::SORT_ASCENDING)
12
+
13
+ @iconview = IconView.new @model
14
+ @iconview.pixbuf_column = COL_PIXBUF
15
+ @iconview.signal_connect 'item_activated' do |iconview, path|
16
+ iter = @model.get_iter path
17
+ album = iter[COL_ALBUM]
18
+ if album
19
+ view.stop
20
+ songs = $db.songs(:album_id => album.db_id)
21
+ view.queue = songs.sort_by {|s| (s.to_s || '')}
22
+ view.play
23
+ end
24
+ end
25
+
26
+ GLib::Timeout.add(1000) do
27
+ fill_model
28
+ false
29
+ end
30
+
31
+ @scroll = ScrolledWindow.new(nil, nil)
32
+ @scroll.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
33
+ @scroll.add @iconview
34
+ end
35
+
36
+ def fill_model
37
+ @model.clear
38
+ $db.albums.each do |album|
39
+ begin
40
+ pixbuf = Gdk::Pixbuf.new(album.image_path).scale(128, 128)
41
+ iter = @model.append
42
+ iter[COL_ALBUM] = album
43
+ iter[COL_PIXBUF] = pixbuf
44
+ rescue Exception
45
+ # Insufficient data about album
46
+ end if album
47
+ end
48
+ end
49
+
50
+ def widget
51
+ return @scroll
52
+ end
53
+ end
54
+ end