aniview 3.1.0 → 3.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 827d5b9084b305647f640fd4cf3ba14ce15bcea5
4
- data.tar.gz: 3e10b4ccf9d9f02cfee4260921eb7f22ae097339
3
+ metadata.gz: 0ea930584ab074643f3257cfb4ead82380d28a8e
4
+ data.tar.gz: dd51a52724248c507c8fcf171181ed73aa30acd0
5
5
  SHA512:
6
- metadata.gz: 69a1e2a4ba2c11f38811bb5eb951993f5ea41228796c77f397a9292c2f053993f653b8ecbe614df7c41a9fe3d3a88c481bc8379e73340d0ea580166974c57206
7
- data.tar.gz: 6195b01dcd02520fb7119c4b690f2266c0459ea603b96c6eaea03ab9e6aa352314800cac6b6f3052f846306886a409b7772fa0d6534ae2d29f7ad18ff73dbb97
6
+ metadata.gz: 337ae5115ba6619e0e526e8ef7d8dfc424671c36c8698d4e5b39dddb24fd6f873b715c8c383805e21d48f4cd33fc4d1ab9b7ba4cae0e16c4bf1cec8e95c813fe
7
+ data.tar.gz: 657aee82a1a6ac61017368fd4d5238813535a25dcfe36d0c7326be23883188e58b1811786bfa06ce5124d0ac885c082ea84efbbaaff70eb35fa4f34afc44222c
data/README.md CHANGED
@@ -7,6 +7,6 @@ command line anime library viewer
7
7
  ### using rubygems
8
8
  `gem install aniview`
9
9
 
