aniview 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="