tivohmo 0.2.3 → 0.3.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 (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