serial-watcher 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ status
2
+ data
3
+ *.gem
4
+ Gemfile.lock
@@ -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
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'gtk2'
4
+ gem 'naturalsort'
5
+ gem 'simple-rss'
6
+ gem 'mplayer-ruby'
7
+ gem 'lockfile'
8
+ #gem 'pry'
@@ -0,0 +1,12 @@
1
+
2
+ class Episode
3
+
4
+ attr_accessor :name, :path, :number, :serial
5
+
6
+ def initialize data
7
+ @name = data[:name]
8
+ @path = data[:path]
9
+ @number = data[:number]
10
+ @serial = data[:serial]
11
+ end
12
+ end
@@ -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
+
@@ -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
+