tivohmo 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG +30 -0
  4. data/Gemfile +2 -0
  5. data/README.md +4 -6
  6. data/TODO +1 -0
  7. data/contrib/tivohmo.conf +2 -2
  8. data/contrib/tivohmo.plist +1 -1
  9. data/contrib/tivohmo.yml +60 -0
  10. data/lib/tivohmo/adapters/filesystem/file_item.rb +2 -3
  11. data/lib/tivohmo/adapters/plex/category.rb +87 -6
  12. data/lib/tivohmo/adapters/plex/episode.rb +30 -11
  13. data/lib/tivohmo/adapters/plex/group.rb +20 -0
  14. data/lib/tivohmo/adapters/plex/movie.rb +15 -3
  15. data/lib/tivohmo/adapters/plex/qualified_category.rb +14 -3
  16. data/lib/tivohmo/adapters/plex/season.rb +2 -2
  17. data/lib/tivohmo/adapters/plex/section.rb +5 -5
  18. data/lib/tivohmo/adapters/plex/transcoder.rb +5 -1
  19. data/lib/tivohmo/adapters/plex.rb +1 -0
  20. data/lib/tivohmo/adapters/settings/application.rb +37 -0
  21. data/lib/tivohmo/adapters/settings/display_item.rb +35 -0
  22. data/lib/tivohmo/adapters/settings/key_container.rb +35 -0
  23. data/lib/tivohmo/adapters/settings/metadata.rb +26 -0
  24. data/lib/tivohmo/adapters/settings/reset_defaults_item.rb +39 -0
  25. data/lib/tivohmo/adapters/settings/set_value_item.rb +38 -0
  26. data/lib/tivohmo/adapters/settings/transcoder.rb +23 -0
  27. data/lib/tivohmo/adapters/settings.rb +7 -0
  28. data/lib/tivohmo/adapters/streamio/transcoder.rb +39 -1
  29. data/lib/tivohmo/api/container.rb +5 -1
  30. data/lib/tivohmo/api/item.rb +2 -0
  31. data/lib/tivohmo/api/subtitle.rb +13 -0
  32. data/lib/tivohmo/api/transcoder.rb +1 -3
  33. data/lib/tivohmo/api.rb +1 -0
  34. data/lib/tivohmo/beacon.rb +3 -3
  35. data/lib/tivohmo/cli.rb +175 -48
  36. data/lib/tivohmo/config.rb +157 -0
  37. data/lib/tivohmo/server/views/_container.builder +1 -1
  38. data/lib/tivohmo/server.rb +6 -3
  39. data/lib/tivohmo/version.rb +1 -1
  40. data/lib/tivohmo.rb +1 -0
  41. data/spec/adapters/filesystem/file_item_spec.rb +1 -1
  42. data/spec/adapters/plex/application_spec.rb +10 -1
  43. data/spec/adapters/plex/category_spec.rb +93 -20
  44. data/spec/adapters/plex/episode_spec.rb +34 -15
  45. data/spec/adapters/plex/metadata_spec.rb +5 -7
  46. data/spec/adapters/plex/movie_spec.rb +10 -7
  47. data/spec/adapters/plex/qualified_category_spec.rb +17 -17
  48. data/spec/adapters/plex/season_spec.rb +23 -4
  49. data/spec/adapters/plex/section_spec.rb +4 -4
  50. data/spec/adapters/plex/show_spec.rb +19 -4
  51. data/spec/adapters/plex/transcoder_spec.rb +2 -8
  52. data/spec/adapters/settings/application_spec.rb +25 -0
  53. data/spec/adapters/settings/display_item_spec.rb +29 -0
  54. data/spec/adapters/settings/key_container_spec.rb +33 -0
  55. data/spec/adapters/settings/metadata_spec.rb +33 -0
  56. data/spec/adapters/settings/reset_defaults_item_spec.rb +54 -0
  57. data/spec/adapters/settings/set_value_item_spec.rb +54 -0
  58. data/spec/api/container_spec.rb +12 -0
  59. data/spec/api/item_spec.rb +2 -0
  60. data/spec/api/transcoder_spec.rb +0 -1
  61. data/spec/beacon_spec.rb +3 -1
  62. data/spec/cli_spec.rb +57 -19
  63. data/spec/config_spec.rb +224 -0
  64. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Application/_children/should_get_children.yml +61 -0
  65. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_allow_disabling_subtitles.yml +424 -0
  66. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_display_non-zero_child_count_once_children_fetched.yml +653 -0
  67. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children.yml +599 -0
  68. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_embedded_subtitles.yml +595 -0
  69. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_subtitles.yml +482 -0
  70. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_memoize.yml +478 -0
  71. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_refresh_children_when_config_changes.yml +1245 -0
  72. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_use_category_value_for_children.yml +395 -0
  73. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_instantiate.yml +61 -0
  74. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_set_presorted_if_present.yml +61 -0
  75. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_use_category_value_for_title_if_present.yml +61 -0
  76. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_initialize/should_instantiate.yml +444 -0
  77. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_allow_disabling_series_id_in_metadata.yml +444 -0
  78. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_populate_metadata.yml +579 -0
  79. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Metadata/_initialize/should_instantiate.yml +296 -0
  80. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_initialize/should_instantiate.yml +296 -0
  81. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_metadata/should_populate_metadata.yml +296 -0
  82. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_display_non-zero_child_count_once_children_fetched.yml +220 -0
  83. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_have_children.yml +393 -0
  84. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_memoize.yml +219 -0
  85. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_initialize/should_instantiate.yml +61 -0
  86. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_have_children.yml +444 -0
  87. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_memoize.yml +444 -0
  88. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_initialize/should_instantiate.yml +250 -0
  89. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_have_category_children.yml +61 -0
  90. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_memoize.yml +61 -0
  91. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_initialize/should_instantiate.yml +61 -0
  92. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_have_children.yml +250 -0
  93. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_memoize.yml +250 -0
  94. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_initialize/should_instantiate.yml +166 -0
  95. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Transcoder/_initialize/should_instantiate.yml +296 -0
  96. data/spec/server_spec.rb +31 -2
  97. data/spec/spec_helper.rb +24 -36
  98. data/tivohmo.gemspec +2 -2
  99. metadata +109 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 82b98cf908b70108f96047411d32f7e1b7fe4b86
