groove-dl 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/MANIFEST +4 -8
- data/bin/groove-dl +4 -9
- data/glade/groove-dl.glade +539 -0
- data/groove-dl.gemspec +4 -4
- data/lib/groove-dl/app.rb +16 -43
- data/lib/groove-dl/cli/search.rb +1 -7
- data/lib/groove-dl/displayer.rb +9 -9
- data/lib/groove-dl/downloader.rb +10 -10
- data/lib/groove-dl/version.rb +1 -1
- data/lib/groove-dl/widgets/download.rb +231 -0
- data/lib/groove-dl/widgets/events.rb +23 -0
- data/lib/groove-dl/widgets/search.rb +77 -0
- data/spec/groove-dl/displayer_spec.rb +6 -5
- data/spec/groove-dl/downloader_spec.rb +9 -9
- metadata +10 -14
- data/lib/groove-dl/widgets/download/bar.rb +0 -96
- data/lib/groove-dl/widgets/download/book.rb +0 -77
- data/lib/groove-dl/widgets/download/list/failed.rb +0 -75
- data/lib/groove-dl/widgets/download/list/queue.rb +0 -171
- data/lib/groove-dl/widgets/download/list/success.rb +0 -83
- data/lib/groove-dl/widgets/menu/success.rb +0 -32
- data/lib/groove-dl/widgets/search/bar.rb +0 -74
- data/lib/groove-dl/widgets/search/list.rb +0 -151
@@ -1,171 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
module GrooveDl
|
3
|
-
# Widgets components
|
4
|
-
module Widgets
|
5
|
-
# Download section
|
6
|
-
module Download
|
7
|
-
# List page
|
8
|
-
module List
|
9
|
-
# Queue tree
|
10
|
-
class Queue < Gtk::Box
|
11
|
-
attr_reader :store, :data
|
12
|
-
attr_writer :downloader, :client
|
13
|
-
attr_accessor :failed, :success, :queue
|
14
|
-
|
15
|
-
COLUMN_PATH,
|
16
|
-
COLUMN_PGBAR_VALUE,
|
17
|
-
COLUMN_PGBAR_TEXT = *(0..2).to_a
|
18
|
-
|
19
|
-
##
|
20
|
-
# Initialize widgets
|
21
|
-
#
|
22
|
-
# @param [Grooveshark::Client] client Grooveshark client
|
23
|
-
# @param [Gtk::Window] window Gtk app
|
24
|
-
#
|
25
|
-
def load(client, window)
|
26
|
-
@success = 0
|
27
|
-
@failed = 0
|
28
|
-
@queue = 0
|
29
|
-
@client = client
|
30
|
-
@window = window
|
31
|
-
@data = {}
|
32
|
-
@downloader = GrooveDl::Downloader.new(@client)
|
33
|
-
@downloader.type = 'gui'
|
34
|
-
|
35
|
-
sw = Gtk::ScrolledWindow.new
|
36
|
-
sw.shadow_type = Gtk::ShadowType::ETCHED_IN
|
37
|
-
sw.set_policy(Gtk::PolicyType::AUTOMATIC,
|
38
|
-
Gtk::PolicyType::AUTOMATIC)
|
39
|
-
pack_start(sw, expand: true, fill: true, padding: 0)
|
40
|
-
|
41
|
-
@store = Gtk::ListStore.new(String, Integer, String)
|
42
|
-
create_model
|
43
|
-
treeview = Gtk::TreeView.new(@store)
|
44
|
-
treeview.rules_hint = true
|
45
|
-
|
46
|
-
sw.add(treeview)
|
47
|
-
|
48
|
-
add_columns(treeview)
|
49
|
-
end
|
50
|
-
|
51
|
-
def create_model(data = {})
|
52
|
-
data.each do |id, element|
|
53
|
-
if element.is_a?(Grooveshark::Song)
|
54
|
-
iter = @store.append
|
55
|
-
iter[COLUMN_PATH] =
|
56
|
-
@downloader.build_path(@window
|
57
|
-
.find_by_name('directory_chooser')
|
58
|
-
.filename,
|
59
|
-
element)
|
60
|
-
iter[COLUMN_PGBAR_VALUE] = 0
|
61
|
-
iter[COLUMN_PGBAR_TEXT] = nil
|
62
|
-
@data[element.id] = { iter: iter, song: element }
|
63
|
-
else
|
64
|
-
playlist = Grooveshark::Playlist.new(@client,
|
65
|
-
'playlist_id' => id)
|
66
|
-
result = {}
|
67
|
-
playlist.load_songs.each do |song|
|
68
|
-
result[song.id] = song
|
69
|
-
end
|
70
|
-
|
71
|
-
create_model(result)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
return if @data.empty?
|
76
|
-
@queue = @data.count
|
77
|
-
@window.find_by_name('download_book').set_label('QUEUE', @queue)
|
78
|
-
end
|
79
|
-
|
80
|
-
##
|
81
|
-
# Add columns on the treeview element
|
82
|
-
#
|
83
|
-
# @param [Gtk::Treeview] treeview Treeview
|
84
|
-
#
|
85
|
-
def add_columns(treeview)
|
86
|
-
renderer = Gtk::CellRendererText.new
|
87
|
-
column = Gtk::TreeViewColumn.new('Path',
|
88
|
-
renderer,
|
89
|
-
'text' => COLUMN_PATH)
|
90
|
-
column.fixed_width = 650
|
91
|
-
treeview.append_column(column)
|
92
|
-
|
93
|
-
renderer = Gtk::CellRendererProgress.new
|
94
|
-
column = Gtk::TreeViewColumn.new('Progress',
|
95
|
-
renderer,
|
96
|
-
value: COLUMN_PGBAR_VALUE,
|
97
|
-
text: COLUMN_PGBAR_TEXT)
|
98
|
-
column.fixed_width = 100
|
99
|
-
treeview.append_column(column)
|
100
|
-
end
|
101
|
-
|
102
|
-
##
|
103
|
-
# Download files in queue
|
104
|
-
#
|
105
|
-
def download
|
106
|
-
concurrency = @window.find_by_name('concurrency_entry').text.to_i
|
107
|
-
concurrency = 5 if concurrency == 0
|
108
|
-
Thread.abort_on_exception = true
|
109
|
-
Thread.new do
|
110
|
-
nb = 0
|
111
|
-
@data.each do |_id, s|
|
112
|
-
nb += 1
|
113
|
-
Thread.new do
|
114
|
-
download_file(s)
|
115
|
-
nb -= 1
|
116
|
-
end
|
117
|
-
sleep(0.5) until nb < concurrency
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
##
|
123
|
-
# Download song
|
124
|
-
#
|
125
|
-
# @param [Grooveshark::Song] song Song to download
|
126
|
-
#
|
127
|
-
def download_file(song)
|
128
|
-
begin
|
129
|
-
@downloader.download(song[:song], song[:iter])
|
130
|
-
notify_success(song)
|
131
|
-
rescue Errors::AlreadyDownloaded => e
|
132
|
-
GrooveDl.configuration.logger.info(e.message)
|
133
|
-
notify_success(song)
|
134
|
-
rescue Grooveshark::GeneralError => e
|
135
|
-
GrooveDl.configuration.logger.error(e)
|
136
|
-
notify_error(song, e)
|
137
|
-
end
|
138
|
-
|
139
|
-
@window.find_by_name('download_book')
|
140
|
-
.set_label('QUEUE', @queue -= 1)
|
141
|
-
@store.remove(song[:iter])
|
142
|
-
end
|
143
|
-
|
144
|
-
##
|
145
|
-
# Notify success
|
146
|
-
#
|
147
|
-
# @param [Grooveshark::Song] song Song displayed in success page
|
148
|
-
#
|
149
|
-
def notify_success(song)
|
150
|
-
@window.find_by_name('download_success_list')
|
151
|
-
.create_model(song[:iter])
|
152
|
-
@window.find_by_name('download_book')
|
153
|
-
.set_label('SUCCESS', @success += 1)
|
154
|
-
end
|
155
|
-
|
156
|
-
##
|
157
|
-
# Notify erro
|
158
|
-
#
|
159
|
-
# @param [Grooveshark::Song] song Song displayed in failed page
|
160
|
-
#
|
161
|
-
def notify_error(song, e)
|
162
|
-
@window.find_by_name('download_failed_list')
|
163
|
-
.create_model(song[:iter], e.message)
|
164
|
-
@window.find_by_name('download_book')
|
165
|
-
.set_label('FAILED', @failed += 1)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
module GrooveDl
|
3
|
-
# Widgets components
|
4
|
-
module Widgets
|
5
|
-
# Download section
|
6
|
-
module Download
|
7
|
-
# List page
|
8
|
-
module List
|
9
|
-
# Success tree
|
10
|
-
class Success < Gtk::Box
|
11
|
-
attr_reader :store, :data, :treeview
|
12
|
-
|
13
|
-
COLUMN_PATH,
|
14
|
-
COLUMN_SIZE = *(0..1).to_a
|
15
|
-
RIGHT_CLICK = 3
|
16
|
-
|
17
|
-
##
|
18
|
-
# Initialize widgets
|
19
|
-
#
|
20
|
-
# @param [Grooveshark::Client] client Grooveshark client
|
21
|
-
# @param [Gtk::Window] window Gtk app
|
22
|
-
#
|
23
|
-
def load(_client, _window, menu)
|
24
|
-
@data = {}
|
25
|
-
sw = Gtk::ScrolledWindow.new
|
26
|
-
sw.shadow_type = Gtk::ShadowType::ETCHED_IN
|
27
|
-
sw.set_policy(Gtk::PolicyType::AUTOMATIC,
|
28
|
-
Gtk::PolicyType::AUTOMATIC)
|
29
|
-
pack_start(sw, expand: true, fill: true, padding: 0)
|
30
|
-
|
31
|
-
@store = Gtk::ListStore.new(String, String)
|
32
|
-
@treeview = Gtk::TreeView.new(@store)
|
33
|
-
@treeview.rules_hint = true
|
34
|
-
|
35
|
-
@treeview.signal_connect('button_press_event') do |widget, event|
|
36
|
-
if event.is_a?(Gdk::EventButton) && event.button == RIGHT_CLICK
|
37
|
-
path, _model = widget.get_path_at_pos(event.x, event.y)
|
38
|
-
widget.selection.select_path(path)
|
39
|
-
menu.popup(nil, nil, event.button, event.time)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
sw.add(@treeview)
|
44
|
-
|
45
|
-
add_columns
|
46
|
-
end
|
47
|
-
|
48
|
-
##
|
49
|
-
# Append row in the list store
|
50
|
-
#
|
51
|
-
# @param [Gtk::TreeIter] i Iterable element
|
52
|
-
#
|
53
|
-
def create_model(i)
|
54
|
-
iter = @store.append
|
55
|
-
path = i[COLUMN_PATH]
|
56
|
-
iter[COLUMN_PATH] = path
|
57
|
-
size = (File.size?(path).to_f / (1024 * 1024)).round(2)
|
58
|
-
iter[COLUMN_SIZE] = "#{size} MB"
|
59
|
-
end
|
60
|
-
|
61
|
-
##
|
62
|
-
# Add columns on the treeview element
|
63
|
-
#
|
64
|
-
def add_columns
|
65
|
-
renderer = Gtk::CellRendererText.new
|
66
|
-
column = Gtk::TreeViewColumn.new('Path',
|
67
|
-
renderer,
|
68
|
-
'text' => COLUMN_PATH)
|
69
|
-
column.fixed_width = 650
|
70
|
-
@treeview.append_column(column)
|
71
|
-
|
72
|
-
renderer = Gtk::CellRendererText.new
|
73
|
-
column = Gtk::TreeViewColumn.new('Size',
|
74
|
-
renderer,
|
75
|
-
'text' => COLUMN_SIZE)
|
76
|
-
column.fixed_width = 100
|
77
|
-
@treeview.append_column(column)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
module GrooveDl
|
3
|
-
# Widgets components
|
4
|
-
module Widgets
|
5
|
-
# Download section
|
6
|
-
module Menu
|
7
|
-
# Success menu
|
8
|
-
class Success < Gtk::Menu
|
9
|
-
##
|
10
|
-
# Initialize widgets
|
11
|
-
#
|
12
|
-
# @param [Grooveshark::Client] client Grooveshark client
|
13
|
-
# @param [Gtk::Window] window Gtk app
|
14
|
-
#
|
15
|
-
def load(_client, window)
|
16
|
-
item = Gtk::ImageMenuItem.new(stock_id: Gtk::Stock::OPEN)
|
17
|
-
item.signal_connect('activate') do
|
18
|
-
treeview = window.find_by_name('download_success_list').treeview
|
19
|
-
iter = treeview.selection.selected
|
20
|
-
Thread.new do
|
21
|
-
path = iter[Download::List::Queue::COLUMN_PATH]
|
22
|
-
system("gnome-open #{Shellwords.escape(path)}")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
append(item)
|
27
|
-
show_all
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
module GrooveDl
|
3
|
-
# Widgets components
|
4
|
-
module Widgets
|
5
|
-
# Search section
|
6
|
-
module Search
|
7
|
-
# Search bar
|
8
|
-
class Bar < Gtk::Box
|
9
|
-
attr_reader :type, :query
|
10
|
-
|
11
|
-
##
|
12
|
-
# Initialize widgets
|
13
|
-
#
|
14
|
-
# @param [Grooveshark::Client] client Grooveshark client
|
15
|
-
# @param [Gtk::Window] window Gtk app
|
16
|
-
#
|
17
|
-
def load(client, window)
|
18
|
-
search_box = Gtk::Box.new(:horizontal, 6)
|
19
|
-
|
20
|
-
search_bar = Gtk::Entry.new
|
21
|
-
search_bar.set_name('search_bar')
|
22
|
-
search_bar.set_placeholder_text('Search...')
|
23
|
-
|
24
|
-
search_box.pack_start(search_bar,
|
25
|
-
expand: true,
|
26
|
-
fill: true,
|
27
|
-
padding: 10)
|
28
|
-
|
29
|
-
search_type = Gtk::ComboBoxText.new
|
30
|
-
search_type.set_name('search_type')
|
31
|
-
search_type.append_text 'Playlists'
|
32
|
-
search_type.append_text 'Songs'
|
33
|
-
search_type.active = 0
|
34
|
-
|
35
|
-
search_box.pack_start(search_type,
|
36
|
-
expand: false,
|
37
|
-
fill: true,
|
38
|
-
padding: 5)
|
39
|
-
|
40
|
-
button = Gtk::Button.new(stock_id: Gtk::Stock::FIND)
|
41
|
-
button.set_name('search_button')
|
42
|
-
search_box.pack_start(button,
|
43
|
-
expand: false,
|
44
|
-
fill: false,
|
45
|
-
padding: 10)
|
46
|
-
|
47
|
-
pack_start(search_box,
|
48
|
-
expand: false,
|
49
|
-
padding: 10)
|
50
|
-
|
51
|
-
# Signals
|
52
|
-
button.signal_connect('released') do
|
53
|
-
@type = search_type.active_text
|
54
|
-
@query = search_bar.text
|
55
|
-
next if @type.empty? || @query.empty?
|
56
|
-
search = client.request('getResultsFromSearch',
|
57
|
-
type: @type,
|
58
|
-
query: @query)
|
59
|
-
results = search['result'].map do |data|
|
60
|
-
next Grooveshark::Song.new data if type == 'Songs'
|
61
|
-
data
|
62
|
-
end if search.key?('result')
|
63
|
-
|
64
|
-
window.find_by_name('search_list').create_model(results)
|
65
|
-
end
|
66
|
-
|
67
|
-
search_bar.signal_connect('activate') do
|
68
|
-
button.signal_emit('released')
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,151 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
module GrooveDl
|
3
|
-
# Widgets components
|
4
|
-
module Widgets
|
5
|
-
# Search section
|
6
|
-
module Search
|
7
|
-
# Search list tree
|
8
|
-
class List < Gtk::Box
|
9
|
-
attr_reader :data
|
10
|
-
attr_reader :store
|
11
|
-
|
12
|
-
COLUMN_CHECKBOX,
|
13
|
-
COLUMN_ID,
|
14
|
-
COLUMN_NAME,
|
15
|
-
COLUMN_AUTHOR,
|
16
|
-
COLUMN_SONG = *(0..4).to_a
|
17
|
-
|
18
|
-
##
|
19
|
-
# Initialize widgets
|
20
|
-
#
|
21
|
-
# @param [Grooveshark::Client] client Grooveshark client
|
22
|
-
# @param [Gtk::Window] window Gtk app
|
23
|
-
#
|
24
|
-
def load(_client, _window)
|
25
|
-
set_name('search_list')
|
26
|
-
|
27
|
-
sw = Gtk::ScrolledWindow.new
|
28
|
-
sw.shadow_type = Gtk::ShadowType::ETCHED_IN
|
29
|
-
sw.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC)
|
30
|
-
pack_start(sw, expand: true, fill: true, padding: 0)
|
31
|
-
|
32
|
-
@store = Gtk::ListStore.new(TrueClass,
|
33
|
-
Integer,
|
34
|
-
String,
|
35
|
-
String,
|
36
|
-
String)
|
37
|
-
create_model
|
38
|
-
treeview = Gtk::TreeView.new(@store)
|
39
|
-
treeview.rules_hint = true
|
40
|
-
treeview.search_column = COLUMN_SONG
|
41
|
-
|
42
|
-
sw.add(treeview)
|
43
|
-
|
44
|
-
add_columns(treeview)
|
45
|
-
end
|
46
|
-
|
47
|
-
##
|
48
|
-
# Create line in the list store
|
49
|
-
#
|
50
|
-
# @param [Array] data Data stored
|
51
|
-
#
|
52
|
-
def create_model(data = [])
|
53
|
-
@store.clear
|
54
|
-
@data = {}
|
55
|
-
data.each do |element|
|
56
|
-
iter = @store.append
|
57
|
-
iter[COLUMN_CHECKBOX] = false
|
58
|
-
if element.is_a?(Grooveshark::Song)
|
59
|
-
@data[element.id.to_i] = element
|
60
|
-
iter[COLUMN_ID] = element.id.to_i
|
61
|
-
iter[COLUMN_NAME] = element.name
|
62
|
-
iter[COLUMN_AUTHOR] = element.artist
|
63
|
-
iter[COLUMN_SONG] = element.album
|
64
|
-
else
|
65
|
-
@data[element['playlist_id'].to_i] = element
|
66
|
-
iter[COLUMN_ID] = element['playlist_id'].to_i
|
67
|
-
iter[COLUMN_NAME] = element['name']
|
68
|
-
iter[COLUMN_AUTHOR] = element['f_name']
|
69
|
-
iter[COLUMN_SONG] = element['num_songs']
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
##
|
75
|
-
# Add columns on the treeview element
|
76
|
-
#
|
77
|
-
# @param [Gtk::Treeview] treeview Treeview
|
78
|
-
#
|
79
|
-
def add_columns(treeview)
|
80
|
-
renderer = Gtk::CellRendererToggle.new
|
81
|
-
renderer.signal_connect('toggled') do |_cell, path|
|
82
|
-
fixed_toggled(treeview.model, path)
|
83
|
-
end
|
84
|
-
|
85
|
-
column = Gtk::TreeViewColumn.new('X',
|
86
|
-
renderer,
|
87
|
-
'active' => COLUMN_CHECKBOX)
|
88
|
-
column.sizing = Gtk::TreeViewColumn::Sizing::FIXED
|
89
|
-
column.fixed_width = 30
|
90
|
-
column.set_clickable(true)
|
91
|
-
column.signal_connect('clicked') do
|
92
|
-
@store.each do |_model, _path, iter|
|
93
|
-
fixed = iter[COLUMN_CHECKBOX]
|
94
|
-
fixed ^= 1
|
95
|
-
iter[COLUMN_CHECKBOX] = fixed
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
treeview.append_column(column)
|
100
|
-
|
101
|
-
renderer = Gtk::CellRendererText.new
|
102
|
-
column = Gtk::TreeViewColumn.new('Id',
|
103
|
-
renderer,
|
104
|
-
'text' => COLUMN_ID)
|
105
|
-
column.set_sort_column_id(COLUMN_ID)
|
106
|
-
column.sizing = Gtk::TreeViewColumn::Sizing::FIXED
|
107
|
-
column.fixed_width = 70
|
108
|
-
treeview.append_column(column)
|
109
|
-
|
110
|
-
renderer = Gtk::CellRendererText.new
|
111
|
-
column = Gtk::TreeViewColumn.new('Name',
|
112
|
-
renderer,
|
113
|
-
'text' => COLUMN_NAME)
|
114
|
-
column.set_sort_column_id(COLUMN_NAME)
|
115
|
-
column.fixed_width = 400
|
116
|
-
treeview.append_column(column)
|
117
|
-
|
118
|
-
renderer = Gtk::CellRendererText.new
|
119
|
-
column = Gtk::TreeViewColumn.new('Author',
|
120
|
-
renderer,
|
121
|
-
'text' => COLUMN_AUTHOR)
|
122
|
-
column.set_sort_column_id(COLUMN_AUTHOR)
|
123
|
-
column.fixed_width = 200
|
124
|
-
treeview.append_column(column)
|
125
|
-
|
126
|
-
renderer = Gtk::CellRendererText.new
|
127
|
-
column = Gtk::TreeViewColumn.new('Other data',
|
128
|
-
renderer,
|
129
|
-
'text' => COLUMN_SONG)
|
130
|
-
column.set_sort_column_id(COLUMN_SONG)
|
131
|
-
column.fixed_width = 100
|
132
|
-
treeview.append_column(column)
|
133
|
-
end
|
134
|
-
|
135
|
-
##
|
136
|
-
# Fixed toggle button
|
137
|
-
#
|
138
|
-
# @param [Gtk::ListStore] model List store
|
139
|
-
# @param [String] path_str Path to row
|
140
|
-
#
|
141
|
-
def fixed_toggled(model, path_str)
|
142
|
-
path = Gtk::TreePath.new(path_str)
|
143
|
-
iter = model.get_iter(path)
|
144
|
-
fixed = iter[COLUMN_CHECKBOX]
|
145
|
-
fixed ^= 1
|
146
|
-
iter[COLUMN_CHECKBOX] = fixed
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|