aniview 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/aniview/interface/animeio/animefile.rb +7 -4
- data/lib/aniview/interface/animeio/animeio.rb +97 -39
- data/lib/aniview/interface/animeio/animeseries.rb +20 -2
- data/lib/aniview/interface/mpv/mpvbridge.rb +27 -7
- data/lib/aniview/interface/pref/defaults.json +10 -5
- data/lib/aniview/interface/pref/pref.rb +22 -1
- data/lib/aniview/interface/pref/validate.json +4 -0
- data/lib/aniview/interface/subscription/subscription.rb +0 -4
- data/lib/aniview/util/folder_listen.rb +53 -0
- data/lib/aniview/util/term.rb +6 -5
- data/lib/aniview/util/util.rb +20 -7
- data/lib/aniview/view/aiomenu.rb +45 -39
- data/lib/aniview/view/menu.rb +6 -15
- data/lib/aniview/view/prefmenu.rb +7 -2
- data/lib/application.rb +47 -30
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ea930584ab074643f3257cfb4ead82380d28a8e
|
4
|
+
data.tar.gz: dd51a52724248c507c8fcf171181ed73aa30acd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
11
|
-
+ [
|
12
|
-
+ [
|
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
|
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 =
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
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.
|
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
|
-
|
105
|
-
|
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
|
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.
|
133
|
-
|
134
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
30
|
+
@attr["D"] = Util.format_duration(@attr["d"])
|
28
31
|
@attr["s"] += af.attr["s"]
|
29
|
-
@attr["S"] = Util.format_size
|
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
|
-
|
28
|
+
@logger.debug event
|
29
|
+
case event["event"]
|
30
|
+
#file-loaded
|
28
31
|
when "start-file"
|
29
32
|
@playing = true
|
30
|
-
@what_changed =
|
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 =
|
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
|
-
@
|
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 =
|
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
|
45
|
-
"parent" : " %t
|
46
|
-
"child" : " %t@
|
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@
|
51
|
-
"parent" : " %t
|
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
|
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
|
@@ -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
|
data/lib/aniview/util/term.rb
CHANGED
@@ -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
|
data/lib/aniview/util/util.rb
CHANGED
@@ -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};
|
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
|
-
|
106
|
-
|
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
|
data/lib/aniview/view/aiomenu.rb
CHANGED
@@ -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(
|
13
|
-
"S" => Util.format_size(
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
38
|
+
path = @items.values[sel["out"]][sel["in"]].path
|
34
39
|
|
35
40
|
empty = path == "empty"
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
65
|
+
end
|
60
66
|
end
|
61
67
|
end
|
62
68
|
end
|
data/lib/aniview/view/menu.rb
CHANGED
@@ -9,18 +9,17 @@ module Aniview
|
|
9
9
|
include Aniview::Util
|
10
10
|
include Observable
|
11
11
|
|
12
|
-
attr_accessor :
|
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
|
-
|
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
|
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
|
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
|
-
|
15
|
-
|
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
|
data/lib/application.rb
CHANGED
@@ -75,11 +75,10 @@ module Aniview
|
|
75
75
|
|
76
76
|
@aiomenu = View::AioMenu.new(
|
77
77
|
interface: @aio,
|
78
|
-
name: @pref.get("menu_titles")["
|
78
|
+
name: @pref.get("menu_titles")["library"],
|
79
79
|
pref: @pref,
|
80
|
-
format: "
|
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
|
174
|
+
def change_view(view:, rfunc: :items, format:nil, name:nil)
|
176
175
|
@view.delete_observers
|
177
176
|
@view.pause
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
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
|
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
|
-
|
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
|
-
|
251
|
-
|
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
|
-
|
256
|
-
|
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
|
-
|
261
|
-
|
268
|
+
change_view(views[:torrents])
|
269
|
+
current_view = 2
|
262
270
|
when kb["goto_preferences"]
|
263
|
-
|
264
|
-
|
271
|
+
change_view(views[:preferences ])
|
272
|
+
current_view = 5
|
265
273
|
when kb["goto_schedule"]
|
266
|
-
|
267
|
-
|
274
|
+
change_view(views[:schedule])
|
275
|
+
current_view = 3
|
268
276
|
when kb["goto_subscriptions"]
|
269
|
-
|
270
|
-
|
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.
|
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-
|
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"
|