4
- data.tar.gz: d78429acd344c4df742e8e849600a71ad5ccbb91
3
+ metadata.gz: bf38e58ce80df9b5a177019960ace75cee1214cd
4
+ data.tar.gz: 84358dcbc8cc552c560a34b67cd3bec1f40d7d62
5
5
  SHA512:
6
- metadata.gz: e1d414df9691878b936744d0ce977806df8b820078876e763b3b116d13167b8fabd71b46c1618c30426ae898152f0105d5b17785b1ff2b8a3642dcbffd925c48
7
- data.tar.gz: 31eb7b4f97bc01856f0148f9483ebf1746129dfc9f9f552ecf837a8f02d9923222f0a9f1f823e4e339706c9c1f10eb3095aff035fcd49bcf6323c0e9feb08af9
6
+ metadata.gz: 1643360b023aa69207324541bfd041b25ce3d07e7211aa57bc7a3b33ab6b497c6063a560ba26ebe0f577e18a4fc8361838de7778b5758b5250b06f59996e62be
7
+ data.tar.gz: 807c936f1152bb55a0a62ddcb6c933aaa0c03d19c44cf407fde517f283ca16c8e3ccb6fb2d84744090f3cb90885077fbde392fc831ac75540d59ab85af34fee0
data/.travis.yml CHANGED
@@ -4,7 +4,7 @@ rvm:
4
4
  - 2.1.3
5
5
 
6
6
  before_script:
7
- - sudo add-apt-repository ppa:djcj/vlc-stable -y
7
+ - sudo add-apt-repository ppa:archivematica/externals -y
8
8
  - sudo apt-get update
9
9
  - sudo apt-get install -y ffmpeg
10
10
 
