aniview 0.0.0 → 0.1.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: a8d8f8d8751f6ae9958f7fba7a91885cd69c4b6c
4
- data.tar.gz: 93cf3e3fda395b21eda803f48349cbec5add3069
3
+ metadata.gz: d81c0b1844154bc94629cf7169c785a92666a668
4
+ data.tar.gz: d616a2e6e77f1e2760d21f8c32b71353d67c51c0
5
5
  SHA512:
6
- metadata.gz: b03455fcf82c4828d9ccab8399b25f5aaf81ec456cbd4568da0ee78fc3ed67f50e21162f09b36536d00659bb89214785869be4092b7ca530986729c21026978a
7
- data.tar.gz: dfc42b6400d0ab11f50774b79871428bce747d2f198ea29ea85f215b9dbf107b2b2b21b3a4d05faac8058fa791a1c1a08ae5594738c21d71c6f8cf3905d94d94
6
+ metadata.gz: 36e06051afb3ec9d24319d2ab87e7ced2b12d9c80fa9f47ec0ebad466d2bd76582ac151cada7e9619cd6a20e89596eb03daf9b6485bc6b1338f2b44c66749401
7
+ data.tar.gz: be788e75a654904503993761973bf88c626ea02a293971b1868b1755f188b368df603486ff0106ea2b3d34a4f9e18c9aea6f3602c8f1ec5f67f5154df0b72b0a
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aniview'
4
+
5
+ av = AniView.new
6
+ av.run
@@ -1,8 +1,8 @@
1
1
  require 'highline'
2
- require 'rubygems'
3
2
  require 'highline/import'
4
3
  require 'json'
5
4
  require 'thread'
5
+ require 'io/console'
6
6
 
7
7
  require_relative 'aniview/view/aiomenu'
8
8
  require_relative 'aniview/view/prefmenu'
@@ -14,8 +14,124 @@ require_relative 'aniview/util/logger'
14
14
  require_relative 'aniview/util/term'
15
15
  require_relative 'aniview/util/pref'
16
16
 
17
- require_relative 'aniview/aniviewdriver'
18
-
19
17
  $stdout.sync = true