10
- ### Find out more on the wiki
11
- + [Default Keybingings](https://github.com/annacrombie/aniview/wiki/Default-Keybindings)
12
- + [Format Strings](https://github.com/annacrombie/aniview/wiki/Format-Strings)
10
+ ## documentation
11
+ + [rubydoc.info](http://www.rubydoc.info/gems/aniview/)
12
+ + [wiki](https://github.com/annacrombie/aniview/wiki/)
@@ -14,7 +14,7 @@ module Aniview
14
14
  attr_accessor :accessible
15
15
  attr_accessor :path
16
16
 
17
- def initialize(path_, seen = false)
17
+ def initialize(path_, seen: false, watched_on: 0)
18
18
  @watched = seen
19
19
  @accessible = true
20
20
  begin
@@ -22,7 +22,7 @@ module Aniview
22
22
  rescue Errno::ENOENT
23
23
  @last_modified = "0"
24
24
  end
25
- @watched_on = 0
25
+ @watched_on = watched_on
26
26
  @path = path_
27
27
 
28
28
  @attr = {
@@ -37,6 +37,7 @@ module Aniview
37
37
  "l" => "",
38
38
  "f" => "",
39
39
  "b" => "",
40
+ "w" => seen? ? "x" : " "
40
41
  }
41
42
 
42
43
  if (@path == "empty")
@@ -55,15 +56,15 @@ module Aniview
55
56
  "l" => mov.colorspace,
56
57
  "f" => mov.frame_rate,
57
58
  "b" => String(mov.bitrate) + "kb/s",
59
+ "w" => seen? ? "x" : " "
58
60
  }
59
61
  end
60
-
61
62
  end
62
63
 
63
64
  def title
64
65
  return @attr["t"]
65
66
  end
66
-
67
+
67
68
  def seen?
68
69
  return @watched
69
70
  end
@@ -75,10 +76,12 @@ module Aniview
75
76
  def watch
76
77
  @watched = true
77
78
  @watched_on = DateTime.now.strftime('%Q')
79
+ @attr["w"] = "x"
78
80
  end
79
81
 
80
82
  def unwatch
81
83
  @watched = false
84
+ @attr["w"] = " "
82
85
  end
83
86
 
84
87
  def string
@@ -7,6 +7,7 @@ require 'benchmark'
7
7
  require_relative 'animefile'
8
8
  require_relative 'animeseries'
9
9
  require_relative '../../util/util'
10
+ require_relative '../../util/folder_listen'
10
11
  require_relative '../bridge'
11
12
 
12
13
  module Aniview
@@ -27,24 +28,34 @@ module Aniview
27
28
  @made_changes = true
28
29
 
29
30
  self.load
30
- pref_changed!
31
- relisten
31
+ pref_changed!(verbose: false)
32
+ #relisten
32
33
 
33
34
  @pref.add_observer(self, :pref_changed!)
34
35
  @mpvbridge.add_observer(self, :animefile_changed!)
35
36
  #@@local_anime.each { |key, obj| obj.add_observer(self, :animefile_changed!)}
36
37
  end
37
38
 
39
+ def listen_to_all_dirs
40
+ #listen to all
41
+ all_dirs = (@pref.get("anime_locations").split(":")).map { |dir| @pref.parseDir(dir) }
42
+ @folder_listener = FolderListen.to(all_dirs) { index_anime }
43
+ @folder_listener.start
44
+ end
45
+
38
46
  def relisten
39
- @listener.stop unless @listener == nil
40
- return if @anime_dirs == nil or @anime_dirs[0] == ""
41
- @listener = Listen.to(*@anime_dirs, only: /\.mkv$/) do |modified, added, removed|
42
- index_anime
43
- end
47
+ cleanup
48
+
49
+ listen_to_all_dirs
50
+
51
+ return if @anime_dirs == nil or @anime_dirs[0] == "" or @anime_dirs.length == 0
52
+
53
+ @listener = Listen.to(*@anime_dirs, only: /\.mkv$/) { index_anime }
44
54
  @listener.start
45
55
  end
46
56
 
47
57
  def cleanup
58
+ @folder_listener.stop unless @folder_listener == nil
48
59
  @listener.stop unless @listener == nil
49
60
  end
50
61
 
@@ -66,23 +77,24 @@ module Aniview
66
77
  end
67
78
  end
68
79
 
69
- def pref_changed!
80
+ def pref_changed!(verbose: true)
70
81
  new_dirs = (@pref.get("anime_locations").split(":")).collect { |dir|
71
- @pref.parseDir(dir)
72
- }
82
+ parsed_dir = @pref.parseDir(dir)
83
+ parsed_dir if File.exist?(parsed_dir)
84
+ }.compact
73
85
  if new_dirs != @anime_dirs
74
86
  @anime_dirs = new_dirs
75
- index_anime
87
+ index_anime(verbose: verbose)
76
88
  relisten
77
89
  end
78
90
  end
79
91
 
80
92
  def animefile_changed!
81
- return unless @mpvbridge.what_changed == "local_anime"
93
+ return unless @mpvbridge.what_changed[:local_anime]
94
+ save
82
95
  changed
83
96
  notify_observers
84
97
  end
85
-
86
98
 
87
99
  ##
88
100
  ## @brief Indexes anime files up to 1 level deep in specified anime directories
@@ -92,24 +104,36 @@ module Aniview
92
104
  ##
93
105
  ## @return { description_of_the_return_value }
94
106
  ##
95
- def index_anime(force: false, verbose: false)
107
+ def index_anime(force: false, verbose: true, very_verbose: false)
108
+ puts "Indexing anime" if very_verbose
96
109
  Thread.new {
97
110
  ret = {}
98
111
  made_changes = false
99
112
  lal = @@local_anime.length
100
113
 
101
- @anime_dirs.each { |dir|
114
+ @anime_dirs.each_with_index { |dir, i|
115
+
116
+ avs_file = dir + "/.avs"
117
+
118
+ if File.exist?(avs_file)
119
+ puts "found avs_data" if very_verbose
120
+ avs_data = JSON.parse(File.open(avs_file, "r") { |f| f.read })
121
+ else
122
+ puts "no avs_data" if very_verbose
123
+ avs_data = {}
124
+ end
125
+
102
126
 
103
127
  # check if the directory exists
104
- if not File.exist? dir
105
- if @dir_last_modified[dir] != "check"
128
+ unless File.exist? dir
129
+ unless @dir_last_modified[dir] == "check"
106
130
  @dir_last_modified[dir] = "check"
107
131
  made_changes = true
108
132
  end
109
133
  next
110
134
  end
111
135
 
112
- puts "checking #{dir}" if verbose
136
+ puts "checking #{dir}" if very_verbose
113
137
 
114
138
  # skip indexing a directory if it hasn't been modified since the last
115
139
  # check (disabled because doesn't save to much time)
@@ -129,15 +153,37 @@ module Aniview
129
153
  # get all files in the directory and in all subfolders of the directory
130
154
  files = Dir.glob("#{dir}*/*.{mkv,avi,mp4}") + Dir.glob("#{dir}/*.{mkv,avi,mp4}")
131
155
 
132
- files.each { |file|
133
- print "indexing #{file} -> " if verbose
134
- seen_this = false
156
+ files.each_with_index { |file, j|
157
+ if verbose
158
+ pct = Util.format_progress ((j + 1) / Float(files.length)) * ( (i + 1) / Float(@anime_dirs.length)) * 100
159
+ Util.error_message("#{pct}% #{File.basename(file)}")
160
+ end
161
+
162
+ print "indexing #{file} -> " if very_verbose
163
+ params = {:seen => false, :watched_on => 0}
164
+
165
+ #check if this anime has a stored watched value
166
+ if avs_data.key?(file)
167
+ puts "found stored data for this file" if very_verbose
168
+ params = {
169
+ :seen => avs_data[file]["seen"],
170
+ :watched_on => avs_data[file]["watched_on"]
171
+ }
172
+ puts params if very_verbose
173
+ else
174
+ puts "no data on this file" if very_verbose
175
+ avs_data.merge!({file => params})
176
+ end
135
177
 
136
178
  # we have already indexed this file
137
179
  if @@local_anime != nil and @@local_anime.key?(file)
138
180
 
181
+ puts "this file is in the index" if very_verbose
182
+
139
183
  # get its seen data
140
- seen_this = @@local_anime[file].seen?
184
+ params[:seen] = @@local_anime[file].seen?
185
+ params[:watched_on] = @@local_anime[file].seenOn
186
+ puts params if very_verbose
141
187
  # get its last modifed data
142
188
  last_modified = File::Stat.new(file).mtime
143
189
  # get wether or not it is accessible
@@ -146,7 +192,7 @@ module Aniview
146
192
  # if we arent forcing or the anime hasn't been modified since the
147
193
  # last check, skip it
148
194
  if not force or last_modified == @@local_anime[file].last_modified
149
- puts "unchanged" if verbose
195
+ puts "unchanged" if very_verbose
150
196
  ret.merge!(file => @@local_anime[file])
151
197
  next
152
198
  end
@@ -159,12 +205,15 @@ module Aniview
159
205
 
160
206
  # this is the anime file we are adding
161
207
  ret.merge!(
162
- file => AnimeFile.new(
163
- file,
164
- seen_this
165
- )
208
+ file => AnimeFile.new(file, params)
166
209
  )
167
210
  }
211
+
212
+ mavsf = @pref.get("avs_files")["create?"]
213
+ oimfs = @pref.get("avs_files")["only_on_mounted_fs?"]
214
+ if mavsf and ((oimfs and Util.mounted_filesystem?(dir)) or not oimfs)
215
+ File.open(avs_file, "w") { |f| f.puts JSON.generate(avs_data)}
216
+ end
168
217
  }
169
218
 
170
219
  #if @@local_anime != nil
@@ -184,6 +233,7 @@ module Aniview
184
233
  changed
185
234
  notify_observers
186
235
  end
236
+ #
187
237
  }
188
238
  end
189
239
 
@@ -201,7 +251,7 @@ module Aniview
201
251
  hash = {}
202
252
 
203
253
  arr.sort_by {|i| i.title}.each { |file|
204
- if ! keys.key?(parent file.path)
254
+ unless keys.key?(parent file.path)
205
255
  key = AnimeSeries.new parent(file.path)
206
256
  keys.merge! parent(file.path) => key
207
257
  hash.merge! key => []
@@ -210,7 +260,7 @@ module Aniview
210
260
  end
211
261
 
212
262
  hash[key] << file
213
- key += file
263
+ key << file
214
264
  }
215
265
  hash.sort_by{ |j| j[0].title }.to_h
216
266
  end
@@ -230,10 +280,10 @@ module Aniview
230
280
  f.puts %x(date).chomp + " ./" + File.basename(file) + " " + @watch_log_tag
231
281
  end
232
282
  end
233
-
283
+
234
284
  def addWatched(file)
235
285
  @@local_anime[file].watch
236
- self.save
286
+ save
237
287
  changed
238
288
  notify_observers
239
289
  end
@@ -241,15 +291,14 @@ module Aniview
241
291
  def rmWatched(path = "")
242
292
  if @@local_anime.key? path
243
293
  @@local_anime[path].unwatch
244
- return
294
+ else
295
+ r = []
296
+ @@local_anime.values.each_with_index{ |v, i| r << {Integer(v.seenOn) => @@local_anime.keys[i]} if v.seen? }
297
+ return if r.length == 0
298
+ last_watched = (r.sort_by{ |h| h.keys.first}.reverse)[0].values[0]
299
+ @@local_anime[last_watched].unwatch
245
300
  end
246
-
247
- r = []
248
- @@local_anime.values.each_with_index{ |v, i| r << {Integer(v.seenOn) => @@local_anime.keys[i]} if v.seen? }
249
- return if r.length == 0
250
- last_watched = (r.sort_by{ |h| h.keys.first}.reverse)[0].values[0]
251
- @@local_anime[last_watched].unwatch
252
- self.save
301
+ save
253
302
  changed
254
303
  notify_observers
255
304
  end
@@ -258,6 +307,15 @@ module Aniview
258
307
  @mpvbridge.play file
259
308
  end
260
309
 
310
+ def watch_list(files)
311
+ File.open("/tmp/aniview_list.m3u", "w") { |f|
312
+ files.each { |file|
313
+ f.puts file.path
314
+ }
315
+ }
316
+ @mpvbridge.playlist("/tmp/aniview_list.m3u", files.map { |f| [f.path, f] }.to_h)
317
+ end
318
+
261
319
  end
262
320
  end
263
321
  end
@@ -16,6 +16,9 @@ module Aniview
16
16
  "c" => 0,
17
17
  "d" => 0,
18
18
  "s" => 0,
19
+ "p" => 0,
20
+ "w" => 0,
21
+ "W" => 0
19
22
  }
20
23
 
21
24
  @children = children
@@ -24,10 +27,25 @@ module Aniview
24
27
 
25
28
  def + af
26
29
  @attr["d"] += af.attr["d"]
27
- @attr["D"] = Util.format_duration @attr["d"]
30
+ @attr["D"] = Util.format_duration(@attr["d"])
28
31
  @attr["s"] += af.attr["s"]
29
- @attr["S"] = Util.format_size @attr["s"]
32
+ @attr["S"] = Util.format_size(@attr["s"])
30
33
  @attr["c"] += 1
34
+ @attr["w"] += 1 if af.seen?
35
+ @attr["W"] += af.attr["d"] if af.seen?
36
+ end
37
+
38
+ def << af
39
+ @children << af
40
+ self + af
41
+ average
42
+ end
43
+
44
+ def average
45
+ @attr["p"] = @attr["w"] * 100.0 / @attr["c"]
46
+ @attr["q"] = @attr["d"] > 0 ? @attr["W"] * 100.0 / @attr["d"] : 0
47
+ @attr["Q"] = Util.format_progress @attr["q"]
48
+ @attr["P"] = Util.format_progress @attr["p"]
31
49
  end
32
50
 
33
51
  def title
@@ -11,12 +11,13 @@ module Aniview
11
11
  include Observable
12
12
 
13
13
  attr_accessor :playing
14
+ attr_accessor :playing_file
14
15
  attr_accessor :what_changed
15
16
 
16
17
  def initialize pref
17
18
  @pref = pref
18
19
  @mpv_enabled = true
19
- @logger = Logger.new('mpv.log')
20
+ @logger = Logger.new(@pref.parseDir('$conf_dir/mpv.log'))
20
21
  @logger.level = Logger::DEBUG
21
22
  connect
22
23
  @what_changed = ""
@@ -24,17 +25,25 @@ module Aniview
24
25
  end
25
26
 
26
27
  def event_handler event
27
- case event
28
+ @logger.debug event
29
+ case event["event"]
30
+ #file-loaded
28
31
  when "start-file"
29
32
  @playing = true
30
- @what_changed = "playing_status"
33
+ @what_changed = {:playing_status => :start}
31
34
  changed
32
35
  notify_observers
33
36
  when "end-file"
34
37
  @playing = false
35
- @what_changed = "playing_status"
38
+ @what_changed = {:playing_status => :end}
36
39
  changed
37
40
  notify_observers
41
+ when "property-change"
42
+ case event["name"]
43
+ when "path"
44
+ @playing_file = @playing_files[event["data"]]
45
+ end
46
+ else
38
47
  end
39
48
  end
40
49
 
@@ -50,6 +59,8 @@ module Aniview
50
59
  begin
51
60
  @mpv = MPV::Session.new(user_args: @pref.get("mpv_args").split(" "))
52
61
  @mpv.callbacks << MPV::Callback.new(self, :event_handler)
62
+ @mpv.client.command("observe_property_string", 1, "path")
63
+ #@mpv.client.command("observe_property_string", 1, "playback-abort")
53
64
  rescue MPV::MPVNotAvailableError
54
65
  rescue MPV::MPVUnsupportedFlagError
55
66
  else
@@ -60,11 +71,20 @@ module Aniview
60
71
  def play file
61
72
  return unless @mpv_enabled
62
73
  connect
63
- @playing_file = file
74
+ @playing_files = {file.path => file}
64
75
  @mpv.client.command "loadfile", file.path
65
76
  @playing = true
66
77
  end
67
-
78
+
79
+ def playlist playlist, files
80
+ return unless @mpv_enabled
81
+ connect
82
+ @playing_files = files
83
+ #@playing_file = @playing_files[0]
84
+ @mpv.client.command "loadlist", playlist
85
+ @playing = true
86
+ end
87
+
68
88
  def playing?
69
89
  @playing
70
90
  end
@@ -75,7 +95,7 @@ module Aniview
75
95
  pct = Float(percentage)
76
96
  if pct >= swp and not @playing_file.seen?
77
97
  @playing_file.watch
78
- @what_changed = "local_anime"
98
+ @what_changed = {:local_anime => :watched}
79
99
  changed
80
100
  notify_observers
81
101
  end
@@ -41,14 +41,14 @@
41
41
  },
42
42
  "format_library" :
43
43
  {
44
- "title" : " %t @ %D %S ",
45
- "parent" : " %t - %c@ %D %S ",
46
- "child" : " %t@ %D %S "
44
+ "title" : " %t@ ##%w/##%c #####%Q ########%D #######%S ",
45
+ "parent" : " %t@ ##%w/##%c #####%Q ########%D #######%S ",
46
+ "child" : " %w %t@ ########%D #######%S "
47
47
  },
48
48
  "format_library_unwatched" :
49
49
  {
50
- "title" : " %t@ %D %S ",
51
- "parent" : " %t - %c@ %D %S ",
50
+ "title" : " %t@ ##%w/##%c ########%D #######%S ",
51
+ "parent" : " %t@ ##%w/##%c ########%D #######%S ",
52
52
  "child" : " %t@ %D %S %r "
53
53
  },
54
54
  "format_torrents" :
@@ -89,6 +89,11 @@
89
89
  {
90
90
  "port" : "21312"
91
91
  },
92
+ "avs_files":
93
+ {
94
+ "create?": true,
95
+ "only_on_mounted_fs?": false
96
+ },
92
97
  "set_watched_percentage" : "80",
93
98
  "log_file" : "$conf_dir/aw.log",
94
99
  "daemon_log_file" : "$conf_dir/server.log",
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'json'
3
+ require 'observer'
3
4
 
4
5
  require_relative 'prefitem'
5
6
  require_relative '../../view/color'
@@ -76,6 +77,16 @@ module Aniview
76
77
  ##
77
78
  def set(trail, destination)
78
79
  trail = [trail] if trail.class == String
80
+
81
+ destination = case destination
82
+ when "true"
83
+ true
84
+ when "false"
85
+ false
86
+ else
87
+ destination
88
+ end
89
+
79
90
  return unless valid_preference?(trail, destination)
80
91
 
81
92
  trail[0...-1].inject(@pref, :fetch)[trail.last] = destination
@@ -83,6 +94,14 @@ module Aniview
83
94
  changed
84
95
  notify_observers
85
96
  end
97
+
98
+ def pref_type(trail)
99
+ trail.inject(@validations, :fetch)
100
+ end
101
+
102
+ def get_pref(trail)
103
+ trail.inject(@pref, :fetch)
104
+ end
86
105
 
87
106
  ##
88
107
  ## @brief validates a preference
@@ -96,7 +115,7 @@ module Aniview
96
115
  def valid_preference?(trail, destination)
97
116
  skeys = [ "space", "up", "down", "left", "right", "enter" ]
98
117
 
99
- case trail.inject(@validations, :fetch)
118
+ case pref_type(trail)
100
119
  when "key"
101
120
  destination.length == 1 or skeys.include?(destination)
102
121
  when "path"
@@ -105,6 +124,8 @@ module Aniview
105
124
  Aniview::View::Color.respond_to?(destination)
106
125
  when "int"
107
126
  destination.scan(/\D/).empty? and destination.length > 1
127
+ when "boolean"
128
+ destination.class == TrueClass or destination.class == FalseClass
108
129
  when "locked"
109
130
  false
110
131
  else
@@ -90,6 +90,10 @@
90
90
  {
91
91
  "port" : "int"
92
92
  },
93
+ "avs_files": {
94
+ "create?": "boolean",
95
+ "only_on_mounted_fs?": "boolean"
96
+ },
93
97
  "set_watched_percentage" : "int",
94
98
  "log_file" : "path",
95
99
  "daemon_log_file" : "path",
@@ -50,10 +50,6 @@ module Aniview
50
50
 
51
51
  rescue SocketError
52
52
  @rss = nil
53
-
54
- #rescue
55
- # @rss = nil
56
-
57
53
  end
58
54
  end
59
55
 
@@ -0,0 +1,53 @@
1
+ # watch for changes in a folder's existance
2
+
3
+ module Aniview
4
+ module Util
5
+ module FolderListen
6
+
7
+ def self.to(dirs, &block)
8
+ Listener.new(dirs, &block)
9
+ end
10
+
11
+ ##
12
+ ## @brief Similar to Listen gem but uses polling and is only intended
13
+ ## to be used on a small number of files. the Listen gem
14
+ ## wouldn't support watching an individual FOLDER to see if
15
+ ## the folder went away so this is that!
16
+ ##
17
+ class Listener
18
+ def initialize(dirs, &block)
19
+ @dirs = dirs
20
+ @block = block
21
+ @sleep_duration = 1
22
+ @dir_hash = dir_hash
23
+ end
24
+
25
+ def dir_hash
26
+ @dirs.map { |dir| [dir, File.exist?(dir)]}.to_h
27
+ end
28
+
29
+ def changed?
30
+ @dir_hash != dir_hash
31
+ end
32
+
33
+ def stop
34
+ @listen_thread.exit if @listen_thread
35
+ end
36
+
37
+ def start
38
+ Thread.abort_on_exception = true
39
+ @listen_thread = Thread.new do
40
+ while true
41
+ if changed?
42
+ @block.call
43
+ @dir_hash = dir_hash
44
+ end
45
+ sleep 1
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,6 +1,7 @@
1
1
  module Aniview
2
2
  module Util
3
3
  class Term
4
+
4
5
  def initialize
5
6
  @tput=Hash[
6
7
  "smcup" => %x(tput smcup),
@@ -12,7 +13,7 @@ module Aniview
12
13
  "nobold" => %x(tput sgr0),
13
14
  ]
14
15
  end
15
-
16
+
16
17
  def save; print @tput["smcup"]; return self; end
17
18
  def restore; print @tput["rmcup"]; return self; end
18
19
  def hide_cursor; print @tput["civis"]; return self; end
@@ -23,21 +24,21 @@ module Aniview
23
24
  def clear; print "\033[2J"; return self; end
24
25
  def bold; print @tput["bold"]; return self; end
25
26
  def nobold; print @tput["nobold"]; return self; end
26
-
27
+
27
28
  def cols; return HighLine::SystemExtensions.terminal_size[0]; end
28
29
  def rows; return HighLine::SystemExtensions.terminal_size[1]; end
29
30
 
30
31
  def self.cols; return HighLine::SystemExtensions.terminal_size[0]; end
31
32
  def self.rows; return HighLine::SystemExtensions.terminal_size[1]; end
32
-
33
+
33
34
  def getKey
34
35
  return STDIN.getch.gsub("\r", "enter").gsub(" ", "space").gsub("A", "up").gsub("B", "down").gsub("C", "right").gsub("D", "left").gsub("\e", "skip").gsub("[", "skip")
35
36
  end
36
-
37
+
37
38
  def reset
38
39
  self.restore.show_cursor.echo_on
39
40
  end
40
-
41
+
41
42
  end
42
43
  end
43
44
  end
@@ -9,7 +9,7 @@ module Aniview
9
9
  term.show_cursor
10
10
  Readline.completion_append_character = ""
11
11
  Readline.pre_input_hook = -> do
12
- Readline.insert_text default
12
+ Readline.insert_text "#{default}"
13
13
  Readline.redisplay
14
14
  Readline.pre_input_hook = nil
15
15
  end
@@ -18,9 +18,9 @@ module Aniview
18
18
  return input
19
19
  end
20
20
 
21
- def self.error_message
21
+ def self.error_message(message)
22
22
  #use statusline to show error messages
23
- print "\e[#{Term.rows};2H Message"
23
+ print "\e[#{Term.rows - 1};1H\e[31m #{message}\e[K"
24
24
  end
25
25
 
26
26
  def self.format_duration v
@@ -101,12 +101,12 @@ module Aniview
101
101
  mode = "char"
102
102
 
103
103
  elsif mode == "%"
104
- addstr = ""
105
- addstr = String(d[c]) if d.key?(c)
106
- ol = addstr.length
107
- addstr = " " + addstr while addstr.length < (ol + padding_collected)
104
+ addstr = d.key?(c) ? String(d[c]) : ""
105
+ padding = padding_collected > addstr.length ? " " * (padding_collected - addstr.length) : ""
106
+ addstr = padding + addstr
108
107
  add_to += addstr
109
108
  mode = "char"
109
+ padding_collected = 0
110
110
 
111
111
  else
112
112
  add_to += c
@@ -150,5 +150,18 @@ module Aniview
150
150
  {}
151
151
  end
152
152
  end
153
+
154
+ def self.mounted_filesystem? dir
155
+ %x(mount).split("\n").map { |l|
156
+ mounted = /on (.*?) /.match(l)[1]
157
+ if mounted == "/"
158
+ false
159
+ else
160
+ dir.split("/").zip(mounted.split("/")).reduce(true) { |m,v|
161
+ m and (v[0] == v[1] or v[1] == nil)
162
+ }
163
+ end
164
+ }.reduce { |a,b| a or b}
165
+ end
153
166
  end
154
167
  end
@@ -7,56 +7,62 @@ module Aniview
7
7
  include Aniview::Util
8
8
 
9
9
  def refresh_attributes
10
+ if @items.length > 0
11
+ unformatted_P = sum_attribute("p") / @items.length
12
+ else
13
+ unformatted_P = 0
14
+ end
15
+
16
+ if sum_attribute("d") > 0
17
+ unformatted_Q = sum_attribute("W") * 100.0 / sum_attribute("d")
18
+ else
19
+ unformatted_Q = 0
20
+ end
10
21
  @attributes = {
11
22
  "t" => @attributes["t"],
12
- "D" => Util.format_duration(getDuration),
13
- "S" => Util.format_size(getSize)
23
+ "D" => Util.format_duration(sum_attribute("d")),
24
+ "S" => Util.format_size(sum_attribute("s")),
25
+ "P" => Util.format_progress(unformatted_P),
26
+ "Q" => Util.format_progress(unformatted_Q),
27
+ "c" => sum_attribute("c"),
28
+ "w" => sum_attribute("w")
14
29
  }
15
30
  end
16
-
17
- def getDuration
18
- dur = 0
19
- @items.each{ |p, c| dur += p.attr["d"] }
20
- return dur
21
- end
22
-
23
- def getSize
24
- size = 0
25
- @items.each{ |child|
26
- size += child[0].attributes["s"]
27
- }
28
- return size
29
- end
30
-
31
- def customControl(key, sel)
31
+
32
+ def sum_attribute k
33
+ @items.inject(0) { |mem, c| mem += c[0].attr[k] }
34
+ end
35
+
36
+ def customControl(key, sel)
32
37
  return if @items == {}
33
- path = @items.values[sel["out"]][sel["in"]].path
38
+ path = @items.values[sel["out"]][sel["in"]].path
34
39
 
35
40
  empty = path == "empty"
36
41
 
37
- if key == @pref.get("keybindings")["menu_nav_expand"]
38
- expand(sel["out"])
39
- elsif key == "r"
40
- refresh
41
- elsif key == "enter" and not empty
42
-
43
- @interface.watch(@items.values[sel["out"]][sel["in"]])
44
-
45
- @interface.logWatched(path)
46
-
47
- refresh
48
- elsif key == @pref.get("keybindings")["anime_set_watched"] and not empty
49
- @interface.addWatched(path)
50
- elsif key == @pref.get("keybindings")["anime_undo_set_watched"]
51
- @interface.rmWatched()
52
- elsif key == @pref.get("keybindings")["anime_set_unwatched"] and not empty
53
- @interface.rmWatched(path)
54
- moveCursor("down")
55
- end
42
+ if key == @pref.get("keybindings")["menu_nav_expand"]
43
+ expand(sel["out"])
44
+ elsif key == "r"
45
+ refresh
46
+ elsif key == "enter" and not empty
47
+
48
+ if sel["in_expanded"]
49
+ @interface.watch(@items.values[sel["out"]][sel["in"]])
50
+ @interface.logWatched(path)
51
+ else
52
+ @interface.watch_list(@items.values[sel["out"]])
53
+ end
54
+ elsif key == @pref.get("keybindings")["anime_set_watched"] and not empty
55
+ @interface.addWatched(path)
56
+ elsif key == @pref.get("keybindings")["anime_undo_set_watched"]
57
+ @interface.rmWatched
58
+ elsif key == @pref.get("keybindings")["anime_set_unwatched"] and not empty
59
+ @interface.rmWatched(path)
60
+ moveCursor("down")
61
+ end
56
62
 
57
63
  changed
58
64
  notify_observers
59
- end
65
+ end
60
66
  end
61
67
  end
62
68
  end
@@ -9,18 +9,17 @@ module Aniview
9
9
  include Aniview::Util
10
10
  include Observable
11
11
 
12
- attr_accessor :visible
12
+ attr_accessor :attributes
13
+ attr_accessor :format
13
14
 
14
15
  def initialize(refresh_function: :items, interface:, name:, pref:, format:, term:, children: true, visible: false, logger: nil)
15
16
  @attributes = {}
16
17
 
17
18
  @logger = logger
18
19
 
19
- @visible = visible
20
-
21
20
  @refresh_function = refresh_function
22
21
  @interface = interface
23
- setName name
22
+ @attributes["t"] = name
24
23
  @pref = pref
25
24
  @format = format
26
25
  @term = term
@@ -65,7 +64,7 @@ module Aniview
65
64
  refresh
66
65
  changed
67
66
  notify_observers
68
- sleep 0.07
67
+ sleep 1
69
68
  end
70
69
  end
71
70
  when :stop
@@ -73,7 +72,7 @@ module Aniview
73
72
  end
74
73
  end
75
74
 
76
- def setRfunc(new_method)
75
+ def rfunc=(new_method)
77
76
  @refresh_function = new_method
78
77
  @items_formatted = {}
79
78
  @items_hash = {}
@@ -100,15 +99,7 @@ module Aniview
100
99
  adjustView
101
100
  draw if redraw
102
101
  end
103
-
104
- def setName(name)
105
- @attributes["t"] = name
106
- end
107
-
108
- def setFormat(format_)
109
- @format = format_
110
- end
111
-
102
+
112
103
  def refresh
113
104
  @items = @interface.send(@refresh_function)
114
105
  refreshFormats
@@ -11,8 +11,13 @@ module Aniview
11
11
  title = @items[sel["out"]].attributes["t"]
12
12
  value = @items[sel["out"]].attributes["v"]
13
13
  if key == "enter"
14
- newval = Util.readline(@term, title + ":", value)
15
- @interface.set(path, newval)
14
+ case @interface.pref_type(path)
15
+ when "boolean"
16
+ @interface.set(path, (not @interface.get_pref(path)))
17
+ else
18
+ newval = Util.readline(@term, title + ":", value)
19
+ @interface.set(path, newval)
20
+ end
16
21
  refresh
17
22
  end
18
23
  end
@@ -75,11 +75,10 @@ module Aniview
75
75
 
76
76
  @aiomenu = View::AioMenu.new(
77
77
  interface: @aio,
78
- name: @pref.get("menu_titles")["unwatched"],
78
+ name: @pref.get("menu_titles")["library"],
79
79
  pref: @pref,
80
- format: "format_library_unwatched",
80
+ format: "format_library",
81
81
  term: @term,
82
- refresh_function: :unwatched,
83
82
  logger: @logger
84
83
  )
85
84
  @prefmenu = View::PrefMenu.new(
@@ -172,24 +171,28 @@ module Aniview
172
171
  #
173
172
  # @return nil
174
173
  #
175
- def changeView newView
174
+ def change_view(view:, rfunc: :items, format:nil, name:nil)
176
175
  @view.delete_observers
177
176
  @view.pause
178
- newView.unpause
179
- newView.add_observer(self, :drawview)
180
- newView.change_screen_size redraw: false
181
- newView.expand -1
182
- @view = newView
177
+ view.attributes["t"] = name if name
178
+ view.format = format if format
179
+ view.rfunc = rfunc
180
+ view.add_observer(self, :drawview)
181
+ view.change_screen_size redraw: false
182
+ view.expand -1
183
+ view.unpause
184
+ @view = view
183
185
  end
184
186
 
185
187
  def drawview
186
188
  @view.draw
189
+ @statusline.draw
187
190
  @rcount ||= 0
188
191
  #puts "refreshed view #{@rcount+=1}"
189
192
  end
190
193
 
191
194
  def togglestatusline
192
- return unless @mpvbridge.what_changed == "playing_status"
195
+ return unless @mpvbridge.what_changed[:playing_status]
193
196
  unless @statusthread
194
197
  @statusthread = Thread.new do
195
198
  while true
@@ -218,13 +221,23 @@ module Aniview
218
221
  # @return nil
219
222
  #
220
223
  def run
224
+ nm = @pref.get("menu_titles")
225
+ views = {
226
+ :unwatched => {view: @aiomenu, name: nm["unwatched"], format: "format_library_unwatched", rfunc: :unwatched},
227
+ :library => {view: @aiomenu, name: nm["library"], format: "format_library"},
228
+ :torrents => {view: @delugemenu},
229
+ :preferences => {view: @prefmenu},
230
+ :schedule => {view: @schedulemenu},
231
+ :subscription => {view: @subscriptionmenu}
232
+ }
221
233
 
222
- @view = @aiomenu
234
+ current_view = 0
235
+ @view = @aiomenu
236
+
223
237
  @view.add_observer(self, :drawview)
224
- @statusline.draw
225
-
226
238
  @mpvbridge.add_observer(self, :togglestatusline)
227
239
  @statusline.add_observer(self, :drawmessage)
240
+ @statusline.draw
228
241
 
229
242
  mutex = Mutex.new
230
243
  Signal.trap('SIGWINCH', proc {
@@ -243,31 +256,34 @@ module Aniview
243
256
  @shoulddraw = false
244
257
 
245
258
  kb = @pref.get("keybindings")
246
- nm = @pref.get("menu_titles")
247
259
 
248
260
  case key
249
261
  when kb["goto_unwatched"]
250
- changeView @aiomenu
251
- @view.setName nm["unwatched"]
252
- @view.setFormat("format_library_unwatched")
253
- @view.setRfunc(:unwatched)
262
+ change_view(views[:unwatched])
263
+ current_view = 0
254
264
  when kb["goto_library"]
255
- changeView @aiomenu
256
- @view.setName nm["library"]
257
- @view.setFormat("format_library")
258
- @view.setRfunc(:items)
265
+ change_view(views[:library])
266
+ current_view = 1
259
267
  when kb["goto_torrents"]
260
- changeView @delugemenu
261
- @view.setName nm["torrents"]
268
+ change_view(views[:torrents])
269
+ current_view = 2
262
270
  when kb["goto_preferences"]
263
- changeView @prefmenu
264
- @view.setName nm["preferences"]
271
+ change_view(views[:preferences ])
272
+ current_view = 5
265
273
  when kb["goto_schedule"]
266
- changeView @schedulemenu
267
- @view.setName nm["schedule"]
274
+ change_view(views[:schedule])
275
+ current_view = 3
268
276
  when kb["goto_subscriptions"]
269
- changeView @subscriptionmenu
270
- @view.setName nm["subscriptions"]
277
+ change_view(views[:subscription])
278
+ current_view = 4
279
+ when "right"
280
+ current_view += 1
281
+ current_view = views.length - 1 if current_view >= views.length
282
+ change_view(views.values[current_view])
283
+ when "left"
284
+ current_view -= 1
285
+ current_view = 0 if current_view < 0
286
+ change_view(views.values[current_view])
271
287
  when ":"
272
288
  @view.pause
273
289
  if @statusthread
@@ -282,6 +298,7 @@ module Aniview
282
298
  end
283
299
  @view.control(key)
284
300
  end
301
+
285
302
  end
286
303
  end
287
304
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aniview
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - annacrombie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-25 00:00:00.000000000 Z
11
+ date: 2017-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: streamio-ffmpeg
@@ -156,6 +156,7 @@ files:
156
156
  - "./lib/aniview/interface/schedule/scheduleitem.rb"
157
157
  - "./lib/aniview/interface/subscription/subscription.rb"
158
158
  - "./lib/aniview/util/error.rb"
159
+ - "./lib/aniview/util/folder_listen.rb"
159
160
  - "./lib/aniview/util/term.rb"
160
161
  - "./lib/aniview/util/util.rb"
161
162
  - "./lib/aniview/view/aiomenu.rb"