data/CHANGELOG CHANGED
@@ -1,3 +1,33 @@
1
+ 0.3.0 (03/01/2015)
2
+ ------------------
3
+
4
+ Use a group for subtitles+original <a552478> [Matt Conway]
5
+ add ability to optionally hardcode subtitles when present <df079f8> [Matt Conway]
6
+ add ability to skip sorting by setting presorted flag <5148bcf> [Matt Conway]
7
+ add reset to defaults <71f12ce> [Matt Conway]
8
+ add settings mechanism and app to control it from tivo ui <8fce56c> [Matt Conway]
9
+ add todo <e7b0f6b> [Matt Conway]
10
+ allow implementations to not have to load children to show a count <40464c6> [Matt Conway]
11
+ allow reading cli config from file <f208616> [Matt Conway]
12
+ allow video fixture with vcr <09a792e> [Matt Conway]
13
+ app for changing runtime settings <4f1cd05> [Matt Conway]
14
+ beacon seems to work better on linux if I create a new socket for each broadcast <879573a> [Matt Conway]
15
+ better title for subs <1755a82> [Matt Conway]
16
+ change ffmpeg ppa <11c96c0> [Matt Conway]
17
+ cli install templates <e9ec91f> [Matt Conway]
18
+ config tests <0c3b23e> [Matt Conway]
19
+ config working <57f7fc8> [Matt Conway]
20
+ fix performance issue with subtitles, allow for embedded subtitles (e.g. mkv) <6da6aa3> [Matt Conway]
21
+ fix plex cache updating <5f45b69> [Matt Conway]
22
+ more metadata <8608b5c> [Matt Conway]
23
+ move webmock to gemfile to prevent auto require <3f19631> [Matt Conway]
24
+ new vcr fixtures <1dfa166> [Matt Conway]
25
+ partial <951dada> [Matt Conway]
26
+ redo plex tests with vcr <5da4030> [Matt Conway]
27
+ refactor to make item have a file, refactor subtitles and transcoding, misc tests <70a8f37> [Matt Conway]
28
+ update readme <2338cff> [Matt Conway]
29
+ work with rvm <ecff585> [Matt Conway]
30
+
1
31
  0.2.3 (11/29/2014)
2
32
  ------------------
3
33
 
data/Gemfile CHANGED
@@ -5,3 +5,5 @@ gemspec
5
5
 
6
6
  # for code coverage during travis-ci test runs
7
7
  gem 'coveralls', :require => false
8
+ gem 'webmock', require: false
9
+ #gem 'vcr', require: false
data/README.md CHANGED
@@ -21,14 +21,11 @@ Install the gem:
21
21
 
22
22
  $ gem install tivohmo
23
23
 
24
- Or to use this gem in your own ruby project, add this line to your application's Gemfile:
24
+ then install as a daemon with default configuration:
25
25
 
26
- gem 'tivohmo'
27
-
28
- And then execute:
29
-
30
- $ bundle
26
+ $ tivohmo --install
31
27
 
28
+ Optionally edit the installed configuration file mentioned when the install command executes, and restart the daemon.
32
29
 
33
30
  ## Usage
34
31
 
@@ -36,6 +33,7 @@ Run the 'tivohmo' binary for usage:
36
33
 
37
34
  tivohmo --help
38
35
 
36
+
39
37
  ## Developing
40
38
 
41
39
  Use the classes in TivoHMO::API to create your tree of data (Application -> Container(s) -> Items), then serve them with TivoHMO::Server. See TivoHMO::Adapters::Filesystem for a sample implementation based on folders/files/ffmpeg
data/TODO ADDED
@@ -0,0 +1 @@
1
+ fix back after watching movie (anchor)
data/contrib/tivohmo.conf CHANGED
@@ -9,7 +9,7 @@ stop on runlevel [!2345]
9
9
 
10
10
  respawn
11
11
  respawn limit 10 5
12
- setuid user
12
+ setuid root
13
13
  umask 022
14
14
 
15
- exec tivohmo --preload --port 9033 -a TivoHMO::Adapters::Filesystem::Application -i /My/Video -t Video -a TivoHMO::Adapters::Plex::Application -i localhost -t "Plex Video"
15
+ exec bash -l -c "tivohmo --configuration /etc/tivohmo.yml"
@@ -16,7 +16,7 @@ Then to start the server, run: launchctl load ~/Library/LaunchAgents/tivohmo.pli
16
16
  <string>bash</string>
17
17
  <string>-l</string>
