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
data/.gitignore
ADDED
data/bin/serial-watcher
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path(File.join(__FILE__, "../../", "src"))
|
4
|
+
|
5
|
+
$STATIC_PATH = File.expand_path(File.join(__FILE__, "../../static/"))
|
6
|
+
|
7
|
+
require 'main'
|
8
|
+
|
9
|
+
SerialWatcher.new
|
10
|
+
|
11
|
+
# #!/bin/bash -l
|
12
|
+
# cd "`dirname $0`/../src"
|
13
|
+
# bundle exec ruby serialwatcher.rb
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'serial-watcher'
|
3
|
+
s.version = '0.2.1'
|
4
|
+
s.date = '2012-09-17'
|
5
|
+
s.summary = "Serial Watcher"
|
6
|
+
s.description = "Soft to watch serials :)"
|
7
|
+
s.authors = ["rimek"]
|
8
|
+
s.email = 'rimek@poczta.fm'
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.homepage = 'https://github.com/rimek/serialwatcher'
|
11
|
+
s.executables << 'serial-watcher'
|
12
|
+
|
13
|
+
s.add_runtime_dependency 'gtk2'
|
14
|
+
s.add_runtime_dependency 'naturalsort'
|
15
|
+
s.add_runtime_dependency 'simple-rss'
|
16
|
+
s.add_runtime_dependency 'open4'
|
17
|
+
s.add_runtime_dependency 'mplayer-ruby'
|
18
|
+
s.add_runtime_dependency 'lockfile'
|
19
|
+
end
|
data/src/Gemfile
ADDED
data/src/episode.rb
ADDED
data/src/fs.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
require 'natural_sort'
|
4
|
+
include NaturalSort
|
5
|
+
|
6
|
+
require 'episode'
|
7
|
+
require 'serial'
|
8
|
+
|
9
|
+
class Fs
|
10
|
+
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_reader :series
|
14
|
+
attr_reader :episodes
|
15
|
+
|
16
|
+
def init(dir)
|
17
|
+
@main_directory = dir
|
18
|
+
#puts "fs:init #{dir}"
|
19
|
+
clear
|
20
|
+
end
|
21
|
+
|
22
|
+
def series
|
23
|
+
@series ||= load_series
|
24
|
+
end
|
25
|
+
|
26
|
+
def episodes serial
|
27
|
+
@episodes ||= load_episodes serial
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear
|
31
|
+
@series = @episodes = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def read_directory dir
|
36
|
+
return nil if not Dir.exists?(dir)
|
37
|
+
dirs = Dir.entries(dir)
|
38
|
+
dirs.delete(".")
|
39
|
+
dirs.delete("..")
|
40
|
+
dirs.natural_sort
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_series
|
44
|
+
series_directories = read_directory @main_directory
|
45
|
+
return if not series_directories
|
46
|
+
|
47
|
+
counter = 1
|
48
|
+
data = []
|
49
|
+
series_directories.each do |serial_directory_name|
|
50
|
+
path = File.expand_path "#{@main_directory}/#{serial_directory_name}"
|
51
|
+
next if not File.directory?(path)
|
52
|
+
|
53
|
+
name = serial_directory_name
|
54
|
+
|
55
|
+
serial = Serial.new( :name => name,
|
56
|
+
:path => path,
|
57
|
+
:counter => counter
|
58
|
+
)
|
59
|
+
data.push(serial)
|
60
|
+
counter += 1
|
61
|
+
end
|
62
|
+
data
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_episodes serial
|
66
|
+
episode_files = read_directory serial.path
|
67
|
+
return if not episode_files
|
68
|
+
|
69
|
+
data = []
|
70
|
+
counter = 0;
|
71
|
+
episode_files.each do |file|
|
72
|
+
|
73
|
+
next if File.directory?(file)
|
74
|
+
next if not ['.mp4', '.mkv', '.mpg', '.avi', '.flv'].include?(File.extname(file).downcase)
|
75
|
+
|
76
|
+
name = file
|
77
|
+
nr = counter
|
78
|
+
counter += 1
|
79
|
+
|
80
|
+
name = name[0..-5]
|
81
|
+
name.strip!
|
82
|
+
|
83
|
+
path = File.expand_path serial.path+'/'+file
|
84
|
+
next if File.directory?(path)
|
85
|
+
|
86
|
+
episode = Episode.new(:name => name,
|
87
|
+
:path => path,
|
88
|
+
:number => nr,
|
89
|
+
:serial => serial
|
90
|
+
)
|
91
|
+
data.push(episode)
|
92
|
+
end
|
93
|
+
|
94
|
+
data.sort! do |a,b|
|
95
|
+
a.number <=> b.number
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module Gui
|
3
|
+
class ListModel < Gtk::ListStore
|
4
|
+
|
5
|
+
attr_accessor :episodes, :serial, :counter
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
super String, Integer
|
9
|
+
@counter = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def load serial
|
13
|
+
clear
|
14
|
+
@counter = 0
|
15
|
+
@episodes = []
|
16
|
+
@serial = serial
|
17
|
+
|
18
|
+
fs = Fs.instance
|
19
|
+
fs.init serial
|
20
|
+
fs.episodes(serial).each do |episode|
|
21
|
+
item = append
|
22
|
+
item.set_value 0, episode.name
|
23
|
+
item.set_value 1, episode.number
|
24
|
+
|
25
|
+
@episodes << episode
|
26
|
+
@counter += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reload_episodes(serial)
|
31
|
+
load serial
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
module Gui
|
3
|
+
class ListView < Gtk::TreeView
|
4
|
+
COLUMNS = %w( Name )
|
5
|
+
|
6
|
+
def initialize(model = nil)
|
7
|
+
super model
|
8
|
+
|
9
|
+
cell_text = Gtk::CellRendererText.new
|
10
|
+
COLUMNS.each_with_index do |name, idx|
|
11
|
+
col = Gtk::TreeViewColumn.new name, cell_text, :text => idx
|
12
|
+
col.resizable = true
|
13
|
+
append_column col
|
14
|
+
end
|
15
|
+
|
16
|
+
@status = Status.instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def preselect_row serial
|
20
|
+
initial_episode_number = @status.get_last_episode_number serial
|
21
|
+
self.select_custom_row initial_episode_number
|
22
|
+
end
|
23
|
+
|
24
|
+
def preselect_row_via_episode episode
|
25
|
+
self.select_custom_row episode.number
|
26
|
+
end
|
27
|
+
|
28
|
+
def select_custom_row row_number
|
29
|
+
self.model.each do |model, path, iter|
|
30
|
+
if iter.get_value(1) == row_number.to_i
|
31
|
+
|
32
|
+
2.times do
|
33
|
+
path.prev! if path.to_s.to_i > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
self.grab_focus
|
37
|
+
self.set_cursor(path, nil, false)
|
38
|
+
self.scroll_to_cell(path, nil, false, 0, 0)
|
39
|
+
|
40
|
+
self.selection.select_iter(iter)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
data/src/gui/main.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
|
2
|
+
require 'gtk2'
|
3
|
+
|
4
|
+
require 'gui/list_model'
|
5
|
+
require 'gui/list_view'
|
6
|
+
require 'gui/title_box'
|
7
|
+
require 'gui/torrent_panel'
|
8
|
+
require 'gui/settings_dialog'
|
9
|
+
require 'gui/meta_dialog'
|
10
|
+
require 'plugins/torrent/torrentz'
|
11
|
+
require 'player'
|
12
|
+
require 'fs'
|
13
|
+
|
14
|
+
require 'pry'
|
15
|
+
|
16
|
+
module Gui
|
17
|
+
class Main < Gtk::Window
|
18
|
+
|
19
|
+
def initialize()
|
20
|
+
super
|
21
|
+
|
22
|
+
set_title "Serial Watcher"
|
23
|
+
set_window_position Gtk::Window::POS_CENTER
|
24
|
+
set_default_size 600, 600
|
25
|
+
set_border_width 10
|
26
|
+
|
27
|
+
@player = Player.instance
|
28
|
+
@status = Status.instance
|
29
|
+
|
30
|
+
create_elements
|
31
|
+
create_layout
|
32
|
+
connect_signals
|
33
|
+
|
34
|
+
@settings = Gtk::Settings.new
|
35
|
+
@settings.gtk_button_images = true
|
36
|
+
|
37
|
+
@title_box.load
|
38
|
+
|
39
|
+
#binding.pry
|
40
|
+
|
41
|
+
Gtk::Window.set_default_icon File.join($STATIC_PATH, 'images/app.png')
|
42
|
+
|
43
|
+
show
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_elements
|
47
|
+
@vbox = Gtk::VBox.new
|
48
|
+
@paned_inner = Gtk::VPaned.new
|
49
|
+
|
50
|
+
# paned window
|
51
|
+
@pw = Gtk::HPaned.new
|
52
|
+
|
53
|
+
# scroll view
|
54
|
+
@sw = Gtk::ScrolledWindow.new
|
55
|
+
@sw.set_policy Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC
|
56
|
+
|
57
|
+
# tree view
|
58
|
+
@model = ListModel.new
|
59
|
+
@view = ListView.new @model
|
60
|
+
|
61
|
+
# directory chooser
|
62
|
+
@path_label = Gtk::Label.new("", true)
|
63
|
+
@path_label.set_height_request 30
|
64
|
+
|
65
|
+
# change episode series
|
66
|
+
@title_box = TitleBox.new
|
67
|
+
@title_scroll_panel = Gtk::ScrolledWindow.new
|
68
|
+
@title_scroll_panel.add_with_viewport @title_box
|
69
|
+
@title_scroll_panel.set_hscrollbar_policy 'never'
|
70
|
+
viewport = @title_scroll_panel.child
|
71
|
+
viewport.set_shadow_type Gtk::SHADOW_NONE
|
72
|
+
|
73
|
+
#scroll.vadjustment.value = scroll.vadjustment.upper - scroll.vadjustment.page_sizep
|
74
|
+
|
75
|
+
# torrent panel
|
76
|
+
@torrent_panel = TorrentPanel.new
|
77
|
+
|
78
|
+
#toolbar
|
79
|
+
@toolbar = Gtk::Toolbar.new
|
80
|
+
@toolbar.set_toolbar_style Gtk::Toolbar::Style::BOTH # TEXT
|
81
|
+
|
82
|
+
# round play tool item
|
83
|
+
Gtk::Stock.add(:round, " Round play ")
|
84
|
+
icon = Gtk::Image.new "image/round.png"
|
85
|
+
@round_tool = Gtk::ToggleToolButton.new :round
|
86
|
+
@round_tool.active = true if @status.data[:round]
|
87
|
+
|
88
|
+
# round play tool item
|
89
|
+
Gtk::Stock.add(:fullscreen, " Fullscreen ")
|
90
|
+
icon = Gtk::Image.new "image/fullscreen.png"
|
91
|
+
@fullscreen_tool = Gtk::ToggleToolButton.new :fullscreen
|
92
|
+
@fullscreen_tool.active = true if @status.data[:fullscreen]
|
93
|
+
|
94
|
+
# meta edit tool item
|
95
|
+
Gtk::Stock.add(:meta_edit, " Meta edit ")
|
96
|
+
@meta_edit_tool = Gtk::ToolButton.new :meta_edit
|
97
|
+
@meta_edit_tool.sensitive = false
|
98
|
+
|
99
|
+
# settings tool item
|
100
|
+
Gtk::Stock.add(:settings, " Settings ")
|
101
|
+
@settings_tool = Gtk::ToolButton.new :settings
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_layout
|
105
|
+
separator = Gtk::SeparatorToolItem.new
|
106
|
+
space = Gtk::SeparatorToolItem.new
|
107
|
+
space.expand = true
|
108
|
+
space.draw= false
|
109
|
+
@toolbar.insert 0, @round_tool
|
110
|
+
@toolbar.insert 1, @fullscreen_tool
|
111
|
+
@toolbar.insert 2, space
|
112
|
+
@toolbar.insert 3, @meta_edit_tool
|
113
|
+
@toolbar.insert 4, separator
|
114
|
+
@toolbar.insert 5, @settings_tool
|
115
|
+
|
116
|
+
@sw.add @view
|
117
|
+
|
118
|
+
@paned_inner.set_size_request(200, -1)
|
119
|
+
@paned_inner.pack1(@sw, true, false)
|
120
|
+
@paned_inner.pack2(@torrent_panel, false, false)
|
121
|
+
|
122
|
+
@pw.add @title_scroll_panel
|
123
|
+
@pw.add @paned_inner
|
124
|
+
|
125
|
+
|
126
|
+
@vbox.pack_start @toolbar, false, true
|
127
|
+
@vbox.pack_start @path_label, false, true
|
128
|
+
@vbox.pack_end @pw
|
129
|
+
|
130
|
+
add @vbox
|
131
|
+
end
|
132
|
+
|
133
|
+
def connect_signals
|
134
|
+
|
135
|
+
self.signal_connect "destroy" do
|
136
|
+
Gtk.main_quit()
|
137
|
+
end
|
138
|
+
|
139
|
+
#toolbar buttons
|
140
|
+
@round_tool.signal_connect "toggled" do |button|
|
141
|
+
data = { :round => @round_tool.active? }
|
142
|
+
@status.save_config data
|
143
|
+
end
|
144
|
+
@fullscreen_tool.signal_connect "toggled" do |button|
|
145
|
+
data = { :fullscreen => @fullscreen_tool.active? }
|
146
|
+
@status.save_config data
|
147
|
+
end
|
148
|
+
|
149
|
+
@settings_tool.signal_connect "clicked" do |button|
|
150
|
+
settings = SettingsDialog.new #self
|
151
|
+
settings.signal_connect "response" do |sender, response|
|
152
|
+
if response == Gtk::Dialog::RESPONSE_OK
|
153
|
+
@status.save_config settings.values
|
154
|
+
if settings.path_changed?
|
155
|
+
@model.clear
|
156
|
+
@meta_edit_tool.sensitive = false
|
157
|
+
@title_box.load
|
158
|
+
end
|
159
|
+
end
|
160
|
+
settings.destroy
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
@meta_edit_tool.signal_connect "clicked" do |button|
|
165
|
+
return if not serial = @model.serial
|
166
|
+
|
167
|
+
meta = MetaDialog.new :serial => serial
|
168
|
+
meta.signal_connect "response" do |sender, response|
|
169
|
+
if response == Gtk::Dialog::RESPONSE_OK
|
170
|
+
serial.meta.save_file meta.values
|
171
|
+
change_serial serial
|
172
|
+
end
|
173
|
+
meta.destroy
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
# change single episode (play)
|
179
|
+
@view.signal_connect "row_activated" do |view, path, column|
|
180
|
+
@current_episode_path = path
|
181
|
+
self.play_episode @current_episode_path
|
182
|
+
end
|
183
|
+
|
184
|
+
@player.signal_connect "playing_ended" do |sender, param|
|
185
|
+
if @round_tool.active?
|
186
|
+
@current_episode_path.next!
|
187
|
+
self.play_episode @current_episode_path
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# change serial
|
192
|
+
@title_box.signal_connect "title_changed" do |titlebox, serial|
|
193
|
+
change_serial serial
|
194
|
+
end
|
195
|
+
|
196
|
+
# load serial list
|
197
|
+
@title_box.signal_connect "loaded" do |titlebox|
|
198
|
+
change_path_label
|
199
|
+
end
|
200
|
+
|
201
|
+
Gtk::LinkButton.set_uri_hook do end
|
202
|
+
end
|
203
|
+
|
204
|
+
def play_episode path
|
205
|
+
iter = @model.get_iter(path)
|
206
|
+
episode = @model.episodes[(@model.get_value(iter, 1).to_i)]# - 1]
|
207
|
+
@player.play_episode(episode)
|
208
|
+
@view.preselect_row_via_episode episode
|
209
|
+
end
|
210
|
+
|
211
|
+
def change_serial serial
|
212
|
+
@model.reload_episodes serial
|
213
|
+
@view.preselect_row serial
|
214
|
+
@torrent_panel.load serial
|
215
|
+
@meta_edit_tool.sensitive = true
|
216
|
+
end
|
217
|
+
|
218
|
+
def change_path_label
|
219
|
+
@path_label.text = "Current serial directory: #{@status.data_dir}"
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|