20
- av = AniViewDriver.new
21
- av.run
18
+
19
+ class AniView
20
+ def initialize
21
+
22
+ @term = Term.new
23
+
24
+ @pref = Pref.new
25
+
26
+ @format_ = {
27
+ "aiomenu_all" => {
28
+ "title" => "anime_library_title_format_str",
29
+ "parent" => "anime_library_title_show_format_str",
30
+ "child" => "anime_library_title_episode_format_str",
31
+ },
32
+ "aiomenu_uw" => {
33
+ "title" => "anime_library_unwatched_title_format_str",
34
+ "parent" => "anime_library_unwatched_title_show_format_str",
35
+ "child" => "anime_library_unwatched_title_episode_format_str",
36
+ },
37
+ "prefmenu" => {
38
+ "title" => "preferences_title_format_str",
39
+ "parent" => "preferences_item_format_str",
40
+ }
41
+ }
42
+
43
+ @aio = AnimeIO.new(@pref)
44
+ logger = Logger.new(@pref.get "log_file")
45
+ @aiomenu = AioMenu.new(
46
+ :getAll,
47
+ @aio,
48
+ @pref.get("menu_titles")["2"],
49
+ @pref,
50
+ @format_["aiomenu_all"],
51
+ @term
52
+ )
53
+
54
+ @prefmenu = PrefMenu.new(:getAll, @pref, @pref.get("menu_titles")["0"], @pref, @format_["prefmenu"], @term)
55
+
56
+ @term.save.hide_cursor.echo_off
57
+
58
+ end
59
+
60
+ def cleanup
61
+ @term.reset
62
+ exit
63
+ end
64
+
65
+ def runCommand(cmds)
66
+ cmd_s = cmds.split
67
+ cmd = cmd_s[0]
68
+ if cmd == "quit"
69
+ self.cleanup
70
+ elsif cmd == "sync"
71
+ #alist = Anilist.new
72
+ #alist.sync
73
+ elsif cmd == "pref"
74
+ @pref.set cmd_s[1], cmd_s[2]
75
+ elsif cmd == "index"
76
+ @aio.index_anime :force => true, :verbose => true
77
+ @aiomenu.refresh
78
+ end
79
+ end
80
+
81
+ def run
82
+
83
+ view = @aiomenu
84
+
85
+ mutex = Mutex.new
86
+ Signal.trap('SIGWINCH', proc {
87
+ Thread.new { mutex.synchronize { view.draw } }
88
+ } )
89
+
90
+ key = ""
91
+ view.refresh
92
+ view.set_screen_size(@term.rows, @term.cols)
93
+
94
+ while true
95
+ view.draw if key != "skip"
96
+
97
+ key = @term.getKey
98
+
99
+ if key == "1"
100
+ @term.clear
101
+ view = @aiomenu
102
+ view.setName(@pref.get("menu_titles")[key])
103
+ view.setFormat(@format_["aiomenu_uw"])
104
+ view.setRfunc(:getUnwatched)
105
+
106
+ elsif key == "2"
107
+ @term.clear
108
+ view = @aiomenu
109
+ view.setName(@pref.get("menu_titles")[key])
110
+ view.setFormat(@format_["aiomenu_all"])
111
+ view.setRfunc(:getAll)
112
+
113
+ elsif key == "3"
114
+ @term.clear
115
+ view = @anilistmenu
116
+ view.setName(@pref.get("menu_titles")[key])
117
+ view.setRfunc(:getCompleted)
118
+
119
+ elsif key == "0"
120
+ @term.clear
121
+ view = @prefmenu
122
+ view.setName(@pref.get("menu_titles")[key])
123
+ view.setRfunc(:getAll)
124
+
125
+ elsif key == ":"
126
+
127
+ cmd = Command.read(@term, ":")
128
+ self.runCommand cmd
129
+
130
+ elsif key == "q"
131
+ self.cleanup
132
+ end
133
+
134
+ view.control(key)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,74 @@
1
+ require 'streamio-ffmpeg'
2
+ require 'date'
3
+
4
+ require_relative '../../util/format'
5
+
6
+ class AnimeFile
7
+ def initialize(path_, seen = false)
8
+ @watched = seen
9
+ @watched_on = 0
10
+ @path = path_
11
+
12
+ if (@path == "empty")
13
+ @attr = {
14
+ "t" => "empty",
15
+ "d" => 0,
16
+ "D" => "0",
17
+ "s" => 0,
18
+ "S" => "0",
19
+ "r" => 0x0,
20
+ "a" => "n.a.",
21
+ "v" => "",
22
+ "l" => "",
23
+ "f" => "",
24
+ "b" => "",
25
+ }
26
+ else
27
+ mov = FFMPEG::Movie.new(@path)
28
+
29
+ @attr = {
30
+ "t" => self.string,
31
+ "d" => mov.duration,
32
+ "D" => Format.format_duration(mov.duration),
33
+ "s" => mov.size,
34
+ "S" => Format.format_size(mov.size),
35
+ "r" => mov.resolution,
36
+ "a" => mov.audio_codec,
37
+ "v" => mov.video_codec,
38
+ "l" => mov.colorspace,
39
+ "f" => mov.frame_rate,
40
+ "b" => String(mov.bitrate) + "kb/s",
41
+ }
42
+ end
43
+
44
+ end
45
+
46
+ def seen?
47
+ return @watched
48
+ end
49
+
50
+ def seenOn
51
+ return @watched_on
52
+ end
53
+
54
+ def watch
55
+ @watched = true
56
+ @watched_on = DateTime.now.strftime('%Q')
57
+ end
58
+
59
+ def unwatch
60
+ @watched = false
61
+ end
62
+
63
+ def path
64
+ return @path
65
+ end
66
+
67
+ def string
68
+ return (File.basename("#{@path}").gsub(/\s*\[.+?\]\s*/) {}).gsub(/(\.mkv)|(\.avi)|(\.mp4)/, "").gsub("_", " ").gsub(/\s*\(.+?\)\s*/) {}
69
+ end
70
+
71
+ def attributes
72
+ return @attr
73
+ end
74
+ end
@@ -0,0 +1,191 @@
1
+ require 'base64'
2
+ require_relative 'animefile'
3
+ require_relative 'animeseries'
4
+
5
+ class AnimeIO
6
+
7
+ def initialize(pref)
8
+ @pref = pref
9
+ @empty_hash = {
10
+ "empty" => AnimeFile.new(
11
+ "empty",
12
+ {
13
+ "d" => 0,
14
+ "s" => 0,
15
+ "r" => "0x0",
16
+ "a" => "n/a"
17
+ }
18
+ )
19
+ }
20
+ @watch_log_tag = "♪"
21
+
22
+ self.load
23
+ index_anime
24
+ end
25
+
26
+ def save
27
+ @pref.saveLocalAnime Base64.encode64(Marshal.dump @@local_anime)
28
+ end
29
+
30
+ def load
31
+ raw = @pref.get "local_anime"
32
+ if not raw == nil and raw.class == String
33
+ @@local_anime = Marshal.load(Base64.decode64(raw))
34
+ else
35
+ @@local_anime = {}
36
+ end
37
+ end
38
+
39
+ def index_anime(force: false, verbose: false)
40
+ ret = {}
41
+ (@pref.get("anime_locations").split(":")).each { |dir|
42
+ Dir.glob("#{dir}*/*.mkv").each { |file|
43
+ puts "indexing #{file}" if verbose
44
+ seen_this = false
45
+ if @@local_anime != nil and @@local_anime.key?(file)
46
+ seen_this = @@local_anime[file].seen?
47
+ if not force
48
+ ret.merge!(file => @@local_anime[file])
49
+ next
50
+ end
51
+ end
52
+ ret.merge!(
53
+ file => AnimeFile.new(
54
+ file,
55
+ seen_this
56
+ )
57
+ )
58
+
59
+ }
60
+ }
61
+ if @@local_anime != nil
62
+ @@local_anime.merge!(ret)
63
+ else
64
+ @@local_anime = ret
65
+ end
66
+
67
+ pruned = {}
68
+ @@local_anime.each{ |a|
69
+ pruned.merge!(a[0] => a[1]) if File.exist? a[1].path
70
+ }
71
+ @@local_anime = pruned
72
+
73
+ self.save
74
+ end
75
+
76
+ #returns the parent directory of a file
77
+ def parent(file)
78
+ return File.basename(File.dirname(file))
79
+ end
80
+
81
+ #convert an AnimeFile array to a hash structured like
82
+ #(string)parent => (AnimeFile array)[child0, child1, child2] and return it
83
+ def makeHash(arr)
84
+ return @empty_hash if arr[0] == nil
85
+ ret = {}
86
+ subarray = []
87
+
88
+
89
+ sarr = arr.sort_by {|s| s.path}
90
+ last_parent = parent sarr[0].path
91
+
92
+ (sarr).each { |animefile|
93
+
94
+ #puts animefile.path
95
+
96
+ this_parent = parent animefile.path
97
+
98
+ #puts animefile.path
99
+ #puts this_parent
100
+
101
+
102
+ if this_parent != last_parent
103
+ ret.merge!(
104
+ AnimeSeries.new(
105
+ last_parent,
106
+ subarray
107
+ ) => subarray)
108
+ subarray = [animefile]
109
+ else
110
+ subarray << animefile
111
+ end
112
+
113
+ last_parent = this_parent
114
+
115
+ }
116
+ ret.merge!(
117
+ AnimeSeries.new(
118
+ parent(sarr[sarr.length - 1].path),
119
+ subarray
120
+ ) => subarray
121
+ )
122
+
123
+
124
+ #puts ret.inspect
125
+ #exit
126
+
127
+ return Hash[ret.sort_by{ |a| a[0].title}]
128
+ end
129
+
130
+ #return the difference of getAired and getWatched
131
+ def getUnwatched
132
+ ret = []
133
+ #puts @@local_anime
134
+ @@local_anime.each{ |anime|
135
+ ret << anime[1] if not anime[1].seen?
136
+ }
137
+ return ret
138
+ end
139
+
140
+ #return an AnimeFile array of all mkvs in any dir specified in $pref["dirs"]
141
+ def getAll
142
+ return @@local_anime.values
143
+ end
144
+
145
+ def getAllSeries
146
+ arr = getAll
147
+
148
+ last_parent = parent arr[0].path
149
+ ret = [ last_parent ]
150
+
151
+ arr.each { |animefile|
152
+ this_parent = parent animefile.path
153
+ if this_parent != last_parent
154
+ ret << this_parent
155
+ end
156
+ last_parent = this_parent
157
+ }
158
+ return ret
159
+ end
160
+
161
+ def logWatched(file)
162
+ open(@pref.get("watch_log"), 'a') do |f|
163
+ f.puts %x(date).chomp + " ./" + File.basename(file) + " " + @watch_log_tag
164
+ end
165
+ end
166
+
167
+ def addWatched(file)
168
+ @@local_anime[file].watch
169
+ self.save
170
+ end
171
+
172
+ def rmWatched(path = "zoz")
173
+
174
+ if @@local_anime.key? path
175
+ @@local_anime[path].unwatch
176
+ return
177
+ end
178
+
179
+ r = []
180
+ @@local_anime.values.each_with_index{ |v, i| r << {Integer(v.seenOn) => @@local_anime.keys[i]} if v.seen? }
181
+ return if r.length == 0
182
+ last_watched = (r.sort_by{ |h| h.keys.first}.reverse)[0].values[0]
183
+ @@local_anime[last_watched].unwatch
184
+ self.save
185
+ end
186
+
187
+ #run mpv
188
+ def watch(file)
189
+ pid = spawn("mpv -alang jpn -slang eng -msg-level=all=fatal -ass '#{file}'")
190
+ end
191
+ end
@@ -0,0 +1,52 @@
1
+ require 'date'
2
+
3
+ require_relative '../../util/format'
4
+
5
+ class AnimeSeries
6
+ def initialize(_dir, children)
7
+ @path = _dir
8
+ @children = children
9
+
10
+ dur = getDuration
11
+ size = getSize
12
+
13
+ @attr = {
14
+ "t" => String(@path),
15
+ "c" => String(@children.length),
16
+ "d" => dur,
17
+ "D" => Format.format_duration(dur),
18
+ "s" => size,
19
+ "S" => Format.format_size(size),
20
+ }
21
+ end
22
+ def getDuration
23
+ dur = 0
24
+ @children.each{ |child|
25
+ dur += child.attributes["d"]
26
+ }
27
+ return dur
28
+ end
29
+
30
+ def getSize
31
+ size = 0
32
+ @children.each{ |child|
33
+ size += child.attributes["s"]
34
+ }
35
+ return size
36
+ end
37
+
38
+ def oldestChild
39
+ oldest = DateTime.now.strftime('%Q')
40
+ @children.each{ |child|
41
+
42
+ }
43
+ end
44
+
45
+ def title
46
+ return @path
47
+ end
48
+
49
+ def attributes
50
+ return @attr
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require 'readline'
2
+
3
+ class Command
4
+ def self.read(term, prompt = "", default = "")
5
+
6
+ term.bold.echo_on
7
+
8
+ print "\033[" + String(term.rows) + ";1H"
9
+ term.show_cursor
10
+
11
+ Readline.pre_input_hook = -> do
12
+ Readline.insert_text default
13
+ Readline.redisplay
14
+ Readline.pre_input_hook = nil
15
+ end
16
+
17
+ input = Readline.readline(prompt, false)
18
+
19
+ term.hide_cursor.nobold.echo_off.clear
20
+
21
+ return input
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ class Format
2
+ def self.format_duration v
3
+ hours = Integer((v-(v%3600))/3600)
4
+ minutes = Integer( ((v-(v%60))/60) - (hours * 60))
5
+ seconds = Integer(v%60)
6
+ if hours < 1 and minutes < 1
7
+ return String(seconds)
8
+ else
9
+ seconds = "0" + String(seconds) if seconds < 10
10
+ if hours < 1
11
+ return "#{String(minutes)}:#{String(seconds)}"
12
+ else
13
+ minutes = "0" + String(minutes) if minutes < 10
14
+ return "#{String(hours)}:#{String(minutes)}:#{String(seconds)}"
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.format_size s
20
+ fs = Float(s)
21
+
22
+ bytecount = {
23
+ "TB" => 1000000000000.0,
24
+ "GB" => 1000000000.0,
25
+ "MB" => 1000000.0,
26
+ "KB" => 1000.0,
27
+ "B" => 1.0,
28
+ }
29
+ r = "TB"
30
+ r = "GB" if s < bytecount["TB"]
31
+ r = "MB" if s < bytecount["GB"]
32
+ r = "KB" if s < bytecount["KB"]
33
+
34
+ return String( Float(fs/bytecount[r] * 10.0 ).round / 10.0 ) + r
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ class Logger
2
+ def initialize(file)
3
+ @logfile = file
4
+ open(@logfile, 'w') { |f| f.puts "initialized..." }
5
+ end
6
+ def log(string)
7
+ open(@logfile, 'a') { |f| f.puts string }
8
+ end
9
+ end
@@ -0,0 +1,137 @@
1
+ require 'fileutils'
2
+
3
+ class Pref
4
+ def initialize
5
+
6
+ @home = Dir.home
7
+ @conf = @home + "/.aw"
8
+
9
+ dirname = File.dirname(@conf)
10
+ unless File.directory?(dirname)
11
+ FileUtils.mkdir_p(dirname)
12
+ end
13
+
14
+ @pref_file = @conf + "/r_anime"
15
+
16
+ self.defaults if not File.exist?(@pref_file)
17
+ self.load
18
+ end
19
+
20
+ def defaults
21
+ @pref = Hash[
22
+ "anime_locations" => @home + "/airing/shows/" + ":" + @home + "/anime/",
23
+ "clr" => Hash[
24
+ "main" => "blue",
25
+ "primary" => "white",
26
+ "secondary"=> "cyan",
27
+ "selected" => "red"
28
+ ],
29
+ "menu_titles" => {
30
+ "1" => "unwatched",
31
+ "2" => "anime library",
32
+ "3" => "MyAnimeList",
33
+
34
+ "7" => "torrents",
35
+ "8" => "subscriptions",
36
+ "9" => "schedule",
37
+ "0" => "preferences"
38
+ },
39
+ "anime_library_title_format_str" => " %t - $r@ %D %S ",
40
+ "anime_library_title_show_format_str" => " %t - %c@ %D %S ",
41
+ "anime_library_title_episode_format_str" => " %t@ %D %S %t",
42
+ "anime_library_unwatched_title_format_str" => " %t@ $q@ %D %S ",
43
+ "anime_library_unwatched_title_show_format_str" => " %t - %c@ %D %S ",
44
+ "anime_library_unwatched_title_episode_format_str" => " %t@ %D %S %r ",
45
+
46
+ "preferences_title_format_str" => " %t",
47
+ "preferences_item_format_str" => " %t@.%v",
48
+
49
+ #"watch_file" => @conf + "/watched",
50
+ "log_file" => @conf + "/aw.log",
51
+ "watch_log" => @conf + "/watchlog",
52
+
53
+ "local_anime" => nil
54
+ ]
55
+ self.save
56
+ end
57
+
58
+ def set(s, val)
59
+ arr = [s] if s.class == String
60
+ arr = s if s.class == Array
61
+ @pref = setpref(arr, val, @pref)
62
+ save
63
+ end
64
+
65
+ def setpref(s, val, bpref)
66
+ if s.length == 1
67
+ bpref[s[0]]=val
68
+ return bpref
69
+ else
70
+ key = s.delete_at(0)
71
+ bpref[key] = setpref(s, val, bpref[key])
72
+ end
73
+ return bpref
74
+ end
75
+
76
+ def get s
77
+ return @pref[s] if @pref.key?(s)
78
+ return ""
79
+ end
80
+
81
+ def getAll
82
+ ignore = {
83
+ "local_anime" => true,
84
+ }
85
+
86
+ r = {}
87
+ @pref.each{ |item|
88
+ next if ignore.key? item[0]
89
+ title = item[0]
90
+ if item[1].class == String
91
+ val = item[1]
92
+ elsif item[1].class == Array
93
+ val = item[1].join(":")
94
+ elsif item[1].class == Hash
95
+ item[1].each { |subitem|
96
+ subtitle = title + "_" + subitem[0]
97
+ subval = subitem[1]
98
+
99
+ r.merge!(PrefItem.new(subtitle, subval, []) => PrefItem.new("", "", [item[0], subitem[0]]))
100
+ }
101
+ next
102
+ end
103
+ r.merge!(PrefItem.new(title, val, []) => PrefItem.new("", "", [item[0]]))
104
+ }
105
+ return r
106
+ end
107
+
108
+ def saveLocalAnime dump
109
+ @pref["local_anime"] = dump
110
+ self.save
111
+ end
112
+
113
+ def save
114
+ File.open(@pref_file, "w") { |f| f.write(@pref.to_json) }
115
+ end
116
+
117
+ def load
118
+ File.open(@pref_file, "r") {|f| @pref = JSON.parse(f.read)}
119
+ end
120
+ end
121
+
122
+ class PrefItem
123
+ def initialize(title, value, path_ = [])
124
+ @path = path_
125
+ @attr = {
126
+ "t" => title,
127
+ "v" => value,
128
+ }
129
+ end
130
+
131
+ def attributes
132
+ return @attr
133
+ end
134
+ def path
135
+ return @path
136
+ end
137
+ end
@@ -0,0 +1,37 @@
1
+ class Term
2
+
3
+ def initialize
4
+ @tput=Hash[
5
+ "smcup" => %x(tput smcup),
6
+ "rmcup" => %x(tput rmcup),
7
+ "civis" => %x(tput civis),
8
+ "cnorm" => %x(tput cnorm),
9
+ "el1" => "\e[2K",
10
+ "bold" => %x(tput bold),
11
+ "nobold" => %x(tput sgr0),
12
+ ]
13
+ end
14
+
15
+ def save; print @tput["smcup"]; return self; end
16
+ def restore; print @tput["rmcup"]; return self; end
17
+ def hide_cursor; print @tput["civis"]; return self; end
18
+ def show_cursor; print @tput["cnorm"]; return self; end
19
+ def clear_line; print @tput["el1"]; return self; end
20
+ def echo_off; %x(stty -echo); return self; end
21
+ def echo_on; %x(stty echo); return self; end
22
+ def clear; print "\033[2J"; return self; end
23
+ def bold; print @tput["bold"]; return self; end
24
+ def nobold; print @tput["nobold"]; return self; end
25
+
26
+ def cols; return HighLine::SystemExtensions.terminal_size[0]; end
27
+ def rows; return HighLine::SystemExtensions.terminal_size[1]; end
28
+
29
+ def getKey
30
+ 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")
31
+ end
32
+
33
+ def reset
34
+ self.restore.show_cursor.echo_on
35
+ end
36
+
37
+ end
@@ -0,0 +1,67 @@
1
+ require_relative 'menu'
2
+ require_relative '../util/format'
3
+
4
+
5
+ class AioMenu < Menu
6
+
7
+ def setmal malanime
8
+ @malanime = malanime
9
+ end
10
+
11
+ def refresh
12
+ @items = @interface.makeHash @interface.send(@refresh_func)
13
+
14
+ @attributes = {
15
+ "t" => @attributes["t"],
16
+ "D" => Format.format_duration(getDuration),
17
+ "S" => Format.format_size(getSize)
18
+ }
19
+
20
+ @expanded = -1 if @items.values[@expanded] == nil
21
+ fixCursor
22
+ end
23
+
24
+ def getDuration
25
+ dur = 0
26
+ @items.each{ |child|
27
+ dur += child[0].attributes["d"]
28
+ }
29
+ return dur
30
+ end
31
+
32
+ def getSize
33
+ size = 0
34
+ @items.each{ |child|
35
+ size += child[0].attributes["s"]
36
+ }
37
+ return size
38
+ end
39
+
40
+ def customControl(key, sel)
41
+ path = @items.values[sel["out"]][sel["in"]].path
42
+
43
+ if key == "space"
44
+ expand(sel["out"])
45
+ elsif key == "r"
46
+ refresh
47
+ elsif key == "enter"
48
+
49
+ @interface.watch(path)
50
+ @interface.addWatched(path)
51
+ @interface.logWatched(path)
52
+
53
+ #@malanime.update(path)
54
+
55
+ refresh
56
+ elsif key == "Z"
57
+ @interface.addWatched(path)
58
+ refresh
59
+ elsif key == "z"
60
+ @interface.rmWatched()
61
+ refresh
62
+ elsif key == "u"
63
+ @interface.rmWatched(path)
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,97 @@
1
+ #define ascii codes for color names
2
+ $clr = Hash[
3
+ "black" => "\e[30m", "red" => "\e[31m", "green" => "\e[32m", "yellow" => "\e[33m", "blue" => "\e[34m", "magenta" => "\e[35m", "cyan" => "\e[36m", "white" => "\e[37m",
4
+ "bright_black" => "\e[37m", "bright_red" => "\e[38m", "bright_green" => "\e[39m", "bright_yellow" => "\e[40m", "bright_blue" => "\e[41m", "bright_magenta" => "\e[42m", "bright_cyan" => "\e[43m", "bright_white" => "\e[44m",
5
+ ]
6
+
7
+ $emote = {
8
+ "amazed" => '( ゚д゚)',
9
+ "a" => '( ゚д゚)',
10
+ "angry" => '(#゚Д゚)',
11
+ "b" => '(#゚Д゚)',
12
+ "bad" => '(・A・)',
13
+ "c" => '(・A・)',
14
+ "bowing" => 'm(_ _)m',
15
+ "d" => 'm(_ _)m',
16
+ "bun" => '⊂二二二( ^ω^)二⊃',
17
+ "e" => '⊂二二二( ^ω^)二⊃',
18
+ "carefree" => '(´∀`)',
19
+ "f" => '(´∀`)',
20
+ "chinese" => '(`ハ´)',
21
+ "g" => '(`ハ´)',
22
+ "crying" => '( ´Д⊂ヽ',
23
+ "h" => '(´Д⊂ヽ',
24
+ "deflagged" => '[゚д゚]',
25
+ "i" => '[゚д゚]',
26
+ "deflated" => '(´・ω・`)',
27
+ "j" => '(´・ω・`)',
28
+ "depressed" => '( ´,_ゝ`)',
29
+ "k" => '( ´,_ゝ`)',
30
+ "dontknow" => '┐(`~`;)┌',
31
+ "l" => '┐(`~`;)┌',
32
+ "evillaugh" => '( ゚∀゚)アハハ八八ノヽノヽノヽノ \ / \/ \',
33
+ "m" => '( ゚∀゚)アハハ八八ノヽノヽノヽノ \ / \/ \',
34
+ "excitement" => 'キタ━━━━━━(゚∀゚)━━━━━━!!!!!',
35
+ "n" => 'キタ━━━━━━(゚∀゚)━━━━━━!!!!!',
36
+ "goofy" => '(゚∀゚)',
37
+ "o" => '(゚∀゚)',
38
+ "happy" => '( ゚ ヮ゚)',
39
+ "p" => '( ゚ ヮ゚)',
40
+ "heh" => '(´・∀・`)',
41
+ "q" => '(´•∀•`)',
42
+ "hooray" => '\(^o^)/',
43
+ "r" => '\(^o^)/',
44
+ "hugesurprise" => 'Σ(゚Д゚)',
45
+ "s" => 'Σ(゚Д゚)',
46
+ "impatience" => '(゚Д゚;≡;゚Д゚)',
47
+ "t" => '(゚Д゚;≡;゚Д゚)',
48
+ "indifferent" => '( ´_ゝ`)',
49
+ "u" => '( ´_ゝ`)',
50
+ "intuition" => 'm9(・∀・)',
51
+ "v" => 'm9(・∀・)',
52
+ "irritable" => 'ヽ(`Д´)ノ',
53
+ "w" => 'ヽ(`Д´)ノ',
54
+ "koi" => 'щ(゚Д゚щ)',
55
+ "x" => 'щ(゚Д゚щ)',
56
+ "korean" => '<`∀´>',
57
+ "y" => '<`∀´>',
58
+ "money" => '(・∀・)つ⑩',
59
+ "z" => '(・∀・)つ⑩',
60
+ "panting" => '( ´Д`)',
61
+ "M" => '( ´Д`)',
62
+ "peace" => 'ヽ(´ー`)ノ',
63
+ "N" => 'ヽ(´ー`)ノ',
64
+ "perky" => '(`・ω・´)',
65
+ "O" => '(`・ω・´)',
66
+ "sad" => '(´;ω;`)',
67
+ "P" => '(´;ω;`)',
68
+ "sarcasm" => '(・∀・)',
69
+ "A" => '(・∀・)',
70
+ "shocked" => 'Σ(゜д゜;)',
71
+ "B" => 'Σ(゜д゜;)',
72
+ "smoking" => '(´ー`)y-~~',
73
+ "C" => '(´ー`)y-~~',
74
+ "snorlax" => '( ̄ー ̄)',
75
+ "D" => '( ̄ー ̄)',
76
+ "spooked" => '(((( ;゚Д゚)))',
77
+ "E" => '(((( ;゚Д゚)))',
78
+ "stirring" => '(*´Д`)',
79
+ "F" => '(*´Д`)',
80
+ "supercilious" => 'm9(^Д^)',
81
+ "G" => 'm9(^Д^)',
82
+ "surprised" => '( ゚Д゚)',
83
+ "H" => '( ゚Д゚)',
84
+ "tfwnogf" => '(`A`)',
85
+ "I" => '(`A`)',
86
+ "thinking" => '(´-`).。oO',
87
+ "J" => '(´-`).。oO',
88
+ "unconvincing" => 'エェェ(´д`)ェェエ',
89
+ "K" => 'エェェ(´д`)ェェエ',
90
+ "unforeseen" => '(゚д゚)',
91
+ "L" => '(゚д゚)',
92
+ "_positive" => [0, 5, 13, 14, 15, 17, 37]
93
+ }
94
+ def positive_emote
95
+ return $emote.values[$emote["_positive"][rand($emote["_positive"].length)]]
96
+ end
97
+
@@ -0,0 +1,283 @@
1
+ require_relative 'emote'
2
+
3
+ class Menu
4
+
5
+ def initialize(refresh_function, interface, name, pref, format_, term)
6
+ @term = term
7
+ @format_ = format_
8
+ @attributes = {
9
+ "t" => name,
10
+ }
11
+ @pref = pref
12
+ @refresh_func = refresh_function
13
+ @interface = interface
14
+ @expanded = -1
15
+ @selected = 0
16
+ @scrollup = false
17
+
18
+ refresh
19
+ @lastcount = getLen
20
+
21
+ end
22
+
23
+ def setRfunc(name)
24
+ @refresh_func = name
25
+ refresh
26
+ end
27
+
28
+ def set_screen_size(lines, cols)
29
+ #@term.rows = lines
30
+ #@term.cols = cols
31
+ draw
32
+ end
33
+
34
+ def setName(name)
35
+ @attributes["t"] = name
36
+ end
37
+
38
+ def setFormat(format_)
39
+ @format_ = format_
40
+ end
41
+
42
+ def refresh
43
+ @items = @interface.makeHash @interface.send(@refresh_func)
44
+ @expanded = -1 if @items.values[@expanded] == nil
45
+ fixCursor
46
+ end
47
+
48
+ def expand(i)
49
+ return if i > @items.length
50
+ if @expanded == i
51
+ @selected = @expanded
52
+ @expanded = -1
53
+ else
54
+ @selected = i
55
+ @expanded = i
56
+ end
57
+ end
58
+
59
+ def resolveSelected(resolve_me=@selected)
60
+ in_expanded=false
61
+ inside = 0
62
+ outside = resolve_me
63
+ if @expanded > -1
64
+ if resolve_me > @expanded and resolve_me <= (@items.values[@expanded].length + @expanded)
65
+ outside = @expanded
66
+ inside = resolve_me - @expanded - 1
67
+ in_expanded=true
68
+ elsif resolve_me > @expanded
69
+ outside = resolve_me - @items.values[@expanded].length
70
+ end
71
+ end
72
+
73
+ return Hash["out" => outside, "in" => inside, "in_expanded" => in_expanded]
74
+ end
75
+
76
+ def moveCursor(dir)
77
+ @scrollup = false if dir == "down"
78
+ @scrollup = true if dir == "up"
79
+ @selected -= 1 if dir == "up"
80
+ @selected += 1 if dir == "down"
81
+ fixCursor
82
+ end
83
+
84
+ def fixCursor
85
+ #wrap selector
86
+ #@selected = getLen - 1 if @selected < 0
87
+ #@selected = 0 if @selected >= getLen
88
+ #
89
+ #nowrap
90
+ @selected = 0 if @selected < 0
91
+ @selected = getLen - 1 if @selected >= getLen
92
+ end
93
+
94
+ def getLen
95
+ len = @items.length
96
+ len += @items.values[@expanded].length if @expanded >= 0
97
+ return len
98
+ end
99
+
100
+ def color(index)
101
+ if index == @selected
102
+ return $clr[@pref.get("clr")["selected"]]
103
+ else
104
+ return $clr[@pref.get("clr")["secondary"]]
105
+ end
106
+ end
107
+
108
+ def draw
109
+ #the number of lines to preview
110
+ buffer = 3
111
+
112
+ #@term.rows-2 is used because 2 lines are reserved for
113
+ #the command line (the bottom line)
114
+ #and the title line (the top line)
115
+
116
+ if getLen > @term.rows - 2
117
+ finish = @selected + buffer #if not @scrollup
118
+ finish = @term.rows - 2 if finish < @term.rows - 2
119
+ finish = getLen if finish > getLen
120
+ start = finish - ( @term.rows-2 )
121
+ else
122
+ start = 0
123
+ finish = @term.rows - 2
124
+ end
125
+
126
+ drawFrom(start, finish)
127
+ end
128
+
129
+ def drawFrom(line_on, lines_to_draw)
130
+ #print menu header
131
+ printmf(
132
+ @pref.get(@format_["title"]),
133
+ 1,
134
+ false,
135
+ @attributes,
136
+ "title"
137
+ )
138
+
139
+ resolved_start_position = resolveSelected line_on
140
+
141
+ outer_index = resolved_start_position["out"]
142
+ inner_index = resolved_start_position["in"]
143
+
144
+ if resolved_start_position["in_expanded"]
145
+ outer_index+=1
146
+ end
147
+
148
+ offset=line_on-1
149
+
150
+ while line_on < lines_to_draw
151
+
152
+ in_outer_region = ! (line_on >= getLen)
153
+ in_expanded_region = resolveSelected(line_on)["in_expanded"]
154
+ real_line_on = line_on-offset+1
155
+ selected = line_on == @selected
156
+
157
+ if in_expanded_region
158
+ printmf(
159
+ @pref.get(@format_["child"]),
160
+ real_line_on,
161
+ selected,
162
+ @items.values[@expanded][ inner_index ].attributes,
163
+ "inside"
164
+ )
165
+ inner_index+=1
166
+ elsif in_outer_region
167
+ printmf(
168
+ @pref.get(@format_["parent"]),
169
+ real_line_on,
170
+ selected,
171
+ @items.keys[outer_index].attributes,
172
+ "outside"
173
+ )
174
+ outer_index+=1
175
+ else
176
+ print "\e[2K\n"
177
+ end
178
+
179
+ line_on+=1
180
+ end
181
+ end
182
+
183
+ def printmf(string, line, selected, d, loc)
184
+ lo = "\e[#{line};1H"
185
+ le = "\e[K\n"
186
+
187
+ if loc == "outside"
188
+ cl = $clr[@pref.get("clr")["primary"]]
189
+ elsif loc == "inside"
190
+ cl = $clr[@pref.get("clr")["secondary"]]
191
+ elsif loc == "title"
192
+ cl = $clr[@pref.get("clr")["main"]]
193
+ end
194
+
195
+ cl = $clr[@pref.get("clr")["selected"]] if selected
196
+
197
+ pstr = []
198
+ add_to = ""
199
+ buffer_char = []
200
+
201
+ percent_mode = false
202
+ dollar_mode = false
203
+ at_mode = false
204
+ escape_mode = false
205
+ padding_collected = 0
206
+
207
+ string.split('').each { |c|
208
+ if escape_mode == true
209
+ add_to += String(c)
210
+ escape_mode = false
211
+ elsif c == '%'
212
+ percent_mode = true
213
+ elsif c == '$'
214
+ dollar_mode = true
215
+ elsif c == '@'
216
+ pstr << add_to
217
+ add_to = ""
218
+ at_mode = true
219
+ elsif c == "#"
220
+ padding_collected += 1
221
+ elsif c == "\\"
222
+ escape_mode = true
223
+ elsif at_mode
224
+ buffer_char << c
225
+ at_mode = false
226
+ elsif percent_mode
227
+ addstr = ""
228
+ addstr = String(d[c]) if d.key?(c)
229
+ ol = addstr.length
230
+ while addstr.length < (ol + padding_collected)
231
+ addstr = " " + addstr
232
+ end
233
+
234
+ add_to += addstr
235
+
236
+ percent_mode = false
237
+ elsif dollar_mode
238
+ add_to += String($emote[c]) if $emote.key?(c)
239
+ dollar_mode = false
240
+ else
241
+ add_to += c
242
+ end
243
+ }
244
+ pstr << add_to
245
+
246
+ midstr = ""
247
+ tl = 0
248
+ pstr.each{ |s| tl += s.length }
249
+ buffer_space = 0
250
+ buffer_space = (@term.cols - tl) / (pstr.length - 1) if pstr.length > 1
251
+
252
+ addl_space = (@term.cols - tl) % pstr.length
253
+
254
+ pstr.each_with_index{ |str, i|
255
+ #buffer_space += addl_space if i == pstr.length - 2
256
+ #if i == pstr.length - 1
257
+ #puts addl_space
258
+ #puts i == pstr.length - 1
259
+
260
+ break if i == pstr.length - 1
261
+ buffer_ = ""
262
+ buffer_ = buffer_char[i] * buffer_space if buffer_space >= 0
263
+ midstr += str + buffer_
264
+ }
265
+ midstr += pstr[pstr.length - 1]
266
+
267
+ print lo + cl + midstr + le
268
+ end
269
+
270
+ def control(key)
271
+ sel = resolveSelected
272
+ if key == "up"
273
+ moveCursor("up")
274
+ elsif key == "down"
275
+ moveCursor("down")
276
+ else
277
+ customControl(key, sel)
278
+ end
279
+ end
280
+
281
+ def customControl(key, sel); end
282
+
283
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'menu'
2
+ require_relative '../util/format'
3
+ require_relative '../util/command'
4
+
5
+
6
+ class PrefMenu < Menu
7
+
8
+ def refresh
9
+ @items = @interface.send(@refresh_func)
10
+
11
+ @attributes = {
12
+ "t" => @attributes["t"],
13
+ }
14
+
15
+ @expanded = -1 if @items.values[@expanded] == nil
16
+ fixCursor
17
+ end
18
+
19
+ def customControl(key, sel)
20
+ path = @items.values[sel["out"]].path
21
+ title = @items.keys[sel["out"]].attributes["t"]
22
+ value = @items.keys[sel["out"]].attributes["v"]
23
+
24
+ if key == "space"
25
+ #expand(sel["out"])
26
+ elsif key == "r"
27
+ refresh
28
+ elsif key == "enter"
29
+
30
+ newval = Command.read(@term, title + ":", value)
31
+
32
+ @interface.set(path, newval)
33
+ refresh
34
+ end
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aniview
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - annacrombie
@@ -9,15 +9,69 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2017-04-17 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: streamio-ffmpeg
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: highline
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.7'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.7.8
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.7'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.7.8
13
53
  description: Browse local anime, based on cmus