18
18
  <string>-c</string>
19
- <string>tivohmo -p 9033 -l ~/Library/Logs/tivohmo.log -a TivoHMO::Adapters::Plex::Application -i localhost -t 'Plex Videos'</string>
19
+ <string>tivohmo --configuration <%= File.expand_path(config[:configuration]) %></string>
20
20
  </array>
21
21
  <key>RunAtLoad</key>
22
22
  <true/>
@@ -0,0 +1,60 @@
1
+ # The network port to listen to tivo requests on
2
+ port: 9033
3
+
4
+ # Run with debug logging enabled
5
+ debug: false
6
+
7
+ # Walk the application trees to preload all data for quicker browsing at
8
+ # the expense of slower startup
9
+ preload: false
10
+
11
+ # The file to record tivohmo logs in
12
+ logfile: <%= config[:logfile] %>
13
+
14
+ # The runtime settings file for saving config changes made using the
15
+ # Settings app. Must be writable by the tivohmo process
16
+ settings: <%= config[:settings] %>
17
+
18
+ # The limit:interval for broadcasting beacon signals to the network
19
+ # -1 as the limit means infinite
20
+ beacon: "-1:10"
21
+
22
+ # The applications to enable
23
+ applications:
24
+ # The Settings app for editing runtime changeable settings
25
+ TivoHMO Settings:
26
+ application: TivoHMO::Adapters::Settings::Application
27
+ identifier: settings
28
+ # The Plex app for sharing video from a plex media server. This currently
29
+ # only works if the plex server is running on the same host as tivohmo
30
+ TivoHMO Plex Videos:
31
+ application: TivoHMO::Adapters::Plex::Application
32
+ identifier: localhost
33
+ # # The filesystem app for sharing video from a directory
34
+ # TivoHMO Filesystem Videos:
35
+ # application: TivoHMO::Adapters::Filesystem::Application
36
+ # identifier: /My/Video
37
+
38
+
39
+ # BEGIN RUNTIME EDITABLE SETTINGS
40
+ #
41
+
42
+ # These settings are all editable from the TivoHMO Settings app, and will
43
+ # get saved to the file specified in the 'settings' config above, thereby
44
+ # overriding any settings configured below.
45
+
46
+ # Detect subtitles for videos and include them as a choice when browsing
47
+ # Runtime editable from Settings app
48
+ enable_subtitles: true
49
+
50
+ # Force groups/folders regardless of parameters supplied by the tivo request
51
+ # Runtime editable from Settings app
52
+ force_grouping: true
53
+
54
+ # Use zap2it ID for grouping episodes (Gives thumbnail in My Shows, but can
55
+ # cause problems)
56
+ # Runtime editable from Settings app
57
+ group_with_zap2it: true
58
+
59
+ #
60
+ # END RUNTIME EDITABLE SETTINGS
@@ -9,14 +9,13 @@ module TivoHMO
9
9
  include TivoHMO::API::Item
10
10
  include GemLogger::LoggerSupport
11
11
 
12
- attr_reader :full_path
13
-
14
12
  def initialize(identifier)
15
- @full_path = File.expand_path(identifier)
13
+ full_path = File.expand_path(identifier)
16
14
  raise ArgumentError, "Must provide an existing file" unless File.file?(full_path)
17
15
 
18
16
  super(full_path)
19
17
 
18
+ self.file = full_path
20
19
  self.title = File.basename(self.identifier)
21
20
  self.modified_at = File.mtime(self.identifier)
22
21
  self.created_at = File.ctime(self.identifier)
@@ -6,15 +6,20 @@ module TivoHMO
6
6
  include TivoHMO::API::Container
7
7
  include GemLogger::LoggerSupport
8
8
  include MonitorMixin
9
+ include TivoHMO::Config::Mixin
9
10
 
10
11
  attr_reader :delegate
11
12
  attr_accessor :category_type, :category_value
12
13
 
13
- def initialize(delegate, category_type, category_value=nil)
14
+ config_register(:enable_subtitles, true,
15
+ "For items that have subtitles, adds entries that will transcode with those subtitles hardcoded")
16
+
17
+ def initialize(delegate, category_type, category_value=nil, presorted=false)
14
18
  # delegate is a Plex::Section
