serial-watcher 0.2.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/.gitignore +4 -0
- data/bin/serial-watcher +13 -0
- data/serial-watcher.gemspec +19 -0
- data/src/Gemfile +8 -0
- data/src/episode.rb +12 -0
- data/src/fs.rb +103 -0
- data/src/gui/list_model.rb +35 -0
- data/src/gui/list_view.rb +46 -0
- data/src/gui/main.rb +224 -0
- data/src/gui/meta_dialog.rb +42 -0
- data/src/gui/settings_dialog.rb +44 -0
- data/src/gui/title_box.rb +73 -0
- data/src/gui/torrent_panel.rb +42 -0
- data/src/main.rb +53 -0
- data/src/meta.rb +43 -0
- data/src/player.rb +58 -0
- data/src/plugins/torrent/main.rb +29 -0
- data/src/plugins/torrent/torrentz.rb +25 -0
- data/src/plugins/torrent/tpb.rb +25 -0
- data/src/serial.rb +27 -0
- data/src/status.rb +86 -0
- data/static/images/app.png +0 -0
- data/static/images/dash.png +0 -0
- data/static/images/fullscreen.png +0 -0
- data/static/images/round.png +0 -0
- data/tmp/install.rb +38 -0
- data/tmp/sendkey.sh +5 -0
- data/tmp/vimsession.vim +2348 -0
- metadata +169 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module Gui
|
3
|
+
class MetaDialog < Gtk::Dialog
|
4
|
+
|
5
|
+
type_register
|
6
|
+
def initialize(hash)
|
7
|
+
serial = hash[:serial]
|
8
|
+
hash = nil
|
9
|
+
super
|
10
|
+
|
11
|
+
|
12
|
+
set_title "Serial Meta Edit"
|
13
|
+
add_buttons [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_OK],
|
14
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
|
15
|
+
has_separator = false
|
16
|
+
|
17
|
+
table = Gtk::Table.new 5, 2, true
|
18
|
+
table.border_width = 10
|
19
|
+
|
20
|
+
entry = Gtk::Entry.new
|
21
|
+
@status = Status.instance
|
22
|
+
|
23
|
+
# 1st row
|
24
|
+
label = Gtk::Label.new("Name: ")
|
25
|
+
@name_entry = Gtk::Entry.new
|
26
|
+
@name_entry.text = serial.meta.name || serial.name
|
27
|
+
table.attach label, 0, 1, 0, 1
|
28
|
+
table.attach @name_entry, 1, 2, 0, 1
|
29
|
+
|
30
|
+
# 2nd row
|
31
|
+
|
32
|
+
vbox.add(table)
|
33
|
+
show_all
|
34
|
+
end
|
35
|
+
|
36
|
+
def values
|
37
|
+
data = {
|
38
|
+
:name => @name_entry.text
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
module Gui
|
3
|
+
class SettingsDialog < Gtk::Dialog
|
4
|
+
|
5
|
+
type_register
|
6
|
+
def initialize()
|
7
|
+
super
|
8
|
+
set_title "Serial Watcher Settings"
|
9
|
+
add_buttons [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_OK],
|
10
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
|
11
|
+
has_separator = false
|
12
|
+
|
13
|
+
table = Gtk::Table.new 5, 2, true
|
14
|
+
table.border_width = 10
|
15
|
+
|
16
|
+
@status = Status.instance
|
17
|
+
@original_path = @status.data_dir
|
18
|
+
|
19
|
+
entry = Gtk::Entry.new
|
20
|
+
|
21
|
+
# 1st row
|
22
|
+
label = Gtk::Label.new("Serials directory: ")
|
23
|
+
@path_chooser = Gtk::FileChooserButton.new( "Choose a Folder", Gtk::FileChooser::ACTION_SELECT_FOLDER)
|
24
|
+
@path_chooser.current_folder = @status.data_dir
|
25
|
+
table.attach label, 0, 1, 0, 1
|
26
|
+
table.attach @path_chooser, 1, 2, 0, 1
|
27
|
+
|
28
|
+
# 2nd row
|
29
|
+
|
30
|
+
vbox.add(table)
|
31
|
+
show_all
|
32
|
+
end
|
33
|
+
|
34
|
+
def path_changed?
|
35
|
+
@original_path != @path_chooser.current_folder
|
36
|
+
end
|
37
|
+
|
38
|
+
def values
|
39
|
+
data = {
|
40
|
+
:path => @path_chooser.current_folder
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
require 'serial'
|
3
|
+
require 'fs'
|
4
|
+
|
5
|
+
module Gui
|
6
|
+
class TitleBox < Gtk::VBox
|
7
|
+
type_register
|
8
|
+
attr_accessor :serials
|
9
|
+
|
10
|
+
signal_new("title_changed", GLib::Signal::RUN_FIRST, nil, nil, Serial)
|
11
|
+
signal_new("loaded", GLib::Signal::RUN_FIRST, nil, nil, false)
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@colormap = Gtk::Widget.default_colormap
|
15
|
+
@fs = Fs.instance
|
16
|
+
@status = Status.instance
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear
|
21
|
+
self.each_forall do |e|
|
22
|
+
remove(e) if e.class.to_s == "Gtk::Button"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load(dir = @status.data_dir)
|
27
|
+
clear
|
28
|
+
|
29
|
+
@fs.init dir
|
30
|
+
series = @fs.series || []
|
31
|
+
series.each do |serial|
|
32
|
+
|
33
|
+
# button
|
34
|
+
button = Gtk::Button.new " #{serial.name} "
|
35
|
+
button.use_stock = true
|
36
|
+
button.set_xalign 0.0
|
37
|
+
button.set_image Gtk::Image.new( serial.image || File.join($STATIC_PATH, 'images/dash.png') )
|
38
|
+
button.set_image_position Gtk::POS_LEFT
|
39
|
+
|
40
|
+
button.signal_connect "clicked" do
|
41
|
+
self.modify_current_button button
|
42
|
+
self.signal_emit( "title_changed", serial )
|
43
|
+
end
|
44
|
+
|
45
|
+
if serial.path == @status.get_last_serial
|
46
|
+
button.signal_emit "clicked"
|
47
|
+
end
|
48
|
+
|
49
|
+
pack_start button, false, false, 3
|
50
|
+
end
|
51
|
+
self.show_all
|
52
|
+
self.signal_emit( "loaded" , false)
|
53
|
+
end
|
54
|
+
|
55
|
+
def modify_current_button button
|
56
|
+
self.each_forall do |btn|
|
57
|
+
if btn.class.to_s == "Gtk::Button"
|
58
|
+
btn.modify_bg(Gtk::STATE_NORMAL, nil)
|
59
|
+
btn.modify_bg(Gtk::STATE_PRELIGHT, nil)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
button.modify_bg(Gtk::STATE_NORMAL, Gdk::Color.new(45535, 45535, 65535))
|
63
|
+
button.modify_bg(Gtk::STATE_PRELIGHT, Gdk::Color.new(40535, 40535, 65535))
|
64
|
+
end
|
65
|
+
|
66
|
+
def signal_do_title_changed(arg)
|
67
|
+
end
|
68
|
+
|
69
|
+
def signal_do_loaded(arg)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Gui
|
5
|
+
class TorrentPanel < Gtk::VBox
|
6
|
+
|
7
|
+
type_register
|
8
|
+
|
9
|
+
def initialize()
|
10
|
+
height_request = 50
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear
|
15
|
+
each_forall do |e| remove(e) end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load(serial)
|
19
|
+
clear
|
20
|
+
|
21
|
+
Thread.new do
|
22
|
+
@torrent = ::Torrent::Torrentz.instance
|
23
|
+
@torrent.load serial.meta.name
|
24
|
+
@torrent.get_last_feeds.each do |torrent|
|
25
|
+
|
26
|
+
torrent_link = Gtk::LinkButton.new torrent[:url], "#{torrent[:date]} / #{torrent[:name]}"
|
27
|
+
|
28
|
+
torrent_link.set_alignment 0, 0.5
|
29
|
+
torrent_link.signal_connect "clicked" do |link_btn|
|
30
|
+
Thread.new do
|
31
|
+
`chromium #{link_btn.uri}`
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
pack_start_defaults torrent_link#, true, true #false, false
|
36
|
+
|
37
|
+
end
|
38
|
+
show_all
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/src/main.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH << '.'
|
3
|
+
|
4
|
+
require 'lockfile'
|
5
|
+
require 'gui/main'
|
6
|
+
|
7
|
+
$DATADIR = ''
|
8
|
+
$INITIAL_SERIAL = ""
|
9
|
+
$INITIAL_EPISODE = 0
|
10
|
+
|
11
|
+
$DEBUG = false
|
12
|
+
#$DEBUG = true
|
13
|
+
|
14
|
+
class SerialWatcher
|
15
|
+
def initialize
|
16
|
+
begin
|
17
|
+
Lockfile("/tmp/serial-watcher.lock", :retries => 0, :debug => $DEBUG, :poll_retries => 1) do
|
18
|
+
Gtk.init
|
19
|
+
window = Gui::Main.new
|
20
|
+
window.show_all
|
21
|
+
Gtk.main
|
22
|
+
end
|
23
|
+
rescue Lockfile::MaxTriesLockError
|
24
|
+
notify "Another instance is running"
|
25
|
+
self.raise_window
|
26
|
+
end
|
27
|
+
self.quit
|
28
|
+
end
|
29
|
+
|
30
|
+
def raise_window
|
31
|
+
begin
|
32
|
+
window_id = `xdotool search --name "Serial\ Watcher"`
|
33
|
+
`xdotool windowraise #{window_id}`
|
34
|
+
`xdotool windowfocus #{window_id}`
|
35
|
+
rescue
|
36
|
+
notify "you need to install xdotool to raise opened window"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def quit
|
41
|
+
log "Quiting .."
|
42
|
+
Kernel.exit(1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify(message)
|
47
|
+
`notify-send '#{message}'`
|
48
|
+
end
|
49
|
+
|
50
|
+
def log(message)
|
51
|
+
puts message
|
52
|
+
end
|
53
|
+
|
data/src/meta.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
class Meta
|
3
|
+
def initialize(serial)
|
4
|
+
@serial = serial
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
@name ||= (@name.nil? ? @serial.name : @name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
"#{@serial.path}/.meta"
|
13
|
+
end
|
14
|
+
|
15
|
+
def meta_data
|
16
|
+
data = {
|
17
|
+
:name => name
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_file
|
22
|
+
begin
|
23
|
+
File.open(path, 'w') { |f| f.write( meta_data.to_yaml ) }
|
24
|
+
rescue
|
25
|
+
puts "cannot create meta-file"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def load_file
|
30
|
+
meta = File.exists?(path) ? YAML.load(File.open(path)) : meta_data
|
31
|
+
meta_data.each { |key, value|
|
32
|
+
eval("@#{key} = ?", meta.try('[]', key))
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def save_file meta
|
37
|
+
meta_data.each do |key, value|
|
38
|
+
eval("@#{key} = ?", meta.try('[]', key))
|
39
|
+
end
|
40
|
+
create_file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/src/player.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'gtk2'
|
3
|
+
|
4
|
+
require 'status'
|
5
|
+
|
6
|
+
class Player < Gtk::Object
|
7
|
+
|
8
|
+
include Singleton
|
9
|
+
type_register
|
10
|
+
|
11
|
+
attr_accessor :status
|
12
|
+
|
13
|
+
signal_new("playing_ended", GLib::Signal::RUN_FIRST, nil, nil, String)
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
@status = Status.instance
|
18
|
+
end
|
19
|
+
|
20
|
+
def play_episode episode
|
21
|
+
@status.save_status episode
|
22
|
+
begin
|
23
|
+
@thr = Thread.new do
|
24
|
+
|
25
|
+
Thread.abort_on_exception = true
|
26
|
+
#s = `mplayer -identify -fs \'#{episode.path}\' 2>&1 | tail -n 1`
|
27
|
+
`killall mplayer 2>&1 > /dev/null`
|
28
|
+
s = `mplayer -identify \'#{episode.path}\' #{"-fs" if @status.data[:fullscreen]} 2>&1 | tail -n 1`
|
29
|
+
if s == "ID_EXIT=EOF\n"
|
30
|
+
signal_emit("playing_ended", "param")
|
31
|
+
#raise "playing ended"
|
32
|
+
end
|
33
|
+
|
34
|
+
Thread.exit
|
35
|
+
end
|
36
|
+
|
37
|
+
Thread.new do
|
38
|
+
sleep 2
|
39
|
+
4.times do
|
40
|
+
# TODO fix it all
|
41
|
+
m_id=`xwininfo -name MPlayer |grep 'Window id:' |cut -d" " -f4`.strip
|
42
|
+
cmd = "xdotool key --window #{m_id} 114"
|
43
|
+
`#{cmd}`
|
44
|
+
end
|
45
|
+
Thread.exit
|
46
|
+
end
|
47
|
+
|
48
|
+
rescue
|
49
|
+
puts "resque"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def signal_do_playing_ended param
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require 'simple-rss'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module Torrent
|
7
|
+
class Main
|
8
|
+
attr_reader :url, :rss
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@data = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def rss_load
|
16
|
+
@rss = @data[@name] || (@data[@name] = SimpleRSS.parse open(@url) if @url)
|
17
|
+
end
|
18
|
+
|
19
|
+
def human_format
|
20
|
+
raise "Method need to be overloaded"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def get_last_feeds(num = 5, trim = 40)
|
25
|
+
return [] if not @rss
|
26
|
+
human_format(@rss.channel.items.sort{|b,a| a[:pubDate] <=> b[:pubDate]}[0,5], trim)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
require 'plugins/torrent/main'
|
4
|
+
|
5
|
+
module Torrent
|
6
|
+
class Torrentz < Main
|
7
|
+
|
8
|
+
def load(name)
|
9
|
+
@name = name
|
10
|
+
@url = ("http://torrentz.eu/feed?q=#{CGI.escape(name)}" if name) || nil
|
11
|
+
rss_load if @url
|
12
|
+
end
|
13
|
+
|
14
|
+
def human_format list, trim
|
15
|
+
list.map do |item|
|
16
|
+
nitem = {}
|
17
|
+
nitem[:name] = item[:title].length > trim ? "#{item[:title][0, trim]}.." : item[:title]
|
18
|
+
nitem[:date] = "#{(Date.today - item[:pubDate].to_date).to_i} days ago"
|
19
|
+
nitem[:url] = item[:link]
|
20
|
+
nitem
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require 'date'
|
3
|
+
require 'plugins/torrent/main'
|
4
|
+
|
5
|
+
module Torrent
|
6
|
+
class TPB < Main
|
7
|
+
|
8
|
+
def load(name)
|
9
|
+
@name = name
|
10
|
+
@url = ""#http://torrentz.eu/feed?q=#{CGI.escape(name)}"
|
11
|
+
rss_load
|
12
|
+
end
|
13
|
+
|
14
|
+
def human_format list, trim
|
15
|
+
list.map do |item|
|
16
|
+
nitem = {}
|
17
|
+
nitem[:name] = "#{item[:title][0, trim]}.." if item[:title].length > trim
|
18
|
+
nitem[:date] = item[:pubDate]
|
19
|
+
nitem[:url] = item[:link]
|
20
|
+
nitem
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|