aniview 3.2.1 → 5.1.0

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