15
19
  @delegate = delegate
16
20
 
17
21
  super(delegate.key)
22
+ self.presorted = presorted
18
23
 
19
24
  self.category_type = category_type
20
25
  self.category_value = category_value
@@ -27,20 +32,33 @@ module TivoHMO
27
32
 
28
33
  self.modified_at = Time.at(delegate.updated_at.to_i)
29
34
  self.created_at = Time.now
35
+ @subtitles = config_get(:enable_subtitles)
36
+ end
37
+
38
+ alias_method :super_children, :children
39
+
40
+ def child_count
41
+ super_children.size
30
42
  end
31
43
 
32
44
  def children
33
45
  synchronize do
34
46
 
35
- delegate.refresh
36
- new_modified_at = delegate.updated_at.to_i
47
+ # updated_at doesn't get updated for automatic updates, only
48
+ # for updating from within plex media server web ui
49
+ section_id = delegate.key.split('/').last
50
+ new_delegate = delegate.library.section!(section_id)
51
+ new_modified_at = new_delegate.updated_at.to_i
37
52
  if new_modified_at > modified_at.to_i
38
53
  logger.info "Plex section was updated, refreshing"
54
+ @delegate = new_delegate
39
55
  self.modified_at = Time.at(new_modified_at)
40
56
  super.clear
41
57
  end
42
58
 
43
- if super.blank?
59
+ if super.blank? || @subtitles != config_get(:enable_subtitles)
60
+ super.clear
61
+ @subtitles = config_get(:enable_subtitles)
44
62
 
45
63
  if category_value
46
64
  listing = delegate.send(category_type, category_value[:key])
@@ -50,9 +68,9 @@ module TivoHMO
50
68
 
51
69
  Array(listing).each do |media|
52
70
  if media.is_a?(::Plex::Movie)
53
- add_child(Movie.new(media))
71
+ add_grouped(Movie, media)
54
72
  elsif media.is_a?(::Plex::Episode)
55
- add_child(Episode.new(media))
73
+ add_grouped(Episode, media)
56
74
  elsif media.is_a?(::Plex::Show)
57
75
  add_child(Show.new(media))
58
76
  else
@@ -65,6 +83,69 @@ module TivoHMO
65
83
  super
66
84
  end
67
85
 
86
+ def add_grouped(item_class, item_delegate)
87
+ primary = item_class.new(item_delegate)
88
+
89
+ if config_get(:enable_subtitles)
90
+ subs = find_subtitles(item_delegate)
91
+
92
+ if subs.size > 0
93
+ group = Group.new(primary.identifier, primary.title)
94
+ add_child(group)
95
+ group.add_child(primary)
96
+ subs.each {|s| group.add_child(item_class.new(item_delegate, s)) }
97
+ else
98
+ add_child(primary)
99
+ end
100
+ else
101
+ add_child(primary)
102
+ end
103
+ end
104
+
105
+ def find_subtitles(item_delegate)
106
+ subs = []
107
+
108
+ source_filename = CGI.unescape(item_delegate.medias.first.parts.first.file)
109
+
110
+ item_delegate.medias.each do |media|
111
+ media.parts.each do |part|
112
+ prev_stream_count = 0
113
+ part.streams.each do |stream|
114
+
115
+ # stream.stream_type 3=subs, 1=video, 2=audio
116
+ # stream.key.present? means file based srt
117
+ # stream.index and no key, means embedded
118
+ if stream.stream_type.to_i == 3
119
+ if %w[key language language_code].all? {|m| stream.respond_to?(m) && stream.send(m).present? }
120
+ st = TivoHMO::API::Subtitle.new
121
+ st.language = stream.language
122
+ st.language_code = stream.language_code
123
+ st.format = stream.codec
124
+ st.type = :file
125
+ st.location = source_filename.chomp(File.extname(source_filename))
126
+ subs << st
127
+ elsif stream.respond_to?(:index) && stream.index.present?
128
+ st = TivoHMO::API::Subtitle.new
129
+ st.language = stream.respond_to?(:language) && stream.language || "Embedded"
130
+ st.language_code = stream.respond_to?(:language_code) && stream.language_code || "???"
131
+ st.format = stream.codec
132
+ st.type = :embedded
133
+ # subtitle index should be the index amongst just the embedded subtitle streams
134
+ st.location = stream.index.to_i - prev_stream_count
135
+ subs << st
136
+ else
137
+ logger.warn "Unrecognized subtitle for #{item_delegate.title}"
138
+ end
139
+ end
140
+
141
+ prev_stream_count += 1
142
+ end
143
+ end
144
+ end
145
+
146
+ subs
147
+ end
148
+
68
149
  end
