aniview 3.2.1 → 5.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -9
  3. data/bin/aniview +17 -9
  4. data/config/view.yml +3 -0
  5. data/lib/aniview.rb +1 -60
  6. data/modules/animeio/README.md +2 -0
  7. data/modules/animeio/config/daemon.yml +4 -0
  8. data/modules/animeio/config/view.yml +22 -0
  9. data/modules/animeio/lib/animeio.rb +24 -0
  10. data/modules/animeio/lib/animeio/anime_indexer.rb +99 -0
  11. data/modules/animeio/lib/animeio/animefile.rb +91 -0
  12. data/modules/animeio/lib/animeio/animeio.rb +175 -0
  13. data/modules/animeio/lib/animeio/animeio_view.rb +41 -0
  14. data/modules/animeio/lib/animeio/animeseries.rb +61 -0
  15. data/modules/mpv/README.md +2 -0
  16. data/modules/mpv/config/daemon.yml +2 -0
  17. data/modules/mpv/lib/mpv.rb +27 -0
  18. data/modules/mpv/lib/mpv/filename_widget.rb +16 -0
  19. data/modules/mpv/lib/mpv/mpv_controller.rb +85 -0
  20. data/modules/mpv/lib/mpv/time_widget.rb +29 -0
  21. metadata +33 -107
  22. data/lib/aniview/client/aniclient.rb +0 -118
  23. data/lib/aniview/interface/animeio/animefile.rb +0 -93
  24. data/lib/aniview/interface/animeio/animeio.rb +0 -321
  25. data/lib/aniview/interface/animeio/animeseries.rb +0 -60
  26. data/lib/aniview/interface/bridge.rb +0 -11
  27. data/lib/aniview/interface/deluge/delugec.rb +0 -172
  28. data/lib/aniview/interface/deluge/torrentitem.rb +0 -24
  29. data/lib/aniview/interface/item.rb +0 -24
  30. data/lib/aniview/interface/mpv/mpvbridge.rb +0 -149
  31. data/lib/aniview/interface/pref/defaults.json +0 -105
  32. data/lib/aniview/interface/pref/pref.rb +0 -209
  33. data/lib/aniview/interface/pref/prefitem.rb +0 -19
  34. data/lib/aniview/interface/pref/validate.json +0 -105
  35. data/lib/aniview/interface/schedule/schedule.rb +0 -99
  36. data/lib/aniview/interface/schedule/scheduleitem.rb +0 -59
  37. data/lib/aniview/interface/subscription/subscription.rb +0 -123
  38. data/lib/aniview/util/error.rb +0 -9
  39. data/lib/aniview/util/folder_listen.rb +0 -53
  40. data/lib/aniview/util/term.rb +0 -44
  41. data/lib/aniview/util/util.rb +0 -167
  42. data/lib/aniview/view/aiomenu.rb +0 -69
  43. data/lib/aniview/view/color.rb +0 -69
  44. data/lib/aniview/view/delugemenu.rb +0 -38
  45. data/lib/aniview/view/menu.rb +0 -325
  46. data/lib/aniview/view/prefmenu.rb +0 -26
  47. data/lib/aniview/view/schedulemenu.rb +0 -28
  48. data/lib/aniview/view/statusline.rb +0 -60
  49. data/lib/aniview/view/subscriptionmenu.rb +0 -37
  50. data/lib/application.rb +0 -306
  51. data/lib/daemon.rb +0 -188
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35841bf12f2ea102c4989e8ce4834fb71677720e
4
- data.tar.gz: 59a991c9ab06d540d15eca688e90ba4aa2af5a78
3
+ metadata.gz: cfe4f80430aec7a3a6eba43f9aef247d4e3d0f83
4
+ data.tar.gz: 422406a201ee35b9144d31963e2ae9fb5c3f6ac7
5
5
  SHA512:
6
- metadata.gz: c52b3272b6a5fc92f4c2e01ed56939c8f309ba669cabd751ddcb1e4a2b5e54bed48755dff119bbfeff2d97c63c717ecbaa62932a14f6bd6e142093be2920cef8
7
- data.tar.gz: cd05a2b6c9ebcf1ee532987e665136bd82d36b192cba2bbd98060cfdf4a4f1ff37556153810b6a9e41d4ed67a33604acf52d80a45dd14e7cf7a11102139c4d20
6
+ metadata.gz: 80196869ac0dfee147601ec706a588f795f61d7a5cc22c4b721dab295299a154d5a674afd11623616d7b22592af5e47ba2df46ef26cced60099470a1b920255d
7
+ data.tar.gz: 1eae0bc73e999fb61e2e7f3fe1ce27ae4f1b916c01e21c31ebf165e2b811454d46af891247c382f9361726f98048efd115edf5cd4d929b95a349ee2e1b0ac291
data/README.md CHANGED
@@ -1,26 +1,25 @@
1
1
  # aniview
