ampv 1.0.3 → 1.1.0
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.
- checksums.yaml +8 -8
- data/LICENSE +1 -1
- data/bin/ampv +4 -0
- data/input.conf +17 -16
- data/lib/ampv.rb +132 -221
- data/lib/ampv/config.rb +120 -0
- data/lib/ampv/mpvwidget.rb +66 -61
- data/lib/ampv/playlist.rb +87 -81
- data/lib/ampv/progressbarwidget.rb +17 -20
- data/lib/ampv/version.rb +1 -2
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTI1MjM3YjlmNDJhZTdlMTdjOTY4MWU5YjMwZTdkZDZmYmJlNjMwYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZGFjZTg2ZGRiZDI1NDY0MTAwNmRjY2Y1M2MzODY5YmIxZTJkYmY4Yg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTA5ZjMxMTEzNjVhMWY2NGFkZWUwNjJiZmQ4ZjBkZDVjOTg5ZTdjZGMwZDBl
|
10
|
+
MWZmZmVlYTIzY2M0ZGM4ZjhiZGU2NTdkZTE2ZWYxNzc3ZDQyMDdmOGFjNzQ1
|
11
|
+
ODU5ZTJjYjMwMTYyMTBmNmQ2Yzk2ZWJkMzJiNTk5ZDBhZmI5Yjk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YWIzYWM1YWI2ZDU2OGQ2NGZiYjE3MzUyNzQ1YmQwZDYzZjQ5MzE4MjQ4Njcx
|
14
|
+
MzA2MmQyMGM3NDUyMmM3YzdlY2E0OWYyNzYwOTQ3YzhkODU1YTVjZDg3ZjNl
|
15
|
+
YTk4MzMxNjA2YzI3N2IzMWZiMTBkNjgyODg0NjU4YmI4NzZiMmI=
|
data/LICENSE
CHANGED
data/bin/ampv
CHANGED
data/input.conf
CHANGED
@@ -6,23 +6,24 @@ MOUSE_BTN7 playlist_prev
|
|
6
6
|
MOUSE_BTN8 playlist_next
|
7
7
|
MOUSE_BTN9 cycle pause
|
8
8
|
|
9
|
-
RIGHT
|
10
|
-
LEFT
|
11
|
-
UP
|
12
|
-
DOWN
|
9
|
+
RIGHT seek 10
|
10
|
+
LEFT seek -10
|
11
|
+
UP add volume 1
|
12
|
+
DOWN add volume -1
|
13
13
|
|
14
|
-
PGUP
|
15
|
-
PGDWN
|
16
|
-
HOME
|
17
|
-
END
|
18
|
-
INS
|
19
|
-
DEL
|
14
|
+
PGUP seek -90 - exact
|
15
|
+
PGDWN seek 90 - exact
|
16
|
+
HOME add chapter -1
|
17
|
+
END add chapter 1
|
18
|
+
INS playlist_prev
|
19
|
+
DEL playlist_next
|
20
20
|
|
21
|
-
f
|
22
|
-
|
23
|
-
|
21
|
+
f cycle fullscreen
|
22
|
+
s cycle sub
|
23
|
+
SPACE cycle pause
|
24
|
+
ESC quit_watch_later
|
24
25
|
|
25
26
|
# ampv commands
|
26
|
-
l
|
27
|
-
o
|
28
|
-
p
|
27
|
+
l cycle playlist
|
28
|
+
o open_file_chooser
|
29
|
+
p cycle progress_bar
|
data/lib/ampv.rb
CHANGED
@@ -1,31 +1,17 @@
|
|
1
|
-
|
2
1
|
require "gtk2"
|
2
|
+
require "json"
|
3
|
+
require "uri"
|
4
|
+
require "ampv/config"
|
3
5
|
require "ampv/mpvwidget"
|
4
6
|
require "ampv/playlist"
|
5
7
|
require "ampv/progressbarwidget"
|
6
8
|
require "ampv/version"
|
7
|
-
require "uri"
|
8
9
|
|
9
10
|
module Ampv
|
10
11
|
class MainWindow < Gtk::Window
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
VIDEO_EXTS = [ ".avi", ".mkv", ".mp4", ".mpeg", ".mpg", ".ogm", ".ogv" ]
|
15
|
-
KEY_NAMES = {
|
16
|
-
"esc" => "Escape",
|
17
|
-
"space" => "space",
|
18
|
-
"right" => "Right",
|
19
|
-
"left" => "Left",
|
20
|
-
"up" => "Up",
|
21
|
-
"down" => "Down",
|
22
|
-
"pgup" => "Page_Up",
|
23
|
-
"pgdwn" => "Page_Down",
|
24
|
-
"home" => "Home",
|
25
|
-
"end" => "End",
|
26
|
-
"ins" => "Insert",
|
27
|
-
"del" => "Delete",
|
28
|
-
}
|
13
|
+
INPUT_CONF = "#{ENV["HOME"]}/.mpv/input.conf"
|
14
|
+
VIDEO_EXTS = [ ".avi", ".mkv", ".mp4", ".mpeg", ".mpg", ".ogm", ".ogv", ".rm", ".ts", ".wmv" ]
|
29
15
|
WHEEL_BUTTONS = {
|
30
16
|
Gdk::EventScroll::UP => 4,
|
31
17
|
Gdk::EventScroll::DOWN => 5,
|
@@ -37,208 +23,128 @@ module Ampv
|
|
37
23
|
BLANK_CURSOR = Gdk::Cursor.new(Gdk::Cursor::BLANK_CURSOR)
|
38
24
|
|
39
25
|
def initialize
|
40
|
-
|
26
|
+
unless defined?(MpvWidget::PATH)
|
27
|
+
dlg = Gtk::MessageDialog.new(nil,
|
28
|
+
Gtk::Dialog::DESTROY_WITH_PARENT,
|
29
|
+
Gtk::MessageDialog::ERROR,
|
30
|
+
Gtk::MessageDialog::BUTTONS_CLOSE,
|
31
|
+
"Unable to find mpv executable")
|
32
|
+
dlg.set_secondary_text("Please ensure you have mpv installed in your PATH.")
|
33
|
+
dlg.run
|
34
|
+
dlg.destroy
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
args = ARGV.reject { |x| x[0] != "-" }
|
39
|
+
files = ARGV - args
|
40
|
+
|
41
|
+
print_version if args.include?("--version")
|
42
|
+
Config.load
|
41
43
|
super
|
44
|
+
|
42
45
|
set_title(PACKAGE)
|
43
|
-
set_default_size(
|
46
|
+
set_default_size(Config["width"], Config["height"])
|
44
47
|
set_window_position(Gtk::Window::POS_CENTER)
|
45
|
-
move(
|
48
|
+
move(Config["x"], Config["y"]) unless Config["x"] == -1 and Config["y"] == -1
|
46
49
|
|
50
|
+
add_events(Gdk::Event::POINTER_MOTION_MASK)
|
47
51
|
Gtk::Drag.dest_set(self, Gtk::Drag::DEST_DEFAULT_ALL,
|
48
52
|
[ [ "text/uri-list", 0, 0 ] ],
|
49
|
-
Gdk::DragContext::
|
50
|
-
add_events(Gdk::Event::POINTER_MOTION_MASK)
|
53
|
+
Gdk::DragContext::ACTION_COPY)
|
51
54
|
|
52
55
|
signal_connect("delete_event") { quit }
|
53
56
|
signal_connect("scroll_event") { |w, e| handle_mouse_event(e) }
|
54
57
|
signal_connect("button_press_event") { |w, e| handle_mouse_event(e) }
|
55
58
|
signal_connect("key_press_event") { |w, e| handle_keyboard_event(e) }
|
56
59
|
signal_connect("drag_data_received") { |w, dc, x, y, sd, type, time|
|
57
|
-
handle_drop_event(sd.
|
58
|
-
|
59
|
-
signal_connect("motion_notify_event") {
|
60
|
-
window.set_cursor(LEFT_PTR)
|
61
|
-
GLib::Source.remove(@cursor_timeout) if @cursor_timeout
|
62
|
-
@cursor_timeout = GLib::Timeout.add(1000) {
|
63
|
-
window.set_cursor(BLANK_CURSOR)
|
64
|
-
} unless @mpv.is_paused
|
60
|
+
handle_drop_event(sd.uris, false, true)
|
61
|
+
Gtk::Drag.finish(dc, true, false, time)
|
65
62
|
}
|
63
|
+
signal_connect("motion_notify_event") { mouse_cursor_timeout }
|
66
64
|
|
67
|
-
vbox
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
load_bindings
|
65
|
+
vbox = Gtk::VBox.new
|
66
|
+
@mpv = MpvWidget.new(args)
|
67
|
+
@progress_bar = ProgressBarWidget.new
|
68
|
+
@playlist = Playlist.new
|
69
|
+
@ignore_stop = false
|
73
70
|
|
74
|
-
@mpv = MpvWidget.new(args, @config["scrobbler"])
|
75
71
|
@mpv.signal_connect("file_changed") { |w, file|
|
76
|
-
@playing = file
|
77
|
-
|
72
|
+
@playing = URI.decode(file).sub(/^file:\/\/[^\/]*/, "")
|
73
|
+
# hacky work around when mpv catches drag and drop events.
|
74
|
+
unless @playlist.include?(@playing)
|
75
|
+
@playlist.clear(true)
|
76
|
+
if (file = create_playlist(@playing)) != @playing and File.directory?(@playing)
|
77
|
+
@playing = file
|
78
|
+
GLib::Idle.add { @mpv.load_file(@playing); false }
|
79
|
+
# mpv will fail to play the directory and then stop
|
80
|
+
# ignore the next stop event so the playlist does not advance
|
81
|
+
@ignore_stop = true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
@mpv.send("show_text ${media-title} 1500") if window.state.fullscreen? and !@ignore_stop
|
78
85
|
@playlist.set_selected(@playing)
|
79
86
|
set_title(File.basename(@playing))
|
80
87
|
}
|
81
|
-
@mpv.signal_connect("
|
82
|
-
|
83
|
-
@playlist.update_length(@length)
|
84
|
-
}
|
88
|
+
@mpv.signal_connect("playing_watched") { @playlist.on_playing_watched }
|
89
|
+
@mpv.signal_connect("length_changed") { |w, len| @playlist.update_length(@length = len) }
|
85
90
|
@mpv.signal_connect("time_pos_changed") { |w, pos| @progress_bar.value = pos / @length.to_f }
|
86
91
|
@mpv.signal_connect("stopped") {
|
87
92
|
@progress_bar.value = 0
|
88
|
-
next_file = @playlist.get_next
|
89
|
-
@really_stop = next_file.nil? or @really_stop
|
90
93
|
set_title(PACKAGE)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
94
|
+
unless @ignore_stop
|
95
|
+
next_file = @playlist.get_next
|
96
|
+
@really_stop ||= next_file.nil?
|
97
|
+
if !@really_stop
|
98
|
+
load_file(next_file, false)
|
99
|
+
elsif window.state.fullscreen?
|
100
|
+
toggle_fullscreen
|
101
|
+
end
|
95
102
|
end
|
96
|
-
@really_stop = false
|
103
|
+
@really_stop = @ignore_stop = false
|
97
104
|
}
|
98
105
|
|
99
|
-
vbox.pack_start(@mpv)
|
100
|
-
|
101
|
-
@playlist = Playlist.new(@config["playlist_x"],
|
102
|
-
@config["playlist_y"],
|
103
|
-
@config["playlist_width"],
|
104
|
-
@config["playlist_height"],
|
105
|
-
@config["playlist_visible"])
|
106
|
-
|
107
|
-
Gtk::Drag.dest_set(@playlist, Gtk::Drag::DEST_DEFAULT_ALL,
|
108
|
-
[ [ "text/uri-list", 0, 0 ] ],
|
109
|
-
Gdk::DragContext::ACTION_LINK)
|
110
|
-
|
111
106
|
@playlist.signal_connect("open_file_chooser") { open_file_chooser }
|
112
107
|
@playlist.signal_connect("drag_data_received") { |w, dc, x, y, sd, type, time|
|
113
|
-
handle_drop_event(sd.
|
108
|
+
handle_drop_event(sd.uris, true, false)
|
109
|
+
Gtk::Drag.finish(dc, true, false, time)
|
114
110
|
}
|
115
111
|
@playlist.signal_connect("play_entry") { |w, file| load_file(file, false, false, false, true) }
|
116
|
-
@playlist.signal_connect("playing_removed") { @mpv.
|
112
|
+
@playlist.signal_connect("playing_removed") { @mpv.stop; @really_stop = true }
|
113
|
+
|
114
|
+
Gtk::Drag.dest_set(@playlist, Gtk::Drag::DEST_DEFAULT_ALL,
|
115
|
+
[ [ "text/uri-list", 0, 0 ] ],
|
116
|
+
Gdk::DragContext::ACTION_COPY)
|
117
117
|
|
118
|
-
@progress_bar = ProgressBarWidget.new(@config["bar_color"],
|
119
|
-
@config["head_color"],
|
120
|
-
@config["progress_bar_height"])
|
121
118
|
@progress_bar.add_events(Gdk::Event::BUTTON_PRESS_MASK)
|
122
119
|
@progress_bar.signal_connect("button_press_event") { |w, e| handle_seek_event(e) }
|
123
|
-
vbox.pack_start(@progress_bar, false)
|
124
120
|
|
121
|
+
vbox.pack_start(@mpv)
|
122
|
+
vbox.pack_start(@progress_bar, false)
|
123
|
+
add(vbox)
|
125
124
|
show_all
|
126
|
-
@mpv.start
|
127
|
-
|
128
|
-
argv = ARGV.join(" ")
|
129
|
-
if not argv.empty?
|
130
|
-
load_file(argv)
|
131
|
-
elsif not @config["playlist"].empty?
|
132
|
-
@config["playlist"].each { |x| load_file(x, true, true, false) }
|
133
|
-
@playlist.set_selected(@config["playlist_selected"])
|
134
|
-
end
|
135
|
-
|
136
|
-
Gtk.main
|
137
|
-
end
|
138
|
-
|
139
|
-
def load_config
|
140
|
-
@config = {
|
141
|
-
"width" => Gdk::Screen.default.width > 1280 ? 1280 : 853,
|
142
|
-
"height" => Gdk::Screen.default.width > 1280 ? 726 : 486,
|
143
|
-
"x" => -1,
|
144
|
-
"y" => -1,
|
145
|
-
"fullscreen_progressbar" => false,
|
146
|
-
"progress_bar_visible" => true,
|
147
|
-
"progress_bar_height" => 6,
|
148
|
-
"bar_color" => "#8f5b5b",
|
149
|
-
"head_color" => "#c48181",
|
150
|
-
"playlist_width" => 360,
|
151
|
-
"playlist_height" => 550,
|
152
|
-
"playlist_x" => 0,
|
153
|
-
"playlist_y" => 0,
|
154
|
-
"playlist_visible" => true,
|
155
|
-
"always_save_position" => false,
|
156
|
-
"scrobbler" => "",
|
157
|
-
"playlist_selected" => "",
|
158
|
-
"playlist" => [ ],
|
159
|
-
}
|
160
125
|
|
161
|
-
if
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
next unless @config.has_key?(key) and not key.start_with?("#")
|
167
|
-
|
168
|
-
if @config[key].is_a?(Integer)
|
169
|
-
val = val.to_i
|
170
|
-
elsif @config[key].is_a?(TrueClass) or @config[key].is_a?(FalseClass)
|
171
|
-
val = val == "true"
|
172
|
-
elsif @config[key].is_a?(Array)
|
173
|
-
val = val.split("|")
|
174
|
-
elsif val.start_with?("#")
|
175
|
-
begin
|
176
|
-
c = Gdk::Color.parse(val)
|
177
|
-
rescue
|
178
|
-
puts("Invalid hexidecimal color for setting `#{key}': `#{val}")
|
179
|
-
c = Gdk::Color.parse(@config[key])
|
180
|
-
end
|
181
|
-
val = c
|
182
|
-
end
|
183
|
-
|
184
|
-
@config[key] = val
|
185
|
-
}
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def process_args
|
190
|
-
args = [ ]
|
191
|
-
ARGV.dup.each { |arg|
|
192
|
-
if arg.start_with?("-")
|
193
|
-
args.push(arg)
|
194
|
-
ARGV.delete(arg)
|
126
|
+
if !files.empty?
|
127
|
+
if files.length > 1
|
128
|
+
files.each_with_index { |x, i| load_file(x, true, i != 0, false) }
|
129
|
+
else
|
130
|
+
load_file(files[0])
|
195
131
|
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
def load_bindings
|
201
|
-
@mouse_bindings = [ ]
|
202
|
-
@key_bindings = [ ]
|
203
|
-
if File.exists?(INPUT_CONF)
|
204
|
-
File.readlines(INPUT_CONF).each { |line|
|
205
|
-
line = line.strip
|
206
|
-
if line.start_with?("MOUSE_BTN")
|
207
|
-
# 4 = up, 5 = down, 6 = left, 7 = right
|
208
|
-
button, cmd = line.match(/MOUSE_BTN(\d+)(?:_DBL)?\s+(.+)$/).captures
|
209
|
-
button = button.to_i + 1
|
210
|
-
type = (4..7).include?(button) ? Gdk::Event::SCROLL :
|
211
|
-
line.include?("DBL") ? Gdk::Event::BUTTON2_PRESS : Gdk::Event::BUTTON_PRESS
|
212
|
-
@mouse_bindings[type] = [ ] if @mouse_bindings[type].nil?
|
213
|
-
@mouse_bindings[type][button] = cmd
|
214
|
-
elsif not line.empty?
|
215
|
-
key, cmd = line.match(/^([^\s]+)\s+(.+)$/).captures
|
216
|
-
if name = KEY_NAMES[key.downcase]
|
217
|
-
keyval = Gdk::Keyval.from_name(name)
|
218
|
-
else
|
219
|
-
keyval = Gdk::Keyval.from_name(key)
|
220
|
-
end
|
221
|
-
|
222
|
-
@key_bindings[keyval] = cmd if keyval > 0
|
223
|
-
end
|
224
|
-
}
|
132
|
+
elsif Config["playlist"].length > 0
|
133
|
+
Config["playlist"].each { |x| @playlist.add_file(x["file"], x["length"], x["watched"]) }
|
134
|
+
@playlist.set_selected(Config["playlist_selected"])
|
225
135
|
end
|
136
|
+
|
137
|
+
Gtk.main
|
226
138
|
end
|
227
139
|
|
228
|
-
|
229
|
-
|
140
|
+
private
|
141
|
+
def load_file(file, add_to_playlist = true, do_not_play = false, auto_add = true, force_play = false)
|
230
142
|
file = File.expand_path(file) if file[0] == "~"
|
143
|
+
return unless (File.directory?(file) or valid_video_file?(file))
|
231
144
|
|
232
145
|
if add_to_playlist
|
233
146
|
if @playlist.count == 0 and auto_add and file !~ /^https?:\/\//
|
234
|
-
|
235
|
-
entries = Dir.entries(dir).sort
|
236
|
-
entries.delete_if { |x| x.start_with?(".") or not valid_video_file(x) }
|
237
|
-
entries.map { |x|
|
238
|
-
x = dir + "/" + x
|
239
|
-
@playlist.add_file(x)
|
240
|
-
file = x if file == dir
|
241
|
-
}
|
147
|
+
file = create_playlist(file)
|
242
148
|
else
|
243
149
|
@playlist.add_file(file)
|
244
150
|
end
|
@@ -247,19 +153,23 @@ module Ampv
|
|
247
153
|
@mpv.load_file(file, force_play) unless do_not_play
|
248
154
|
end
|
249
155
|
|
156
|
+
def create_playlist(file)
|
157
|
+
dir = File.directory?(file) ? file : File.dirname(file)
|
158
|
+
entries = Dir.entries(dir).sort.map { |x| "#{dir}/#{x}" }
|
159
|
+
entries.delete_if { |x| x[0] == "." || !valid_video_file?(x) }
|
160
|
+
entries.each { |x| @playlist.add_file(x) }
|
161
|
+
file == dir ? entries[0] : file
|
162
|
+
end
|
163
|
+
|
250
164
|
def handle_mouse_event(e)
|
165
|
+
mouse_cursor_timeout
|
251
166
|
button = e.event_type == Gdk::Event::SCROLL ? WHEEL_BUTTONS[e.direction] : e.button
|
252
|
-
return if
|
253
|
-
|
254
|
-
process_cmd(@mouse_bindings[e.event_type][button])
|
255
|
-
|
256
|
-
return true
|
167
|
+
return if Config["mouse_bindings"][e.event_type].nil?
|
168
|
+
process_cmd(Config["mouse_bindings"][e.event_type][button])
|
257
169
|
end
|
258
170
|
|
259
171
|
def handle_keyboard_event(e)
|
260
|
-
process_cmd(
|
261
|
-
|
262
|
-
return true
|
172
|
+
process_cmd(Config["key_bindings"][e.keyval])
|
263
173
|
end
|
264
174
|
|
265
175
|
def handle_seek_event(e)
|
@@ -267,19 +177,19 @@ module Ampv
|
|
267
177
|
pos = e.x / allocation.width * @length.to_f
|
268
178
|
seek("seek #{pos} absolute")
|
269
179
|
end
|
270
|
-
|
271
|
-
return true
|
272
180
|
end
|
273
181
|
|
274
|
-
def handle_drop_event(
|
275
|
-
files = URI.decode(data).gsub("file://", "").split("\r\n")
|
182
|
+
def handle_drop_event(files, do_not_play, replace)
|
276
183
|
@playlist.clear if replace
|
184
|
+
files.each { |x| load_file(URI.decode(x).sub(/^file:\/\/[^\/]*/, ""), true, do_not_play) }
|
185
|
+
end
|
277
186
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
187
|
+
def mouse_cursor_timeout
|
188
|
+
window.set_cursor(LEFT_PTR)
|
189
|
+
GLib::Source.remove(@cursor_timeout) if @cursor_timeout
|
190
|
+
@cursor_timeout = GLib::Timeout.add(1000) {
|
191
|
+
window.set_cursor(BLANK_CURSOR)
|
192
|
+
} unless @mpv.is_paused
|
283
193
|
end
|
284
194
|
|
285
195
|
def process_cmd(cmd)
|
@@ -288,6 +198,8 @@ module Ampv
|
|
288
198
|
toggle_fullscreen
|
289
199
|
when "cycle pause"
|
290
200
|
@mpv.play_pause
|
201
|
+
when "stop"
|
202
|
+
@mpv.stop
|
291
203
|
when "cycle playlist"
|
292
204
|
@playlist.visible? ? @playlist.hide : @playlist.show
|
293
205
|
when /seek /
|
@@ -309,11 +221,11 @@ module Ampv
|
|
309
221
|
else
|
310
222
|
@mpv.send(cmd) if cmd
|
311
223
|
end
|
224
|
+
true
|
312
225
|
end
|
313
226
|
|
314
227
|
def seek(cmd)
|
315
|
-
cmd = "no-osd " + cmd unless cmd.start_with?("no-osd") or
|
316
|
-
not @progress_bar.visible?
|
228
|
+
cmd = "no-osd " + cmd unless cmd.start_with?("no-osd") or !@progress_bar.visible?
|
317
229
|
@mpv.send(cmd)
|
318
230
|
@mpv.send("get_property time-pos")
|
319
231
|
end
|
@@ -323,9 +235,10 @@ module Ampv
|
|
323
235
|
@progress_bar.show unless @progress_bar_user_hidden
|
324
236
|
unfullscreen
|
325
237
|
else
|
326
|
-
@progress_bar.hide unless
|
238
|
+
@progress_bar.hide unless Config["fullscreen_progressbar"]
|
327
239
|
fullscreen
|
328
240
|
end
|
241
|
+
mouse_cursor_timeout
|
329
242
|
end
|
330
243
|
|
331
244
|
def toggle_progress_bar
|
@@ -338,9 +251,8 @@ module Ampv
|
|
338
251
|
end
|
339
252
|
end
|
340
253
|
|
341
|
-
def valid_video_file(x)
|
342
|
-
return (VIDEO_EXTS.include?(File.extname(x))
|
343
|
-
`file -b --mime-type "#{x}"`.start_with?("video"))
|
254
|
+
def valid_video_file?(x)
|
255
|
+
return (x and File.exists?(x) and !File.directory?(x) and VIDEO_EXTS.include?(File.extname(x).downcase))
|
344
256
|
end
|
345
257
|
|
346
258
|
|
@@ -350,7 +262,6 @@ module Ampv
|
|
350
262
|
[ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
|
351
263
|
[ Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT ])
|
352
264
|
dialog.select_multiple = true
|
353
|
-
do_not_play = @playlist.count > 0
|
354
265
|
|
355
266
|
filter = Gtk::FileFilter.new
|
356
267
|
filter.name = "Video Files"
|
@@ -363,35 +274,35 @@ module Ampv
|
|
363
274
|
dialog.add_filter(filterAll)
|
364
275
|
|
365
276
|
dialog.filenames.each { |x|
|
277
|
+
do_not_play = @playlist.count > 0
|
366
278
|
load_file(x, true, do_not_play)
|
367
|
-
do_not_play = true
|
368
279
|
} if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
369
280
|
dialog.destroy
|
370
281
|
end
|
371
282
|
|
372
283
|
def print_version
|
373
|
-
puts("#{PACKAGE} - v#{VERSION}\n" +
|
374
|
-
|
375
|
-
|
284
|
+
puts("#{PACKAGE} - v#{VERSION} (C) 2013-2014 ahoka\n" +
|
285
|
+
`ruby --version` +
|
286
|
+
`#{MpvWidget::PATH} --version`)
|
376
287
|
exit
|
377
288
|
end
|
378
289
|
|
379
|
-
def quit(watch_later=false)
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
@mpv.quit(
|
290
|
+
def quit(watch_later = false)
|
291
|
+
Config["x"],
|
292
|
+
Config["y"],
|
293
|
+
Config["width"],
|
294
|
+
Config["height"] = window.geometry unless window.state.fullscreen?
|
295
|
+
Config["playlist_x"],
|
296
|
+
Config["playlist_y"],
|
297
|
+
Config["playlist_width"],
|
298
|
+
Config["playlist_height"] = @playlist.window.geometry
|
299
|
+
Config["playlist_visible"] = @playlist.visible?
|
300
|
+
Config["playlist_selected"] = @playing
|
301
|
+
Config["playlist"] = @playlist.get_entries.to_json
|
302
|
+
Config["progress_bar_visible"] = @progress_bar.visible?
|
303
|
+
Config.save
|
304
|
+
|
305
|
+
@mpv.quit(Config["always_save_position"] ? true : watch_later)
|
395
306
|
Gtk.main_quit
|
396
307
|
end
|
397
308
|
end
|
data/lib/ampv/config.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
module Ampv
|
2
|
+
class Config
|
3
|
+
|
4
|
+
@@config_file = "#{(ENV["XDG_CONFIG_HOME"] || "#{Dir.home}/.config")}/ampv.conf"
|
5
|
+
@@input_config = File.exists?("#{Dir.home}/.mpv/input.conf") ?
|
6
|
+
"#{Dir.home}/.mpv/input.conf" : File.expand_path("../../../input.conf", __FILE__)
|
7
|
+
@@key_names = {
|
8
|
+
"esc" => "Escape",
|
9
|
+
"space" => "space",
|
10
|
+
"right" => "Right",
|
11
|
+
"left" => "Left",
|
12
|
+
"up" => "Up",
|
13
|
+
"down" => "Down",
|
14
|
+
"pgup" => "Page_Up",
|
15
|
+
"pgdwn" => "Page_Down",
|
16
|
+
"home" => "Home",
|
17
|
+
"end" => "End",
|
18
|
+
"ins" => "Insert",
|
19
|
+
"del" => "Delete",
|
20
|
+
}
|
21
|
+
@@config = { }
|
22
|
+
@@defaults = {
|
23
|
+
"width" => Gdk::Screen.default.width > 1280 ? 1280 : 853,
|
24
|
+
"height" => Gdk::Screen.default.width > 1280 ? 726 : 486,
|
25
|
+
"x" => -1,
|
26
|
+
"y" => -1,
|
27
|
+
"fullscreen_progressbar" => false,
|
28
|
+
"progress_bar_visible" => true,
|
29
|
+
"progress_bar_height" => 6,
|
30
|
+
"bar_color" => Gdk::Color.parse("#8f5b5b"),
|
31
|
+
"head_color" => Gdk::Color.parse("#c48181"),
|
32
|
+
"playlist_width" => 360,
|
33
|
+
"playlist_height" => 550,
|
34
|
+
"playlist_x" => 0,
|
35
|
+
"playlist_y" => 0,
|
36
|
+
"playlist_visible" => true,
|
37
|
+
"always_save_position" => false,
|
38
|
+
"scrobbler" => "",
|
39
|
+
"playlist_selected" => "",
|
40
|
+
"playlist" => [ ],
|
41
|
+
"key_bindings" => [ ],
|
42
|
+
"mouse_bindings" => [ ]
|
43
|
+
}
|
44
|
+
|
45
|
+
def self.load
|
46
|
+
if File.exists?(@@config_file)
|
47
|
+
File.readlines(@@config_file).each { |line|
|
48
|
+
key, _, val = line.partition("=")
|
49
|
+
key.strip!
|
50
|
+
val.strip!
|
51
|
+
next unless @@defaults.has_key?(key) and key[0] != "#"
|
52
|
+
|
53
|
+
if @@defaults[key].is_a?(Integer)
|
54
|
+
val = val.to_i
|
55
|
+
elsif @@defaults[key].is_a?(TrueClass) or @@defaults[key].is_a?(FalseClass)
|
56
|
+
val = val == "true"
|
57
|
+
elsif @@defaults[key].is_a?(Gdk::Color)
|
58
|
+
begin
|
59
|
+
val = Gdk::Color.parse(val)
|
60
|
+
rescue
|
61
|
+
puts("Invalid hexidecimal color for setting `#{key}': `#{val}'")
|
62
|
+
next
|
63
|
+
end
|
64
|
+
elsif key == "playlist"
|
65
|
+
begin
|
66
|
+
val = JSON.parse(val)
|
67
|
+
rescue
|
68
|
+
puts("Failed to parse playlist JSON array.")
|
69
|
+
next
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@@config[key] = val
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# load input config
|
78
|
+
self["mouse_bindings"] = [ ]
|
79
|
+
self["key_bindings"] = [ ]
|
80
|
+
if File.exists?(@@input_config)
|
81
|
+
File.readlines(@@input_config).each { |line|
|
82
|
+
line.strip!
|
83
|
+
if line.start_with?("MOUSE_BTN")
|
84
|
+
# 4 = up, 5 = down, 6 = left, 7 = right
|
85
|
+
button, cmd = line.match(/MOUSE_BTN(\d+)(?:_DBL)?\s+(.+)$/).captures
|
86
|
+
button = button.to_i + 1
|
87
|
+
type = (4..7).include?(button) ? Gdk::Event::SCROLL :
|
88
|
+
line.include?("DBL") ? Gdk::Event::BUTTON2_PRESS : Gdk::Event::BUTTON_PRESS
|
89
|
+
self["mouse_bindings"][type] = [ ] if self["mouse_bindings"][type].nil?
|
90
|
+
self["mouse_bindings"][type][button] = cmd
|
91
|
+
elsif !line.empty?
|
92
|
+
key, cmd = line.match(/^([^\s]+)\s+(.+)$/).captures
|
93
|
+
if name = @@key_names[key.downcase]
|
94
|
+
keyval = Gdk::Keyval.from_name(name)
|
95
|
+
else
|
96
|
+
keyval = Gdk::Keyval.from_name(key)
|
97
|
+
end
|
98
|
+
|
99
|
+
self["key_bindings"][keyval] = cmd if keyval > 0
|
100
|
+
end
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.[](key)
|
106
|
+
@@config[key].nil? ? @@defaults[key] : @@config[key]
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.[]=(key, value)
|
110
|
+
@@config[key] = value
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.save
|
114
|
+
File.open(@@config_file, "w") { |file|
|
115
|
+
@@config.each { |k, v| file.puts("#{k}=#{v}") unless k == "key_bindings" or k == "mouse_bindings" }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
data/lib/ampv/mpvwidget.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fifo"
|
2
|
+
require "tmpdir"
|
2
3
|
|
3
4
|
module Ampv
|
4
5
|
class MpvWidget < Gtk::EventBox
|
@@ -6,112 +7,115 @@ module Ampv
|
|
6
7
|
type_register
|
7
8
|
signal_new("file_changed", GLib::Signal::RUN_FIRST, nil, nil, String)
|
8
9
|
signal_new("length_changed", GLib::Signal::RUN_FIRST, nil, nil, Integer)
|
10
|
+
signal_new("playing_watched", GLib::Signal::RUN_FIRST, nil, nil)
|
9
11
|
signal_new("time_pos_changed", GLib::Signal::RUN_FIRST, nil, nil, Float)
|
10
12
|
signal_new("stopped", GLib::Signal::RUN_FIRST, nil, nil)
|
11
13
|
|
12
|
-
PATH
|
14
|
+
ENV["PATH"].split(":").each { |x|
|
15
|
+
if File.executable?("#{x}/mpv")
|
16
|
+
PATH = "#{x}/mpv"
|
17
|
+
break
|
18
|
+
end
|
19
|
+
}
|
13
20
|
|
14
21
|
attr_reader :is_paused
|
15
22
|
|
16
|
-
def initialize(args
|
23
|
+
def initialize(args)
|
17
24
|
if args.include?("--debug")
|
18
25
|
args.delete("--debug")
|
19
26
|
@debug = true
|
20
27
|
end
|
21
28
|
|
22
|
-
@scrobbler = scrobbler
|
23
|
-
@mpv_path = "/usr/bin/mpv"
|
24
29
|
@mpv_options = args.join(" ")
|
25
|
-
@mpv_fifo = "/
|
30
|
+
@mpv_fifo = "#{Dir.tmpdir}/mpv.fifo." + Process.pid.to_s
|
31
|
+
@is_paused = true
|
26
32
|
|
27
33
|
super()
|
34
|
+
signal_connect("realize") { start }
|
28
35
|
|
29
36
|
@socket = Gtk::Socket.new
|
30
37
|
@socket.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse("#000"))
|
31
|
-
|
32
38
|
@socket.signal_connect("plug_removed") { signal_emit("stopped"); true }
|
33
|
-
add(@socket)
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
if @thread.nil?
|
38
|
-
@fifo = Fifo.new(@mpv_fifo, :w, :nowait)
|
39
|
-
|
40
|
-
cmd = "#{@mpv_path} \
|
41
|
-
--identify \
|
42
|
-
--idle \
|
43
|
-
--input-file=#{@mpv_fifo} \
|
44
|
-
--no-mouse-movements \
|
45
|
-
--cursor-autohide=no \
|
46
|
-
--msglevel=all=info \
|
47
|
-
--wid=#{@socket.id} #{@mpv_options}"
|
48
|
-
@thread = Thread.new { slave_reader(cmd) }
|
49
|
-
end
|
40
|
+
add(@socket)
|
50
41
|
end
|
51
42
|
|
52
43
|
def send(cmd)
|
53
|
-
@fifo.puts(cmd)
|
44
|
+
@fifo.puts(cmd) if @fifo
|
54
45
|
end
|
55
46
|
|
56
|
-
def load_file(file, force_play=false)
|
47
|
+
def load_file(file, force_play = false)
|
57
48
|
send("loadfile \"#{file}\"")
|
58
49
|
@force_play = force_play
|
59
50
|
end
|
60
51
|
|
61
52
|
def play_pause
|
62
53
|
send("cycle pause")
|
63
|
-
@is_paused =
|
54
|
+
@is_paused = !@is_paused
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
send("stop")
|
59
|
+
@is_paused = true
|
64
60
|
end
|
65
61
|
|
66
62
|
def quit(watch_later)
|
67
63
|
send("quit" + (watch_later ? "_watch_later" : ""))
|
68
|
-
@thread.
|
64
|
+
@thread.kill if @thread
|
65
|
+
@prog_thread.kill if @prog_thread
|
69
66
|
@fifo.close
|
70
|
-
File.delete(@mpv_fifo)
|
71
67
|
end
|
72
68
|
|
73
69
|
private
|
74
|
-
def
|
75
|
-
@
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
70
|
+
def start
|
71
|
+
return if @thread and @thread.alive?
|
72
|
+
@fifo = Fifo.new(@mpv_fifo, :w, :nowait)
|
73
|
+
ObjectSpace.define_finalizer(self, proc { File.delete(@mpv_fifo) })
|
74
|
+
|
75
|
+
cmd = "#{PATH} \
|
76
|
+
--idle \
|
77
|
+
--input-file=#{@mpv_fifo} \
|
78
|
+
--cursor-autohide=no \
|
79
|
+
--no-mouse-movements \
|
80
|
+
--msglevel=all=info \
|
81
|
+
--wid=#{@socket.id} #{@mpv_options}"
|
82
|
+
|
83
|
+
@thread = Thread.start {
|
84
|
+
IO.popen(cmd) { |io|
|
85
|
+
io.each { |line|
|
86
|
+
line.chomp!
|
87
|
+
if line.include?("Playing: ")
|
88
|
+
signal_emit("file_changed", (@playing = line.partition("Playing: ")[-1]))
|
89
|
+
send("get_property length")
|
90
|
+
send("get_property time-pos")
|
91
|
+
send("get_property pause")
|
92
|
+
elsif line.start_with?("ANS_length=")
|
93
|
+
signal_emit("length_changed", (@length = line.rpartition("=")[-1].to_i))
|
94
|
+
elsif line.start_with?("ANS_pause=")
|
95
|
+
@is_paused = line.rpartition("=")[-1] == "yes"
|
96
|
+
@prog_thread.kill if @prog_thread
|
97
|
+
@prog_thread = Thread.new { progress_update }
|
98
|
+
play_pause if @force_play and @is_paused
|
99
|
+
elsif line.start_with?("ANS_time-pos=")
|
100
|
+
signal_emit("time_pos_changed", line.rpartition("=")[-1].to_f)
|
101
|
+
end
|
102
|
+
|
103
|
+
puts(line) if @debug and !line.start_with?("ANS_")
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
101
107
|
end
|
102
108
|
|
103
109
|
def progress_update
|
104
110
|
scrobbled = false
|
105
111
|
watched = 0
|
106
|
-
|
107
112
|
loop {
|
108
113
|
send("get_property time-pos") unless @is_paused
|
109
|
-
|
110
|
-
|
111
|
-
|
114
|
+
unless scrobbled or watched < @length * 0.5
|
115
|
+
system("#{Config["scrobbler"]} \"#{@playing}\"") if Config["scrobbler"]
|
116
|
+
signal_emit("playing_watched")
|
112
117
|
scrobbled = true
|
113
118
|
end
|
114
|
-
|
115
119
|
sleep(1)
|
116
120
|
watched += 1 unless @is_paused
|
117
121
|
}
|
@@ -119,9 +123,10 @@ module Ampv
|
|
119
123
|
|
120
124
|
def signal_do_file_changed(file) end
|
121
125
|
def signal_do_length_changed(len) end
|
126
|
+
def signal_do_playing_watched; end
|
122
127
|
def signal_do_time_pos_changed(pos) end
|
123
|
-
def signal_do_stopped
|
124
|
-
@prog_thread.kill
|
128
|
+
def signal_do_stopped
|
129
|
+
@prog_thread.kill if @prog_thread
|
125
130
|
end
|
126
131
|
end
|
127
132
|
end
|
data/lib/ampv/playlist.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#require "gtk2"
|
2
|
-
|
3
1
|
module Ampv
|
4
2
|
class Playlist < Gtk::Window
|
5
3
|
|
@@ -8,7 +6,9 @@ module Ampv
|
|
8
6
|
signal_new("playing_removed", GLib::Signal::RUN_FIRST, nil, nil)
|
9
7
|
signal_new("open_file_chooser", GLib::Signal::RUN_FIRST, nil, nil)
|
10
8
|
|
11
|
-
|
9
|
+
WATCHED_PIXBUF = Gtk::Invisible.new.render_icon(Gtk::Stock::OK, Gtk::IconSize::MENU)
|
10
|
+
|
11
|
+
def initialize
|
12
12
|
buttons = {
|
13
13
|
[ Gtk::Stock::OPEN, "Add to Playlist" ] => lambda { signal_emit("open_file_chooser") },
|
14
14
|
[ Gtk::Stock::GO_UP, "Move Up" ] => lambda { move_selected_up },
|
@@ -17,11 +17,11 @@ module Ampv
|
|
17
17
|
[ Gtk::Stock::CLEAR, "Clear Playlist" ] => lambda { clear }
|
18
18
|
}
|
19
19
|
|
20
|
-
super
|
20
|
+
super
|
21
21
|
set_title("Playlist - #{PACKAGE}")
|
22
|
-
set_default_size(
|
22
|
+
set_default_size(Config["playlist_width"], Config["playlist_height"])
|
23
23
|
set_skip_taskbar_hint(true)
|
24
|
-
move(
|
24
|
+
move(Config["playlist_x"], Config["playlist_y"])
|
25
25
|
|
26
26
|
signal_connect("show") { move(@pos[0], @pos[1]) unless @pos.nil? }
|
27
27
|
signal_connect("hide") { @pos = window.root_origin }
|
@@ -31,38 +31,36 @@ module Ampv
|
|
31
31
|
hide_on_delete if e.keyval == Gdk::Keyval::GDK_Escape
|
32
32
|
}
|
33
33
|
|
34
|
-
vbox
|
35
|
-
|
36
|
-
|
34
|
+
vbox = Gtk::VBox.new(false, 10)
|
35
|
+
sw = Gtk::ScrolledWindow.new
|
36
|
+
@model = Gtk::ListStore.new(String, String, Gdk::Pixbuf)
|
37
|
+
@treeview = Gtk::TreeView.new(@model)
|
38
|
+
hbox = Gtk::HBox.new(true, 5)
|
39
|
+
@menu = Gtk::Menu.new
|
37
40
|
|
38
|
-
|
41
|
+
vbox.border_width = 10
|
39
42
|
sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
|
40
|
-
vbox.pack_start(sw)
|
41
43
|
|
42
|
-
@model = Gtk::ListStore.new(String, String)
|
43
|
-
@treeview = Gtk::TreeView.new(@model)
|
44
44
|
@treeview.enable_search = false
|
45
45
|
@treeview.rubber_banding = true
|
46
46
|
@treeview.reorderable = true
|
47
47
|
@treeview.selection.mode = Gtk::SELECTION_MULTIPLE
|
48
|
+
@treeview.tooltip_column = 0
|
48
49
|
|
49
|
-
@treeview.signal_connect("row_activated") { |
|
50
|
+
@treeview.signal_connect("row_activated") { |w, p, c|
|
50
51
|
signal_emit("play_entry", @model.get_iter(p)[0])
|
51
52
|
}
|
52
|
-
@treeview.signal_connect("key_press_event") { |
|
53
|
+
@treeview.signal_connect("key_press_event") { |w, e|
|
53
54
|
remove_selected if e.keyval == Gdk::Keyval::GDK_Delete
|
54
55
|
}
|
55
|
-
@treeview.signal_connect("button_press_event") { |
|
56
|
-
@menu.popup(nil, nil, e.button, e.time) if
|
57
|
-
e.event_type == Gdk::Event::BUTTON_PRESS and e.button == 3
|
56
|
+
@treeview.signal_connect("button_press_event") { |w, e|
|
57
|
+
@menu.popup(nil, nil, e.button, e.time) if e.event_type == Gdk::Event::BUTTON_PRESS and e.button == 3
|
58
58
|
}
|
59
59
|
|
60
|
-
["Name", "Length"].each_with_index { |
|
60
|
+
["Name", "Length"].each_with_index { |x, i|
|
61
61
|
renderer = Gtk::CellRendererText.new
|
62
|
-
column = Gtk::TreeViewColumn.new(
|
63
|
-
|
64
|
-
:text => i)
|
65
|
-
if _x == "Name"
|
62
|
+
column = Gtk::TreeViewColumn.new(x, renderer, :text => i)
|
63
|
+
if x == "Name"
|
66
64
|
renderer.ellipsize = Pango::ELLIPSIZE_MIDDLE
|
67
65
|
column.expand = true
|
68
66
|
column.set_cell_data_func(renderer) { |t, c, m, j|
|
@@ -72,105 +70,87 @@ module Ampv
|
|
72
70
|
|
73
71
|
@treeview.append_column(column)
|
74
72
|
}
|
75
|
-
|
76
|
-
sw.add(@treeview)
|
77
|
-
|
78
|
-
hbox = Gtk::HBox.new(true, 5)
|
73
|
+
@treeview.append_column(Gtk::TreeViewColumn.new("", Gtk::CellRendererPixbuf.new, :pixbuf => 2))
|
79
74
|
|
80
75
|
buttons.each { |k, v|
|
81
76
|
button = Gtk::Button.new
|
77
|
+
item = Gtk::ImageMenuItem.new(k[1])
|
78
|
+
|
82
79
|
button.image = Gtk::Image.new(k[0], Gtk::IconSize::BUTTON)
|
83
80
|
button.height_request = 36
|
84
81
|
button.set_tooltip_text(k[1])
|
85
82
|
button.signal_connect("clicked") { v.call }
|
86
|
-
hbox.pack_start(button)
|
87
|
-
}
|
88
|
-
|
89
|
-
vbox.pack_start(hbox, false)
|
90
83
|
|
91
|
-
@menu = Gtk::Menu.new
|
92
|
-
buttons.each { |k, v|
|
93
|
-
item = Gtk::ImageMenuItem.new(k[1])
|
94
84
|
item.image = Gtk::Image.new(k[0], Gtk::IconSize::MENU)
|
95
85
|
item.signal_connect("activate") { v.call }
|
86
|
+
|
87
|
+
hbox.pack_start(button)
|
96
88
|
@menu.append(item)
|
97
89
|
}
|
90
|
+
|
91
|
+
sw.add(@treeview)
|
92
|
+
vbox.pack_start(sw)
|
93
|
+
vbox.pack_start(hbox, false)
|
94
|
+
add(vbox)
|
98
95
|
@menu.show_all
|
99
96
|
|
100
|
-
show_all if
|
97
|
+
show_all if Config["playlist_visible"]
|
101
98
|
end
|
102
99
|
|
103
100
|
def count
|
104
101
|
i = 0
|
105
102
|
@model.each { i += 1 }
|
106
|
-
|
103
|
+
i
|
107
104
|
end
|
108
105
|
|
109
|
-
def add_file(file)
|
110
|
-
|
111
|
-
@model.each { |m, p, iter|
|
112
|
-
if iter[0] == file
|
113
|
-
contains = true
|
114
|
-
break
|
115
|
-
end
|
116
|
-
}
|
117
|
-
unless contains
|
106
|
+
def add_file(file, length = nil, watched = false)
|
107
|
+
unless include?(file)
|
118
108
|
iter = @model.append
|
119
109
|
iter[0] = file
|
110
|
+
iter[1] = length if length
|
111
|
+
iter[2] = WATCHED_PIXBUF if watched
|
120
112
|
end
|
121
113
|
end
|
122
114
|
|
123
|
-
def
|
124
|
-
@
|
125
|
-
tmp = path.dup
|
126
|
-
break if not tmp.prev! or @treeview.selection.selected_rows.include?(tmp)
|
127
|
-
@model.move_before(@model.get_iter(path), @model.get_iter(tmp))
|
128
|
-
}
|
115
|
+
def on_playing_watched
|
116
|
+
@playing_iter[2] = WATCHED_PIXBUF
|
129
117
|
end
|
130
118
|
|
131
|
-
def
|
132
|
-
@
|
133
|
-
|
134
|
-
tmp = @model.get_iter(tmp)
|
135
|
-
@model.move_after(@model.get_iter(path), tmp) unless tmp.nil?
|
136
|
-
}
|
137
|
-
end
|
138
|
-
|
139
|
-
def remove_selected
|
140
|
-
to_remove = [ ]
|
141
|
-
@treeview.selection.selected_rows.each { |path|
|
142
|
-
to_remove.push(@model.get_iter(path))
|
143
|
-
}
|
144
|
-
to_remove.each { |iter|
|
145
|
-
signal_emit("playing_removed") if iter[0] == @playing
|
146
|
-
@model.remove(iter)
|
147
|
-
}
|
119
|
+
def include?(file)
|
120
|
+
@model.each { |m, p, iter| return true if iter[0] == file }
|
121
|
+
false
|
148
122
|
end
|
149
123
|
|
150
124
|
def get_next
|
151
125
|
@model.each { |m, p, iter|
|
152
|
-
return iter.next! ? iter[0] : nil if iter
|
126
|
+
return iter.next! ? iter[0] : nil if iter == @playing_iter
|
153
127
|
}
|
154
|
-
|
128
|
+
nil
|
155
129
|
end
|
156
130
|
|
157
131
|
def get_prev
|
158
132
|
prev = nil
|
159
133
|
@model.each { |m, p, iter|
|
160
|
-
return prev if iter
|
134
|
+
return prev if iter == @playing_iter
|
161
135
|
prev = iter[0]
|
162
136
|
}
|
163
137
|
end
|
164
138
|
|
165
139
|
def get_entries
|
166
140
|
entries = [ ]
|
167
|
-
@model.each { |m, p, iter|
|
168
|
-
|
141
|
+
@model.each { |m, p, iter|
|
142
|
+
entries << {
|
143
|
+
"file" => iter[0],
|
144
|
+
"length" => iter[1] ? iter[1] : "",
|
145
|
+
"watched" => !iter[2].nil?
|
146
|
+
}
|
147
|
+
}
|
148
|
+
entries
|
169
149
|
end
|
170
150
|
|
171
|
-
def clear
|
151
|
+
def clear(quiet = false)
|
172
152
|
@model.clear
|
173
|
-
signal_emit("playing_removed")
|
153
|
+
signal_emit("playing_removed") unless quiet or @playing_iter.nil?
|
174
154
|
end
|
175
155
|
|
176
156
|
def set_selected(file)
|
@@ -179,6 +159,7 @@ module Ampv
|
|
179
159
|
@model.each { |m, p, iter|
|
180
160
|
if iter[0] == @playing
|
181
161
|
@treeview.set_cursor(Gtk::TreePath.new(i), nil, false)
|
162
|
+
@playing_iter = iter
|
182
163
|
break
|
183
164
|
end
|
184
165
|
i += 1
|
@@ -186,15 +167,40 @@ module Ampv
|
|
186
167
|
end
|
187
168
|
|
188
169
|
def update_length(length)
|
189
|
-
|
190
|
-
|
191
|
-
iter[1] = Time.at(length).utc.strftime("%H:%M:%S") unless length == 0
|
192
|
-
break
|
193
|
-
end
|
194
|
-
}
|
170
|
+
return if length == 0
|
171
|
+
@playing_iter[1] = Time.at(length).utc.strftime("%H:%M:%S") if @playing_iter
|
195
172
|
end
|
196
173
|
|
197
174
|
private
|
175
|
+
def move_selected_up
|
176
|
+
@treeview.selection.selected_rows.each { |path|
|
177
|
+
tmp = path.dup
|
178
|
+
# prev!: Returns: true if path has a previous node, and the move was made.
|
179
|
+
break unless tmp.prev! or @treeview.selection.selected_rows.include?(tmp)
|
180
|
+
@model.move_before(@model.get_iter(path), @model.get_iter(tmp))
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
def move_selected_down
|
185
|
+
@treeview.selection.selected_rows.reverse.each { |path|
|
186
|
+
# next!: Moves the path to point to the next node at the current depth. Returns self
|
187
|
+
break if @treeview.selection.selected_rows.include?(tmp = path.dup.next!)
|
188
|
+
tmp = @model.get_iter(tmp)
|
189
|
+
@model.move_after(@model.get_iter(path), tmp) unless tmp.nil?
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
def remove_selected
|
194
|
+
to_remove = [ ]
|
195
|
+
@treeview.selection.selected_rows.each { |path|
|
196
|
+
to_remove << @model.get_iter(path)
|
197
|
+
}
|
198
|
+
to_remove.each { |iter|
|
199
|
+
signal_emit("playing_removed") if iter == @playing_iter
|
200
|
+
@model.remove(iter)
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
198
204
|
def signal_do_play_entry(file) end
|
199
205
|
def signal_do_playing_removed() end
|
200
206
|
def signal_do_open_file_chooser() end
|
@@ -1,30 +1,27 @@
|
|
1
|
-
|
2
1
|
module Ampv
|
3
2
|
class ProgressBarWidget < Gtk::DrawingArea
|
4
|
-
|
5
|
-
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
super
|
6
6
|
modify_bg(Gtk::STATE_NORMAL, Gdk::Color.parse("#000"))
|
7
|
-
set_size_request(-1,
|
7
|
+
set_size_request(-1, Config["progress_bar_height"])
|
8
8
|
|
9
|
-
signal_connect("expose_event") {
|
10
|
-
@cx = window.create_cairo_context
|
11
|
-
draw_widget
|
12
|
-
}
|
13
9
|
@value = 0
|
14
|
-
@bar_color = bar_color
|
15
|
-
@head_color = head_color
|
16
|
-
end
|
10
|
+
@bar_color = Config["bar_color"]
|
11
|
+
@head_color = Config["head_color"]
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if @value > 0
|
24
|
-
@cx.set_source_color(@head_color)
|
25
|
-
@cx.rectangle((allocation.width * @value.to_f).round, 0, 2, allocation.height)
|
13
|
+
signal_connect("expose_event") {
|
14
|
+
@cx = window.create_cairo_context
|
15
|
+
@cx.set_source_color(@bar_color)
|
16
|
+
@cx.rectangle(0, 0, (allocation.width * @value.to_f).round, allocation.height)
|
26
17
|
@cx.fill
|
27
|
-
|
18
|
+
|
19
|
+
if @value > 0
|
20
|
+
@cx.set_source_color(@head_color)
|
21
|
+
@cx.rectangle((allocation.width * @value.to_f).round, 0, 2, allocation.height)
|
22
|
+
@cx.fill
|
23
|
+
end
|
24
|
+
}
|
28
25
|
end
|
29
26
|
|
30
27
|
def value=(v)
|
data/lib/ampv/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ampv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ahoka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gtk2
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: ruby-fifo
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -47,10 +61,11 @@ extra_rdoc_files: []
|
|
47
61
|
files:
|
48
62
|
- LICENSE
|
49
63
|
- input.conf
|
64
|
+
- lib/ampv/config.rb
|
50
65
|
- lib/ampv/playlist.rb
|
51
|
-
- lib/ampv/mpvwidget.rb
|
52
66
|
- lib/ampv/version.rb
|
53
67
|
- lib/ampv/progressbarwidget.rb
|
68
|
+
- lib/ampv/mpvwidget.rb
|
54
69
|
- lib/ampv.rb
|
55
70
|
- bin/ampv
|
56
71
|
homepage: https://github.com/ahodesuka/ampv
|
@@ -72,8 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
87
|
- !ruby/object:Gem::Version
|
73
88
|
version: '0'
|
74
89
|
requirements:
|
75
|
-
-
|
76
|
-
- mpv
|
90
|
+
- mpv v0.3.x
|
77
91
|
rubyforge_project:
|
78
92
|
rubygems_version: 2.0.3
|
79
93
|
signing_key:
|