69
150
 
70
151
  end
@@ -7,16 +7,25 @@ module TivoHMO
7
7
  class Episode
8
8
  include TivoHMO::API::Item
9
9
  include GemLogger::LoggerSupport
10
+ include TivoHMO::Config::Mixin
10
11
 
11
- attr_reader :delegate
12
+ attr_reader :delegate, :subtitle
12
13
 
13
- def initialize(delegate)
14
+ config_register(:group_with_zap2it, true,
15
+ "Use zap2it ID for grouping episodes (Gives thumbnail in My Shows, but can cause problems)")
16
+
17
+ def initialize(delegate, subtitle=nil)
14
18
  # delegate is a Plex::Episode
15
19
  @delegate = delegate
16
20
 
17
21
  super(delegate.key)
18
22
 
23
+ self.file = CGI.unescape(delegate.medias.first.parts.first.file)
24
+ self.subtitle = subtitle
25
+
19
26
  self.title = delegate.title
27
+ self.title = "[#{subtitle.language_code} sub] #{self.title}" if subtitle
28
+
20
29
  self.modified_at = Time.at(delegate.updated_at.to_i)
21
30
  self.created_at = Time.parse(delegate.originally_available_at) rescue nil
22
31
  self.created_at ||= Time.at(delegate.added_at.to_i)
@@ -29,7 +38,7 @@ module TivoHMO
29
38
 
30
39
  md.original_air_date = Time.parse(delegate.originally_available_at) rescue nil
31
40
 
32
- rating_name = delegate.content_rating.upcase
41
+ rating_name = delegate.content_rating.upcase rescue nil
33
42
  rating_value = TivoHMO::API::Metadata::TV_RATINGS[rating_name]
34
43
  if rating_value
35
44
  md.tv_rating = {name: rating_name, value: rating_value}
@@ -43,6 +52,13 @@ module TivoHMO
43
52
 
44
53
  md.series_id = lookup_series_id
45
54
 
55
+ md.actors = delegate.roles.collect(&:tag) rescue nil
56
+ md.series_genres = delegate.genres.collect(&:tag) rescue nil
57
+
58
+ %w[writers directors producers].each do |md_name|
59
+ md.send("#{md_name}=", delegate.send(md_name).collect(&:tag)) rescue nil
60
+ end
61
+
46
62
  rescue => e
47
63
  logger.log_exception e, "Failed to read plex metadata for #{self}"
48
64
  end
@@ -52,20 +68,23 @@ module TivoHMO
52
68
 
53
69
  # groups tv shows under same name if we can extract a seriesId
54
70
  def lookup_series_id
55
- series_id = nil
71
+ series_id = "SH%08i" % delegate.season.show.key.scan(/\d+/).first.to_i
56
72
 
57
73
  guid = delegate.guid
58
74
  if guid =~ /thetvdb:\/\/(\d+)/
59
75
  # zap2it ids allow tivo to show a relevant thumbnail image for the
60
76
  # series in the My Shows UI.
61
77
  tvdb_series_id = $1
62
- begin
63
- tvdb_series = TVDBHelper.instance.find_by_id(tvdb_series_id)
64
- series_id = tvdb_series.zap2it_id
65
- series_id = series_id.sub(/^EP/, 'SH')
66
- logger.debug "Using zap2it series id: #{series_id}"
67
- rescue => e
68
- logger.log_exception e, "Failed to get zap2it series id", level: :warn
78
+
79
+ if config_get(:group_with_zap2it)
80
+ begin
81
+ tvdb_series = TVDBHelper.instance.find_by_id(tvdb_series_id)
82
+ series_id = tvdb_series.zap2it_id
83
+ series_id = series_id.sub(/^EP/, 'SH')
84
+ logger.debug "Using zap2it series id: #{series_id}"
85
+ rescue => e
86
+ logger.log_exception e, "Failed to get zap2it series id", level: :warn
87
+ end
69
88
  end