14
54
  email: meotleft@gmail.com
15
- executables: []
55
+ executables:
56
+ - aniview
16
57
  extensions: []
17
58
  extra_rdoc_files: []
18
59
  files:
60
+ - bin/aniview
19
61
  - lib/aniview.rb
20
- homepage: http://rubygems.org/gems/aniview
62
+ - lib/aniview/interface/animeio/animefile.rb
63
+ - lib/aniview/interface/animeio/animeio.rb
64
+ - lib/aniview/interface/animeio/animeseries.rb
65
+ - lib/aniview/util/command.rb
66
+ - lib/aniview/util/format.rb
67
+ - lib/aniview/util/logger.rb
68
+ - lib/aniview/util/pref.rb
69
+ - lib/aniview/util/term.rb
70
+ - lib/aniview/view/aiomenu.rb
71
+ - lib/aniview/view/emote.rb
72
+ - lib/aniview/view/menu.rb
73
+ - lib/aniview/view/prefmenu.rb
74
+ homepage: https://github.com/annacrombie/aniview/
21
75
  licenses:
22
76
  - MIT
23
77
  metadata: {}
@@ -27,9 +81,9 @@ require_paths:
27
81
  - lib
28
82
  required_ruby_version: !ruby/object:Gem::Requirement
29
83
  requirements:
30
- - - ">="
84
+ - - ">"
31
85
  - !ruby/object:Gem::Version
32
- version: '0'
86
+ version: 2.2.0
33
87
  required_rubygems_version: !ruby/object:Gem::Requirement
34
88
  requirements:
35
89
  - - ">="