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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG +30 -0
- data/Gemfile +2 -0
- data/README.md +4 -6
- data/TODO +1 -0
- data/contrib/tivohmo.conf +2 -2
- data/contrib/tivohmo.plist +1 -1
- data/contrib/tivohmo.yml +60 -0
- data/lib/tivohmo/adapters/filesystem/file_item.rb +2 -3
- data/lib/tivohmo/adapters/plex/category.rb +87 -6
- data/lib/tivohmo/adapters/plex/episode.rb +30 -11
- data/lib/tivohmo/adapters/plex/group.rb +20 -0
- data/lib/tivohmo/adapters/plex/movie.rb +15 -3
- data/lib/tivohmo/adapters/plex/qualified_category.rb +14 -3
- data/lib/tivohmo/adapters/plex/season.rb +2 -2
- data/lib/tivohmo/adapters/plex/section.rb +5 -5
- data/lib/tivohmo/adapters/plex/transcoder.rb +5 -1
- data/lib/tivohmo/adapters/plex.rb +1 -0
- data/lib/tivohmo/adapters/settings/application.rb +37 -0
- data/lib/tivohmo/adapters/settings/display_item.rb +35 -0
- data/lib/tivohmo/adapters/settings/key_container.rb +35 -0
- data/lib/tivohmo/adapters/settings/metadata.rb +26 -0
- data/lib/tivohmo/adapters/settings/reset_defaults_item.rb +39 -0
- data/lib/tivohmo/adapters/settings/set_value_item.rb +38 -0
- data/lib/tivohmo/adapters/settings/transcoder.rb +23 -0
- data/lib/tivohmo/adapters/settings.rb +7 -0
- data/lib/tivohmo/adapters/streamio/transcoder.rb +39 -1
- data/lib/tivohmo/api/container.rb +5 -1
- data/lib/tivohmo/api/item.rb +2 -0
- data/lib/tivohmo/api/subtitle.rb +13 -0
- data/lib/tivohmo/api/transcoder.rb +1 -3
- data/lib/tivohmo/api.rb +1 -0
- data/lib/tivohmo/beacon.rb +3 -3
- data/lib/tivohmo/cli.rb +175 -48
- data/lib/tivohmo/config.rb +157 -0
- data/lib/tivohmo/server/views/_container.builder +1 -1
- data/lib/tivohmo/server.rb +6 -3
- data/lib/tivohmo/version.rb +1 -1
- data/lib/tivohmo.rb +1 -0
- data/spec/adapters/filesystem/file_item_spec.rb +1 -1
- data/spec/adapters/plex/application_spec.rb +10 -1
- data/spec/adapters/plex/category_spec.rb +93 -20
- data/spec/adapters/plex/episode_spec.rb +34 -15
- data/spec/adapters/plex/metadata_spec.rb +5 -7
- data/spec/adapters/plex/movie_spec.rb +10 -7
- data/spec/adapters/plex/qualified_category_spec.rb +17 -17
- data/spec/adapters/plex/season_spec.rb +23 -4
- data/spec/adapters/plex/section_spec.rb +4 -4
- data/spec/adapters/plex/show_spec.rb +19 -4
- data/spec/adapters/plex/transcoder_spec.rb +2 -8
- data/spec/adapters/settings/application_spec.rb +25 -0
- data/spec/adapters/settings/display_item_spec.rb +29 -0
- data/spec/adapters/settings/key_container_spec.rb +33 -0
- data/spec/adapters/settings/metadata_spec.rb +33 -0
- data/spec/adapters/settings/reset_defaults_item_spec.rb +54 -0
- data/spec/adapters/settings/set_value_item_spec.rb +54 -0
- data/spec/api/container_spec.rb +12 -0
- data/spec/api/item_spec.rb +2 -0
- data/spec/api/transcoder_spec.rb +0 -1
- data/spec/beacon_spec.rb +3 -1
- data/spec/cli_spec.rb +57 -19
- data/spec/config_spec.rb +224 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Application/_children/should_get_children.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_allow_disabling_subtitles.yml +424 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_display_non-zero_child_count_once_children_fetched.yml +653 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children.yml +599 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_embedded_subtitles.yml +595 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_subtitles.yml +482 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_memoize.yml +478 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_refresh_children_when_config_changes.yml +1245 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_use_category_value_for_children.yml +395 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_instantiate.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_set_presorted_if_present.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_use_category_value_for_title_if_present.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_initialize/should_instantiate.yml +444 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_allow_disabling_series_id_in_metadata.yml +444 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_populate_metadata.yml +579 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Metadata/_initialize/should_instantiate.yml +296 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_initialize/should_instantiate.yml +296 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_metadata/should_populate_metadata.yml +296 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_display_non-zero_child_count_once_children_fetched.yml +220 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_have_children.yml +393 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_memoize.yml +219 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_initialize/should_instantiate.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_have_children.yml +444 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_memoize.yml +444 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_initialize/should_instantiate.yml +250 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_have_category_children.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_memoize.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_initialize/should_instantiate.yml +61 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_have_children.yml +250 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_memoize.yml +250 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_initialize/should_instantiate.yml +166 -0
- data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Transcoder/_initialize/should_instantiate.yml +296 -0
- data/spec/server_spec.rb +31 -2
- data/spec/spec_helper.rb +24 -36
- data/tivohmo.gemspec +2 -2
- metadata +109 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf38e58ce80df9b5a177019960ace75cee1214cd
|
|
4
|
+
data.tar.gz: 84358dcbc8cc552c560a34b67cd3bec1f40d7d62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1643360b023aa69207324541bfd041b25ce3d07e7211aa57bc7a3b33ab6b497c6063a560ba26ebe0f577e18a4fc8361838de7778b5758b5250b06f59996e62be
|
|
7
|
+
data.tar.gz: 807c936f1152bb55a0a62ddcb6c933aaa0c03d19c44cf407fde517f283ca16c8e3ccb6fb2d84744090f3cb90885077fbde392fc831ac75540d59ab85af34fee0
|
data/.travis.yml
CHANGED
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
data/README.md
CHANGED
|
@@ -21,14 +21,11 @@ Install the gem:
|
|
|
21
21
|
|
|
22
22
|
$ gem install tivohmo
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
then install as a daemon with default configuration:
|
|
25
25
|
|
|
26
|
-
|
|
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/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
|
|
12
|
+
setuid root
|
|
13
13
|
umask 022
|
|
14
14
|
|
|
15
|
-
exec
|
|
15
|
+
exec bash -l -c "tivohmo --configuration /etc/tivohmo.yml"
|
data/contrib/tivohmo.plist
CHANGED
|
@@ -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
|
|
19
|
+
<string>tivohmo --configuration <%= File.expand_path(config[:configuration]) %></string>
|
|
20
20
|
</array>
|
|
21
21
|
<key>RunAtLoad</key>
|
|
22
22
|
<true/>
|
data/contrib/tivohmo.yml
ADDED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
71
|
+
add_grouped(Movie, media)
|
|
54
72
|
elsif media.is_a?(::Plex::Episode)
|
|
55
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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] }
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|