70
89
 
71
90
  series_id ||= "SH#{tvdb_series_id}"
@@ -0,0 +1,20 @@
1
+ module TivoHMO
2
+ module Adapters
3
+ module Plex
4
+
5
+ class Group
6
+ include TivoHMO::API::Container
7
+ include GemLogger::LoggerSupport
8
+
9
+ def initialize(ident, title)
10
+ super(ident)
11
+
12
+ self.presorted = true
13
+ self.title = title
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -6,15 +6,20 @@ module TivoHMO
6
6
  include TivoHMO::API::Item
7
7
  include GemLogger::LoggerSupport
8
8
 
9
- attr_reader :delegate
9
+ attr_reader :delegate, :subtitle
10
10
 
11
- def initialize(delegate)
11
+ def initialize(delegate, subtitle=nil)
12
12
  # delegate is a Plex::Movie
13
13
  @delegate = delegate
14
14
 
15
15
  super(delegate.key)
16
16
 
17
+ self.file = CGI.unescape(delegate.medias.first.parts.first.file)
18
+ self.subtitle = subtitle
19
+
17
20
  self.title = delegate.title
21
+ self.title = "[#{subtitle.language_code} sub] #{self.title}" if subtitle
22
+
18
23
  self.modified_at = Time.at(delegate.updated_at.to_i)
19
24
  self.created_at = Time.parse(delegate.originally_available_at) rescue nil
20
25
  self.created_at ||= Time.at(delegate.added_at.to_i)
@@ -27,12 +32,19 @@ module TivoHMO
27
32
 
28
33
  md.movie_year = Time.parse(delegate.originally_available_at).year rescue nil
29
34
 
30
- rating_name = delegate.content_rating.upcase
35
+ rating_name = delegate.content_rating.upcase rescue nil
31
36
  rating_value = TivoHMO::API::Metadata::MPAA_RATINGS[rating_name]
32
37
  if rating_value
33
38
  md.mpaa_rating = {name: rating_name, value: rating_value}
34
39
  end
35
40
 
41
+ md.actors = delegate.roles.collect(&:tag) rescue nil
42
+ md.program_genres = delegate.genres.collect(&:tag) rescue nil
43
+
44
+ %w[writers directors producers].each do |md_name|
45
+ md.send("#{md_name}=", delegate.send(md_name).collect(&:tag)) rescue nil
46
+ end
47
+
36
48
  rescue => e
37
49
  logger.log_exception e, "Failed to read plex metadata for #{self}"
38
50
  end
@@ -15,6 +15,7 @@ module TivoHMO
15
15
  @delegate = delegate
16
16
 
17
17
  super(delegate.key)
18
+ self.presorted = true
18
19
 
19
20
  self.category_type = category_type
20
21
  self.category_qualifier = category_qualifier
@@ -23,13 +24,23 @@ module TivoHMO
23
24
  self.created_at = Time.now
24
25
  end
25
26
 
27
+ alias_method :super_children, :children
28
+
29
+ def child_count
30
+ super_children.size
31
+ end
32
+
26
33
  def children
27
34
  synchronize do
28
35
 
29
- delegate.refresh
30
- new_modified_at = delegate.updated_at.to_i
36
+ # updated_at doesn't get updated for automatic updates, only
37
+ # for updating from within plex media server web ui
38
+ section_id = delegate.key.split('/').last
39
+ new_delegate = delegate.library.section!(section_id)
40
+ new_modified_at = new_delegate.updated_at.to_i
31
41
  if new_modified_at > modified_at.to_i
32
42
  logger.info "Plex section was updated, refreshing"
43
+ @delegate = new_delegate
33
44
  self.modified_at = Time.at(new_modified_at)
34
45
  super.clear
35
46
  end
@@ -39,7 +50,7 @@ module TivoHMO
39
50
  # Sort by title descending so that creation times are
40
51
  # correct for tivo sort of newest first (Time.now for
41
52
  # created_at in Category)
