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.
@@ -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
+