2
- command line media library viewer
2
+ command line media library viewer powered by [zashoku](https://github.com/zashoku/zashoku)
3
3
 
4
- ![screenshot](https://raw.githubusercontent.com/annacrombie/aniview/master/screenshot.png)
4
+ ![screenshot](screenshot.png)
5
+
6
+ ## Note
7
+ This software is still in development and is pretty buggy
5
8
 
6
9
  ## features
7
10
  + well optimized, aniview consues very little resources
8
11
  + keeps track of which episodes you have watched, even on removable drives
9
- + ability to set up subscriptions to automatically torrent new episodes of shows as they become available
10
12
  + real time updating library as files are added / removed
11
13
  + fully customizeable display with format strings
12
- + client / server model with daemon process that keeps your shows up to date
14
+ + client / server model, with persistent daemon and multiple simultaneous connections.
15
+ - run aniview server on some computer and connect with a different one to use aniview like a remote
13
16
 
14
17
  ## installation
15
18
  First ensure that all the requirements are satisfied
16
19
 
17
20
  ### requirements
18
- + ruby 2.4.0
19
- + ffmpeg
21
+ + ffmpeg with ffprobe
20
22
  + an up to date version of [mpv](https://mpv.io/installation/)
21
- + Deluge*
22
-
23
- * Only required to use the torrent feature
24
23
 
25
24
  ### install using rubygems
26
25
  `gem install aniview`
@@ -1,14 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- begin
4
- require_relative '../lib/aniview' if File.exist?(File.join(File.dirname(__FILE__), '../lib/aniview.rb'))
5
- rescue LoadError
6
- require 'aniview'
4
+ module Zashoku
5
+ AppConf = {
6
+ app: {
7
+ root: File.expand_path('../', __dir__),
8
+ name: 'aniview',
9
+ modules: {
10
+ viewer: %w[AnimeIO Mpv],
11
+ daemon: %w[AnimeIO Mpv]
12
+ },
13
+ save_config: true,
14
+ save_history: true,
15
+ persistent_daemon: true
16
+ }
17
+ }
7
18
  end
8
19
 
9
- unless defined? Aniview
10
- puts "cant find aniview library"
11
- exit
12
- end
20
+ require File.join(Dir.home, 'github/zashoku/zashoku/lib/zashoku')
13
21
 
14
- Aniview::main(ARGV)
22
+ Zashoku.main(ARGV)
@@ -0,0 +1,3 @@
1
+ ---
2
+ format:
3
+ statusline: " %F@ ########%t/########%e #####%p "
@@ -1,62 +1,3 @@
1
- require_relative 'aniview/util/error'
2
- require_relative 'application'
3
- require_relative 'daemon'
4
-
5
- # Main namespace for Aniview and all its classes
6
1
  module Aniview
7
-
8
- self::Version = "3.2.1"
9
-
10
- def self.show_help
11
- puts "aniview"
12
- puts "-h : show this message"
13
- puts "-d <command> : send a command to the daemon"
14
- puts " help : show help"
15
- puts " start : start the daemon"
16
- puts " stop : stop the daemon"
17
- puts " info : show daemon info"
18
- puts " up? : check if the daemon is up"
19
- puts " nani : ###"
20
- puts " items : check the daemon's items"
21
- end
22
-
23
- # initializes either the application or the daemon with given arguments
24
- # @param argv an array of arguments, if -d is supplied, the remaining
25
- # arguments will be passed on to the daemon
26
- #
27
- # @return nil
28
- #
29
- def self.main argv
30
-
31
- main_class = nil
32
- daemonize = false
33
-
34
- argv.each { |arg|
35
- case arg
36
- when "-d"
37
- argv.delete_at(0)
38
- daemonize = true
39
- main_class = Aniview::Daemon.new(argv)
40
- when "-h"
41
- self.show_help
42
- exit
43
- else
44
- puts "unknown argument #{arg}"
45
- self.show_help
46
- exit
47
- end
48
- }
49
-
50
- main_class = Aniview::Application.new if main_class == nil
51
-
52
- if daemonize
53
- process = fork do
54
- main_class.run
55
- end
56
- Process.detach(process)
57
- else
58
- main_class.run
59
- end
60
- end
61
-
2
+ Version = [5, 1, 0].freeze
62
3
  end
@@ -0,0 +1,2 @@
1
+ # zashoku-animeio
2
+ media indexer
@@ -0,0 +1,4 @@
1
+ ---
2
+ anime_locations: '~/anime/:/Volumes/JEJ/anime/'
3
+ avs_files:
4
+ create?: true
@@ -0,0 +1,22 @@
1
+ ---
2
+ keymaps:
3
+ global:
4
+ "x": change_view animeio
5
+ animeio:
6
+ 10: remote::mpv watch $selected_item
7
+ Z: remote::animeio watched! $selected_item
8
+ z: remote::animeio unwatch
9
+ u: remote::animeio unwatch $selected_item
10
+ title:
11
+ animeio: animeio
12
+ format:
13
+ animeio:
14
+ title: " %t@ ##%w/##%c #####%Q ########%D #######%S "
15
+ parent: " %t@ ##%w/##%c #####%Q ########%D #######%S "
16
+ child: " %w %t@ ########%D #######%S "
17
+
18
+ animeio:
19
+ icons:
20
+ watched: ✓
21
+ unwatched: " "
22
+ partially: p
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zashoku
4
+ module AnimeIO
5
+ extend Zashoku::Module
6
+
7
+ Version = [1, 0, 0].freeze
8
+ Root = File.expand_path('../', __dir__)
9
+
10
+ EXTENSIONS = %w[mkv avi mp4].freeze
11
+ WATCH_LOG_TAG = '♪'
12
+
13
+ autoload :AnimeIO, File.join(Root, 'lib/animeio/animeio')
14
+ autoload :AnimeIOView, File.join(Root, 'lib/animeio/animeio_view')
15
+
16
+ def self.view
17
+ AnimeIOView.new
18
+ end
19
+
20
+ def self.controller
21
+ AnimeIO.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Zashoku
6
+ module AnimeIO
7
+ class AnimeIndexer
8
+ EMPTY_PARAMS = { watched: false, watched_on: 0 }.freeze
9
+ AVS_FILE = '.avs.yml'
10
+
11
+ attr_accessor :avs, :force, :indexed
12
+
13
+ def initialize
14
+ @made_changes = false
15
+ @avs = {}
16
+ @force = false
17
+ @indexed = {}
18
+ end
19
+
20
+ def made_changes?
21
+ @made_changes
22
+ end
23
+
24
+ def made_changes!
25
+ @made_changes = true
26
+ end
27
+
28
+ def update_avs(file)
29
+ obj = indexed[file]
30
+ dir = File.dirname(File.dirname(obj.path))
31
+ @avs = load_avs(File.join(dir, AVS_FILE))
32
+ @avs[file] ||= {}
33
+ @avs[file][:watched] = obj.watched?
34
+ @avs[file][:watched_on] = obj.watched_on
35
+ write_avs(File.join(dir, AVS_FILE))
36
+ end
37
+
38
+ def load_avs(file)
39
+ File.exist?(file) ? YAML.load(File.open(file, 'r', &:read)) : {}
40
+ end
41
+
42
+ def write_avs(file)
43
+ return unless Zashoku.conf.get(%w[avs_files create?])
44
+ File.open(file, 'w') { |f| f.puts YAML.dump(@avs) }
45
+ end
46
+
47
+ def glob_files(dir)
48
+ Dir.glob("#{dir}*/*.{#{Zashoku::AnimeIO::EXTENSIONS.join(',')}}")
49
+ end
50
+
51
+ def index_files(files)
52
+ files.map do |file|
53
+ Zashoku.logger.debug "indexing #{file}"
54
+
55
+ @avs[file] = EMPTY_PARAMS.dup unless avs.key?(file)
56
+
57
+ if indexed&.key?(file)
58
+ @avs[file][:watched] = indexed[file].watched?
59
+ @avs[file][:watched_on] = indexed[file].watched_on
60
+ end
61
+
62
+ {
63
+ file =>
64
+ # we have already indexed this file
65
+ # get its seen data
66
+ if indexed&.key?(file) && File::Stat.new(file).mtime <= indexed[file].last_modified
67
+ indexed[file]
68
+ else
69
+ made_changes!
70
+ AnimeFile.new(file, @avs[file])
71
+ end
72
+ }
73
+ end.compact.reduce(:merge) || {}
74
+ end
75
+
76
+ def index_dirs(dirs)
77
+ dirs.map do |dir|
78
+ next unless File.exist? dir
79
+ @avs = load_avs(File.join(dir, AVS_FILE))
80
+
81
+ i = index_files(glob_files(dir))
82
+
83
+ write_avs(File.join(dir, AVS_FILE))
84
+
85
+ i
86
+ end.reduce(:merge)
87
+ end
88
+
89
+ def index(dirs)
90
+ @made_changes = false
91
+ indexed_ = index_dirs(dirs)
92
+
93
+ made_changes! if indexed_.length != indexed.length
94
+
95
+ @indexed = indexed_
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'streamio-ffmpeg'
4
+ require 'date'
5
+
6
+ module Zashoku
7
+ module AnimeIO
8
+ class AnimeFile < Zashoku::Item
9
+ include Zashoku::Util
10
+
11
+ EMPTY_ATTR = {
12
+ 't' => 'empty',
13
+ 'd' => 0,
14
+ 'D' => '0',
15
+ 's' => 0,
16
+ 'S' => '0',
17
+ 'r' => 0x0,
18
+ 'a' => 'n.a.',
19
+ 'v' => '',
20
+ 'l' => '',
21
+ 'f' => '',
22
+ 'b' => '',
23
+ }.freeze
24
+
25
+ attr_accessor :last_modified, :accessible, :path, :watched_on, :attr
26
+
27
+ def initialize(path, attr = {})
28
+ @watched = attr['watched'] || false
29
+ @watched_on = attr['watched_on'] || 0
30
+ @path = path
31
+
32
+ begin
33
+ @last_modified = File::Stat.new(path).mtime
34
+ rescue Errno::ENOENT
35
+ @last_modified = '0'
36
+ end
37
+
38
+ @attr = (@path != 'empty') ? probe_file : EMPTY_ATTR
39
+ end
40
+
41
+ def attr
42
+ @attr.merge({
43
+ 'w' => watched? ? Zashoku.conf.get(%w[animeio icons watched]) : Zashoku.conf.get(%w[animeio icons unwatched])
44
+ })
45
+ end
46
+
47
+ def probe_file
48
+ mov = FFMPEG::Movie.new(@path)
49
+ {
50
+ 't' => pretty_string,
51
+ 'd' => mov.duration,
52
+ 'D' => Zashoku::Formatter.duration(mov.duration),
53
+ 's' => mov.size,
54
+ 'S' => Zashoku::Formatter.size(mov.size),
55
+ 'r' => mov.resolution,
56
+ 'a' => mov.audio_codec,
57
+ 'v' => mov.video_codec,
58
+ 'l' => mov.colorspace,
59
+ 'f' => mov.frame_rate,
60
+ 'b' => String(mov.bitrate) + 'kb/s',
61
+ }
62
+ end
63
+
64
+ def title
65
+ @attr['t']
66
+ end
67
+
68
+ def watched?
69
+ @watched
70
+ end
71
+
72
+ def watched!
73
+ @watched_on = DateTime.now.strftime('%Q')
74
+ @attr['w'] = 'x'
75
+ @watched = true
76
+ end
77
+
78
+ def unwatch
79
+ @watched = false
80
+ end
81
+
82
+ def pretty_string
83
+ @string || @string = Zashoku::Formatter.media_file(path)
84
+ end
85
+
86
+ def to_s
87
+ path
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+ require 'fileutils'
5
+ require 'observer'
6
+ require 'listen'
7
+ require 'benchmark'
8
+
9
+ require_relative 'animefile'
10
+ require_relative 'animeseries'
11
+ require_relative 'anime_indexer'
12
+ # require_relative '../../util/util'
13
+ # require_relative '../../util/folder_listen'
14
+
15
+ module Zashoku
16
+ module AnimeIO
17
+ class AnimeIO < Zashoku::Controller
18
+ include Zashoku::Util
19
+ include Observable
20
+
21
+ PLAYLIST = '/tmp/zashoku_list.m3u'
22
+
23
+ def initialize
24
+ @empty_hash = make_empty_hash
25
+ @dir_last_modified = {}
26
+ @made_changes = true
27
+ @indexer = AnimeIndexer.new
28
+ load
29
+ # p ref_changed!(verbose: false)
30
+ listen
31
+ index
32
+ # Zashoku.conf.add_observer(self, :pref_changed!)
33
+ end
34
+
35
+ def index
36
+ @indexer.index(dirs)
37
+ changed!('items') if @indexer.made_changes?
38
+ end
39
+
40
+ def indexed
41
+ @indexer.indexed
42
+ end
43
+
44
+ def changed!(e)
45
+ save
46
+ changed
47
+ notify_observers(sender: 'animeio', event: e)
48
+ end
49
+
50
+ def pref_changed!
51
+ @indexer.index(dirs)
52
+ changed! if @indexer.changed?
53
+ relisten
54
+ end
55
+
56
+ def listen
57
+ @listener = Listen.to(*dirs, only: /\.mkv$/) do |mod, add, del|
58
+ a = @indexer.index_files(add + mod)
59
+ indexed.merge!(a).reject! { |p| del.include? p }
60
+ changed!('items')
61
+ end
62
+ @listener.start
63
+ end
64
+
65
+ def animefile_changed!
66
+ return unless @mpvbridge.what_changed[:local_anime]
67
+ changed!
68
+ end
69
+
70
+ def dirs
71
+ Zashoku.conf.get(['anime_locations']).split(':').collect do |dir|
72
+ parsed_dir = Zashoku.conf.parse_dir(dir)
73
+ parsed_dir if File.exist?(parsed_dir)
74
+ end.compact
75
+ end
76
+
77
+ def make_empty_hash
78
+ af = AnimeFile.new('empty')
79
+ { AnimeSeries.new('empty', [af]) => [af] }
80
+ end
81
+
82
+ # returns the parent directory of a file
83
+ def parent(file)
84
+ File.basename(File.dirname(file))
85
+ end
86
+
87
+ # convert an AnimeFile array to a hash structured like (string)parent =>
88
+ # (AnimeFile array)[child0, child1, child2] and return it
89
+ def to_h(arr)
90
+ keys = {}
91
+ hash = {}
92
+
93
+ arr.sort_by(&:title).each do |file|
94
+ if keys.key?(parent(file.path))
95
+ key = keys[parent(file.path)]
96
+ else
97
+ key = AnimeSeries.new(parent(file.path))
98
+ keys[parent(file.path)] = key
99
+ hash[key] = []
100
+ end
101
+
102
+ hash[key] << file
103
+ key << file
104
+ end
105
+ hash.sort_by { |j| j[0].title }.to_h
106
+ end
107
+
108
+ # return the difference of getAired and getWatched
109
+ def unwatched
110
+ to_h(indexed.reject { |_f, a| a.watched? }.values)
111
+ end
112
+
113
+ # return an AnimeFile array of all mkvs in any dir specified in
114
+ # $pref["dirs"]
115
+ def items
116
+ to_h(indexed.values)
117
+ end
118
+
119
+ def log_watched(file)
120
+ file = Zashoku.conf.parse_dir(Zashoku.WATCH_LOG_TAG('watch_log'))
121
+ open(file, 'a') do |f|
122
+ f.puts "#{`date`.chomp} ./#{File.basename(file)} #{WATCH_LOG_TAG}"
123
+ end
124
+ end
125
+
126
+ def watched!(file)
127
+ return if indexed[file].watched?
128
+ indexed[file].watched!
129
+ changed!('items')
130
+ @indexer.update_avs(file)
131
+ end
132
+
133
+ def recently_watched
134
+ indexed.map { |k, v| { v.watched_on => k } if v.watched? }.compact
135
+ end
136
+
137
+ def unwatch(path = nil)
138
+ path =
139
+ if indexed[path]&.watched?
140
+ path
141
+ elsif !path
142
+ r = recently_watched
143
+ r.empty? ? nil : r.sort_by { |h| h.keys.first }.reverse[0].values[0]
144
+ else
145
+ nil
146
+ end
147
+
148
+ return unless path
149
+ indexed[path].unwatch
150
+ changed!('items')
151
+ @indexer.update_avs(path)
152
+ end
153
+
154
+ def watch(file)
155
+ Zashoku.command(mod: 'mpv', meth: :play, args: file)
156
+ end
157
+
158
+ def watch_list(files)
159
+ File.open(PLAYLIST, 'w') { |f| files.each { |i| f.puts i.path } }
160
+
161
+ Zashoku.command(mod: 'mpv', meth: :play, args: PLAYLIST)
162
+ end
163
+
164
+ def save
165
+ Zashoku.conf.set(['local_anime'], Util.encode_object(indexed))
166
+ end
167
+
168
+ def load
169
+ raw = Zashoku.conf.get(['local_anime'])
170
+ @indexer.indexed =
171
+ raw.class == String ? Util.decode_object(raw) : {}
172
+ end
173
+ end
174
+ end
175
+ end