42
- qualified = qualified.sort_by{|c| c[:title] }.reverse
53
+ qualified = qualified.sort_by{|c| c[:title] }
43
54
  qualified.each do |category_value|
44
55
  add_child(Category.new(delegate, category_type, category_value))
45
56
  end
@@ -16,8 +16,8 @@ module TivoHMO
16
16
  super(delegate.key)
17
17
 
18
18
  self.title = delegate.title
19
- # self.modified_at = Time.at(delegate.updated_at.to_i)
20
- # self.created_at = Time.at(delegate.added_at.to_i)
19
+ self.modified_at = Time.at(delegate.updated_at.to_i) rescue nil
20
+ self.created_at = Time.at(delegate.added_at.to_i) rescue nil
21
21
  end
22
22
 
23
23
  def children
@@ -34,11 +34,11 @@ module TivoHMO
34
34
  add_child(QualifiedCategory.new(delegate, :by_first_character, :first_characters))
35
35
 
36
36
  #add_child(Category.new(delegate, :unwatched))
37
- add_child(Category.new(delegate, :on_deck))
38
- add_child(Category.new(delegate, :newest))
39
- add_child(Category.new(delegate, :recently_viewed))
40
- add_child(Category.new(delegate, :recently_added))
41
- add_child(Category.new(delegate, :all))
37
+ add_child(Category.new(delegate, :on_deck, nil, true))
38
+ add_child(Category.new(delegate, :newest, nil, true))
39
+ add_child(Category.new(delegate, :recently_viewed, nil, true))
40
+ add_child(Category.new(delegate, :recently_added, nil, true))
41
+ add_child(Category.new(delegate, :all, nil, true))
42
42
  end
43
43
  end
44
44
 
@@ -9,7 +9,11 @@ module TivoHMO
9
9
 
10
10
  def initialize(item)
11
11
  super(item)
12
- self.source_filename = CGI.unescape(item.delegate.medias.first.parts.first.file)
12
+ end
13
+
14
+ def transcode(writeable_io, format="video/x-tivo-mpeg")
15
+ # TODO: mark as watched in plex
16
+ super
13
17
  end
14
18
 
15
19
  end
@@ -1,6 +1,7 @@
1
1
  require 'plex-ruby'
2
2
  require_relative 'plex/movie'
3
3
  require_relative 'plex/episode'
4
+ require_relative 'plex/group'
4
5
  require_relative 'plex/season'
5
6
  require_relative 'plex/show'
6
7
  require_relative 'plex/category'
@@ -0,0 +1,37 @@
1
+ module TivoHMO
2
+ module Adapters
3
+ module Settings
4
+
5
+ # An Application for live modification of settings
6
+ class Application
7
+ include TivoHMO::API::Application
8
+ include GemLogger::LoggerSupport
9
+ include MonitorMixin
10
+
11
+ def initialize(identifier)
12
+ super("Settings")
13
+
14
+ self.metadata_class = TivoHMO::Adapters::Settings::Metadata
15
+ self.transcoder_class = TivoHMO::Adapters::Settings::Transcoder
16
+ self.title = self.identifier
17
+ self.presorted = true
18
+ end
19
+
20
+ def children
21
+ synchronize do
22
+ if super.blank?
23
+ Config.instance.known_config.keys.sort.each do |k|
24
+ add_child(KeyContainer.new(k))
25
+ end
26
+ add_child(ResetDefaultsItem.new)
27
+ end
28
+ end
29
+
30
+ super
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'listen'
3
+
4
+ module TivoHMO
5
+ module Adapters
6
+ module Settings
7
+
8
+ # An Item for displaying a string
9
+ class DisplayItem
10
+ include TivoHMO::API::Item
11
+ include GemLogger::LoggerSupport
12
+ include MonitorMixin
13
+
14
+ def initialize(name, description=nil)
15
+ super(name)
16
+ @description = description
17
+ end
18
+
19
+ def metadata
20
+ md = super
21
+
22
+ if @description.nil?
23
+ md.description = "Nothing to do here, hit back to return"
24
+ else
25
+ md.description = @description + ". Nothing to do here, hit back to return"
26
+ end
27
+
28
+ md
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end