shroom 0.0.1
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/LICENSE +674 -0
- data/README +16 -0
- data/Rakefile +51 -0
- data/bin/shroom +4 -0
- data/lib/sh_browse.rb +122 -0
- data/lib/sh_cover_art.rb +34 -0
- data/lib/sh_database.rb +86 -0
- data/lib/sh_global.rb +54 -0
- data/lib/sh_lyrics.rb +24 -0
- data/lib/sh_main.rb +27 -0
- data/lib/sh_player.rb +164 -0
- data/lib/sh_song.rb +164 -0
- data/lib/sh_util.rb +21 -0
- data/lib/sh_view.rb +430 -0
- data/lib/shroom-res/icon_16x16.png +0 -0
- data/lib/shroom-res/shroom.glade +193 -0
- metadata +123 -0
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
spec = Gem::Specification.new do |s|
|
9
|
+
s.name = 'shroom'
|
10
|
+
s.version = '0.0.1'
|
11
|
+
s.has_rdoc = true
|
12
|
+
s.extra_rdoc_files = ['README', 'LICENSE']
|
13
|
+
s.summary = 'Shroom is a music player and organizer'
|
14
|
+
s.description = s.summary
|
15
|
+
s.author = 'Aiden Nibali'
|
16
|
+
s.email = 'dismal.denizen@gmail.com'
|
17
|
+
s.rubyforge_project = 'shroom'
|
18
|
+
s.homepage = 'http://shroom.rubyforge.org'
|
19
|
+
s.add_dependency('ruby-mp3info', '>= 0.6.13')
|
20
|
+
s.add_dependency('ruby-ogginfo', '>= 0.3.2')
|
21
|
+
s.add_dependency('flacinfo-rb', '>= 0.4')
|
22
|
+
s.add_dependency('earworm', '>= 0.0.2')
|
23
|
+
s.add_dependency('sequel', '>= 3.2.0')
|
24
|
+
s.requirements << 'libgstreamer0.10-ruby'
|
25
|
+
s.requirements << 'libgnome2-ruby'
|
26
|
+
s.requirements << 'libsqlite3-ruby'
|
27
|
+
s.requirements << 'libglade2-ruby'
|
28
|
+
s.executables = ['shroom']
|
29
|
+
s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
|
30
|
+
s.require_path = "lib"
|
31
|
+
s.bindir = "bin"
|
32
|
+
end
|
33
|
+
|
34
|
+
Rake::GemPackageTask.new(spec) do |p|
|
35
|
+
p.gem_spec = spec
|
36
|
+
p.need_tar = true
|
37
|
+
p.need_zip = true
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
files =['README', 'LICENSE', 'lib/**/*.rb']
|
42
|
+
rdoc.rdoc_files.add(files)
|
43
|
+
rdoc.main = "README" # page to start on
|
44
|
+
rdoc.title = "Shroom Docs"
|
45
|
+
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
|
46
|
+
rdoc.options << '--line-numbers'
|
47
|
+
end
|
48
|
+
|
49
|
+
Rake::TestTask.new do |t|
|
50
|
+
t.test_files = FileList['test/**/*.rb']
|
51
|
+
end
|
data/bin/shroom
ADDED
data/lib/sh_browse.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Sh
|
2
|
+
class Browse
|
3
|
+
def initialize view
|
4
|
+
@tree = TreeView.new
|
5
|
+
@tree.selection.mode = Gtk::SELECTION_MULTIPLE
|
6
|
+
|
7
|
+
ren_artist = CellRendererText.new
|
8
|
+
col_artist = TreeViewColumn.new('Songs', ren_artist)
|
9
|
+
col_artist.set_cell_data_func(ren_artist) do |tvc, cell, model, iter|
|
10
|
+
cell.text = ''
|
11
|
+
data = iter[0]
|
12
|
+
cell.text = data if data.is_a? String
|
13
|
+
if data.is_a? Sh::Song
|
14
|
+
if data.title
|
15
|
+
cell.text = sprintf("%.2d\t%s", data.track_num, data.title)
|
16
|
+
else
|
17
|
+
cell.text = data.path.split('/').last
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@tree.append_column col_artist
|
22
|
+
|
23
|
+
@model = TreeStore.new(Object)
|
24
|
+
@tree.model = @model
|
25
|
+
|
26
|
+
GLib::Timeout.add(1000) do
|
27
|
+
fill_model
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
@tree.signal_connect('button-press-event') do |widget, event|
|
32
|
+
# Right-click
|
33
|
+
if event.button == 3
|
34
|
+
path = @tree.get_path_at_pos(event.x, event.y).first
|
35
|
+
data = @tree.model.get_iter(path)[0]
|
36
|
+
selection = @tree.selection
|
37
|
+
selected_rows = selection.selected_rows
|
38
|
+
clicked_in_selection = false
|
39
|
+
selected_rows.each do |sel|
|
40
|
+
clicked_in_selection = sel.indices == path.indices
|
41
|
+
break if clicked_in_selection
|
42
|
+
end
|
43
|
+
|
44
|
+
unless clicked_in_selection
|
45
|
+
# Change the selection
|
46
|
+
selection.unselect_all
|
47
|
+
selection.select_path(path)
|
48
|
+
selected_rows = selection.selected_rows
|
49
|
+
end
|
50
|
+
|
51
|
+
puts selected_rows
|
52
|
+
|
53
|
+
if data.is_a? Sh::Song
|
54
|
+
song = data
|
55
|
+
menu = Menu.new
|
56
|
+
|
57
|
+
pbtn_lookup = MenuItem.new 'Lookup metadata automatically'
|
58
|
+
menu.append pbtn_lookup
|
59
|
+
pbtn_lookup.signal_connect('activate') do |widget|
|
60
|
+
song.lookup!
|
61
|
+
$db.save_song song
|
62
|
+
@model.clear
|
63
|
+
fill_model
|
64
|
+
end
|
65
|
+
|
66
|
+
menu.show_all
|
67
|
+
menu.popup(nil, nil, event.button, event.time)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@tree.signal_connect('row_activated') do |widget, path, col|
|
72
|
+
iter = @tree.model.get_iter(path)
|
73
|
+
parent = iter.parent
|
74
|
+
song = iter[0]
|
75
|
+
if song.is_a? Sh::Song
|
76
|
+
view.stop
|
77
|
+
queue = []
|
78
|
+
while iter.parent == parent
|
79
|
+
queue << iter[0]
|
80
|
+
iter.next!
|
81
|
+
end
|
82
|
+
view.queue = queue
|
83
|
+
view.play
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
@scroll = ScrolledWindow.new(nil, nil)
|
88
|
+
@scroll.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
|
89
|
+
@scroll.add @tree
|
90
|
+
end
|
91
|
+
|
92
|
+
def fill_model
|
93
|
+
artist_node = album_node = track_node = nil
|
94
|
+
$db.songs.sort_by {|a| a.to_s}.each do |song|
|
95
|
+
artist = song.artist || '!Unknown Artist!'
|
96
|
+
if not artist_node or artist_node[0] != artist
|
97
|
+
artist_node = @model.append nil
|
98
|
+
artist_node[0] = artist
|
99
|
+
# Required for proper handling of multiple artists with on album
|
100
|
+
album_node = nil
|
101
|
+
#artist_node[1] = []
|
102
|
+
end
|
103
|
+
#artist_node[1] << song
|
104
|
+
|
105
|
+
album = song.album || '!Unknown Album!'
|
106
|
+
if not album_node or album_node[0] != album
|
107
|
+
album_node = @model.append artist_node
|
108
|
+
album_node[0] = album
|
109
|
+
#album_node[1] = []
|
110
|
+
end
|
111
|
+
#album_node[1] << song
|
112
|
+
|
113
|
+
track_node = @model.append album_node
|
114
|
+
track_node[0] = song
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def widget
|
119
|
+
@scroll
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/sh_cover_art.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Sh
|
6
|
+
class CoverArt
|
7
|
+
def CoverArt.get_cover song
|
8
|
+
if $prefs[:cover_art]
|
9
|
+
artist, album = song.artist, song.album
|
10
|
+
path = "#{$cover_dir}/#{artist} - #{album}"
|
11
|
+
return path if File.exists? path
|
12
|
+
doc = lastfm("album.getInfo", {:artist => artist, :album => album})
|
13
|
+
img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
|
14
|
+
if img_url
|
15
|
+
open(path, 'w') do |output|
|
16
|
+
open(img_url) do |input|
|
17
|
+
output << input.read
|
18
|
+
end
|
19
|
+
end
|
20
|
+
return path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def CoverArt.lastfm(method, arg_map = {}.freeze)
|
28
|
+
args = arg_map.collect { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}"}.join('&')
|
29
|
+
url = "http://ws.audioscrobbler.com/2.0/?method=#{method}&api_key=#{Sh::KEYS[:lastfm]}&#{args}"
|
30
|
+
doc = REXML::Document.new(open(url).read)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/sh_database.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sequel'
|
3
|
+
require 'sh_song'
|
4
|
+
|
5
|
+
module Sh
|
6
|
+
class Database
|
7
|
+
def initialize
|
8
|
+
@db = Sequel.sqlite("#{$config_dir}/songs.db")
|
9
|
+
|
10
|
+
@db.create_table :songs do
|
11
|
+
primary_key :id
|
12
|
+
String :path
|
13
|
+
String :title
|
14
|
+
String :lyrics
|
15
|
+
String :image
|
16
|
+
String :year
|
17
|
+
String :album
|
18
|
+
String :artist
|
19
|
+
String :track_num
|
20
|
+
end unless @db.table_exists? :songs
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_song song
|
24
|
+
hash = {
|
25
|
+
:path => song.path,
|
26
|
+
:title => song.title,
|
27
|
+
:lyrics => song.lyrics,
|
28
|
+
:image => song.image,
|
29
|
+
:year => song.year,
|
30
|
+
:album => song.album,
|
31
|
+
:artist => song.artist,
|
32
|
+
:track_num => song.track_num
|
33
|
+
}
|
34
|
+
|
35
|
+
song_id = nil
|
36
|
+
row_song = @db[:songs][:path => song.path]
|
37
|
+
if row_song
|
38
|
+
hash.each do |k, v|
|
39
|
+
if row_song[k] != v
|
40
|
+
@db[:songs].filter(:path => song.path).update(k => v)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
song_id = row_song[:id]
|
44
|
+
else
|
45
|
+
# Create row in db for song
|
46
|
+
song_id = @db[:songs].insert(hash)
|
47
|
+
# Retrieve row to return
|
48
|
+
row_song = @db[:songs][:id => song_id]
|
49
|
+
end
|
50
|
+
|
51
|
+
return row_song
|
52
|
+
end
|
53
|
+
|
54
|
+
def songs
|
55
|
+
songs = []
|
56
|
+
@db[:songs].each do |hash|
|
57
|
+
songs << hash_to_song(hash)
|
58
|
+
end
|
59
|
+
return songs
|
60
|
+
end
|
61
|
+
|
62
|
+
def contains? path
|
63
|
+
@db[:songs][:path => path]
|
64
|
+
end
|
65
|
+
|
66
|
+
def clean
|
67
|
+
@db[:songs].each do |hash|
|
68
|
+
path = hash[:path]
|
69
|
+
unless File.exists? path
|
70
|
+
puts "Removing: #{path}"
|
71
|
+
@db[:songs].filter(:path => path).delete
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def hash_to_song hash
|
78
|
+
song = Sh::Song.new hash[:path]
|
79
|
+
hash.each do |k, v|
|
80
|
+
assign = (k.to_s + '=').to_sym
|
81
|
+
song.send assign, v if song.respond_to? assign
|
82
|
+
end
|
83
|
+
return song
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/sh_global.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Sh
|
5
|
+
KEYS = {
|
6
|
+
:lastfm => '1a265c70e0ecbe87d51efc5da4bf8a10'.freeze,
|
7
|
+
:music_dns => '5acf4dd55bc341877bacd07878239a82'.freeze
|
8
|
+
}
|
9
|
+
|
10
|
+
class Global
|
11
|
+
def initialize
|
12
|
+
#$KCODE = 'u'
|
13
|
+
# Won't work on Windows
|
14
|
+
$home = ENV['HOME']
|
15
|
+
$config_dir = "#{$home}/.shroom"
|
16
|
+
FileUtils.mkdir $config_dir unless File.exists? $config_dir
|
17
|
+
$cover_dir = "#{$config_dir}/covers"
|
18
|
+
FileUtils.mkdir $cover_dir unless File.exists? $cover_dir
|
19
|
+
@@prefs_file = "#{$config_dir}/preferences.yml"
|
20
|
+
|
21
|
+
$prefs = {
|
22
|
+
:library_dir => "#{$home}/Music",
|
23
|
+
:cover_art => true,
|
24
|
+
:lyrics => true,
|
25
|
+
:lastfm => false,
|
26
|
+
:lastfm_user => nil,
|
27
|
+
:lastfm_password => nil,
|
28
|
+
}
|
29
|
+
|
30
|
+
Global.load_prefs
|
31
|
+
end
|
32
|
+
|
33
|
+
def Global.load_prefs
|
34
|
+
if File.exists?(@@prefs_file)
|
35
|
+
prefs = YAML::load(File.read(@@prefs_file))
|
36
|
+
prefs.each do |k, v|
|
37
|
+
$prefs[k] = v
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def Global.save_prefs
|
43
|
+
File.open(@@prefs_file, 'w') do |f|
|
44
|
+
f.write $prefs.to_yaml
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def Global.locate(file)
|
49
|
+
find_in_load_path("shroom-res/#{file}") || "shroom-res/#{file}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Sh::Global.new
|
data/lib/sh_lyrics.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Sh
|
6
|
+
class Lyrics
|
7
|
+
def Lyrics.get_lyrics song
|
8
|
+
artist, title = song.artist, song.title
|
9
|
+
return "" unless artist and title
|
10
|
+
LyricsWiki.get_lyrics(artist, title)
|
11
|
+
end
|
12
|
+
|
13
|
+
class LyricsWiki
|
14
|
+
def LyricsWiki.get_lyrics(artist, title)
|
15
|
+
url = "http://lyricwiki.org/api.php?func=getSong&fmt=xml" +
|
16
|
+
"&artist=#{CGI.escape(artist)}" +
|
17
|
+
"&song=#{CGI.escape(title)}"
|
18
|
+
doc = REXML::Document.new(open(url).read)
|
19
|
+
REXML::XPath.first(doc, '//lyrics').text
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/sh_main.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Note to self (Mercurial push URL):
|
4
|
+
# http://dismal_denizen@bitbucket.org/dismal_denizen/shroom/
|
5
|
+
|
6
|
+
require 'socket'
|
7
|
+
|
8
|
+
require 'sh_util'
|
9
|
+
require 'sh_global'
|
10
|
+
require 'sh_song'
|
11
|
+
require 'sh_database'
|
12
|
+
require 'sh_player'
|
13
|
+
require 'sh_view'
|
14
|
+
|
15
|
+
module Sh
|
16
|
+
class Main
|
17
|
+
def initialize
|
18
|
+
$db = Sh::Database.new
|
19
|
+
Sh::View.new.show
|
20
|
+
puts 'Saving preferences...'
|
21
|
+
Sh::Global.save_prefs
|
22
|
+
puts 'Bye!'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Sh::Main.new if $0 == __FILE__
|
data/lib/sh_player.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'gst'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'scrobbler'
|
4
|
+
|
5
|
+
module Sh
|
6
|
+
class Player
|
7
|
+
@@scrobble_auth = nil
|
8
|
+
|
9
|
+
Gst.init
|
10
|
+
|
11
|
+
attr_reader :song
|
12
|
+
|
13
|
+
def initialize song
|
14
|
+
@song = song
|
15
|
+
@stopped = false
|
16
|
+
@play_time = 0
|
17
|
+
|
18
|
+
init
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def init
|
23
|
+
# setup the playbin
|
24
|
+
@playbin = Gst::ElementFactory.make('playbin')
|
25
|
+
@playbin.uri = "file://#{@song.path}"
|
26
|
+
@playbin.ready
|
27
|
+
|
28
|
+
# Get duration
|
29
|
+
# FIXME: This duration-getting code results in nasty error messages
|
30
|
+
@playbin.audio_sink = Gst::ElementFactory.make('fakesink')
|
31
|
+
@playbin.play
|
32
|
+
pos = -1
|
33
|
+
while true
|
34
|
+
while (Gtk.events_pending?)
|
35
|
+
Gtk.main_iteration
|
36
|
+
end
|
37
|
+
query = Gst::QueryPosition.new(Gst::Format::TIME)
|
38
|
+
@playbin.query(query)
|
39
|
+
new_pos = query.parse.last / 1000000000.to_f
|
40
|
+
break if pos > 0 and new_pos == pos
|
41
|
+
pos = new_pos
|
42
|
+
end
|
43
|
+
query = Gst::QueryDuration.new(Gst::Format::TIME)
|
44
|
+
@playbin.query(query)
|
45
|
+
@duration = query.parse.last / 1000000000.to_f
|
46
|
+
@playbin.stop
|
47
|
+
|
48
|
+
# setup the sink
|
49
|
+
@playbin.audio_sink = Gst::ElementFactory.make('autoaudiosink')
|
50
|
+
|
51
|
+
@playbin.bus.add_watch do |a, message|
|
52
|
+
case message.type
|
53
|
+
when Gst::Message::Type::ERROR
|
54
|
+
puts "An error occured: #{message.parse_error[0]}"
|
55
|
+
when Gst::Message::Type::EOS
|
56
|
+
pause
|
57
|
+
seek 0
|
58
|
+
@finished_cb.call self if @finished_cb
|
59
|
+
end
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
#@duration = @song.duration if @duration <= 0
|
64
|
+
|
65
|
+
# Make sure pipeline is stopped when program is exited
|
66
|
+
Signal.trap('EXIT') do
|
67
|
+
stop
|
68
|
+
end
|
69
|
+
|
70
|
+
GLib::Timeout.add(1000) do
|
71
|
+
@play_time += 1 if playing?
|
72
|
+
not @stopped
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
public
|
77
|
+
def play
|
78
|
+
@stopped = false
|
79
|
+
@playbin.play
|
80
|
+
end
|
81
|
+
|
82
|
+
def pause
|
83
|
+
@playbin.pause
|
84
|
+
end
|
85
|
+
|
86
|
+
def stop
|
87
|
+
@stopped = true
|
88
|
+
@playbin.stop
|
89
|
+
scrobble! if $prefs[:lastfm]
|
90
|
+
end
|
91
|
+
|
92
|
+
def scrobble!
|
93
|
+
play_time = @play_time
|
94
|
+
if play_time > 240 or (play_time > duration / 2 and duration > 30)
|
95
|
+
auth = @@scrobble_auth
|
96
|
+
if not auth
|
97
|
+
user, pass = $prefs[:lastfm_user], $prefs[:lastfm_password]
|
98
|
+
auth = @@scrobble_auth = Scrobbler::SimpleAuth.new(:user => user, :password => pass)
|
99
|
+
auth.handshake!
|
100
|
+
puts "Auth Status: #{auth.status}"
|
101
|
+
end
|
102
|
+
|
103
|
+
Thread.new do
|
104
|
+
scrobble = Scrobbler::Scrobble.new(
|
105
|
+
:session_id => auth.session_id,
|
106
|
+
:submission_url => auth.submission_url,
|
107
|
+
:artist => song.artist,
|
108
|
+
:track => song.title,
|
109
|
+
:album => song.album,
|
110
|
+
:time => Time.new,
|
111
|
+
:length => play_time,
|
112
|
+
:track_number => song.track_num)
|
113
|
+
|
114
|
+
begin
|
115
|
+
scrobble.submit!
|
116
|
+
puts "Scrobbler Submission Status: #{scrobble.status}"
|
117
|
+
#user = Scrobbler::User.new($prefs[:lastfm_user])
|
118
|
+
#user.recent_tracks.each { |t| puts t.name }
|
119
|
+
rescue
|
120
|
+
puts "Scrobble oopsie"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def duration
|
127
|
+
@duration
|
128
|
+
end
|
129
|
+
|
130
|
+
def playing?
|
131
|
+
@playbin and @playbin.get_state.include? Gst::STATE_PLAYING
|
132
|
+
end
|
133
|
+
|
134
|
+
def paused?
|
135
|
+
@playbin and @playbin.get_state.include? Gst::STATE_PAUSED
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get the position (in seconds) that playback is up to
|
139
|
+
def position
|
140
|
+
if not playing? and not paused?
|
141
|
+
return 0
|
142
|
+
else
|
143
|
+
query = Gst::QueryPosition.new(Gst::Format::TIME)
|
144
|
+
@playbin.query(query)
|
145
|
+
pos = query.parse.last
|
146
|
+
if pos < 0
|
147
|
+
return duration
|
148
|
+
else
|
149
|
+
return pos / 1000000000.to_f
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set the position (in seconds) to start playback from
|
155
|
+
def seek pos
|
156
|
+
@playbin.seek 1.0, Gst::Format::TIME, Gst::SeekFlags::FLUSH, Gst::SeekType::SET,
|
157
|
+
pos * 1000000000, Gst::SeekType::NONE, -1
|
158
|
+
end
|
159
|
+
|
160
|
+
def on_finished &block
|
161
|
+
@finished_cb = block
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|