tivohmo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.travis.yml +10 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +514 -0
- data/README.md +50 -0
- data/Rakefile +7 -0
- data/bin/tivohmo +8 -0
- data/contrib/tivohmo.conf +17 -0
- data/contrib/tivohmo.plist +22 -0
- data/lib/tivohmo/adapters/filesystem/application.rb +23 -0
- data/lib/tivohmo/adapters/filesystem/file_item.rb +29 -0
- data/lib/tivohmo/adapters/filesystem/folder_container.rb +105 -0
- data/lib/tivohmo/adapters/filesystem.rb +3 -0
- data/lib/tivohmo/adapters/plex/application.rb +41 -0
- data/lib/tivohmo/adapters/plex/category.rb +72 -0
- data/lib/tivohmo/adapters/plex/episode.rb +26 -0
- data/lib/tivohmo/adapters/plex/metadata.rb +24 -0
- data/lib/tivohmo/adapters/plex/movie.rb +26 -0
- data/lib/tivohmo/adapters/plex/qualified_category.rb +51 -0
- data/lib/tivohmo/adapters/plex/season.rb +39 -0
- data/lib/tivohmo/adapters/plex/section.rb +48 -0
- data/lib/tivohmo/adapters/plex/show.rb +39 -0
- data/lib/tivohmo/adapters/plex/transcoder.rb +19 -0
- data/lib/tivohmo/adapters/plex.rb +11 -0
- data/lib/tivohmo/adapters/streamio/metadata.rb +26 -0
- data/lib/tivohmo/adapters/streamio/transcoder.rb +239 -0
- data/lib/tivohmo/adapters/streamio.rb +17 -0
- data/lib/tivohmo/api/application.rb +35 -0
- data/lib/tivohmo/api/container.rb +31 -0
- data/lib/tivohmo/api/item.rb +32 -0
- data/lib/tivohmo/api/metadata.rb +98 -0
- data/lib/tivohmo/api/node.rb +115 -0
- data/lib/tivohmo/api/server.rb +24 -0
- data/lib/tivohmo/api/transcoder.rb +39 -0
- data/lib/tivohmo/api.rb +7 -0
- data/lib/tivohmo/beacon.rb +69 -0
- data/lib/tivohmo/cli.rb +182 -0
- data/lib/tivohmo/logging.rb +50 -0
- data/lib/tivohmo/server/views/_container.builder +19 -0
- data/lib/tivohmo/server/views/_item.builder +57 -0
- data/lib/tivohmo/server/views/container.builder +26 -0
- data/lib/tivohmo/server/views/item_details.builder +153 -0
- data/lib/tivohmo/server/views/layout.builder +2 -0
- data/lib/tivohmo/server/views/layout.erb +10 -0
- data/lib/tivohmo/server/views/server.builder +18 -0
- data/lib/tivohmo/server/views/server_info.builder +7 -0
- data/lib/tivohmo/server/views/unsupported.erb +7 -0
- data/lib/tivohmo/server/views/video_formats.builder +10 -0
- data/lib/tivohmo/server.rb +306 -0
- data/lib/tivohmo/version.rb +3 -0
- data/lib/tivohmo.rb +5 -0
- data/spec/adapters/filesystem/application_spec.rb +19 -0
- data/spec/adapters/filesystem/file_item_spec.rb +33 -0
- data/spec/adapters/filesystem/folder_container_spec.rb +115 -0
- data/spec/adapters/plex/application_spec.rb +20 -0
- data/spec/adapters/plex/category_spec.rb +66 -0
- data/spec/adapters/plex/episode_spec.rb +22 -0
- data/spec/adapters/plex/metadata_spec.rb +24 -0
- data/spec/adapters/plex/movie_spec.rb +22 -0
- data/spec/adapters/plex/qualified_category_spec.rb +51 -0
- data/spec/adapters/plex/season_spec.rb +22 -0
- data/spec/adapters/plex/section_spec.rb +38 -0
- data/spec/adapters/plex/show_spec.rb +22 -0
- data/spec/adapters/plex/transcoder_spec.rb +27 -0
- data/spec/adapters/streamio/metadata_spec.rb +34 -0
- data/spec/adapters/streamio/transcoder_spec.rb +42 -0
- data/spec/api/application_spec.rb +63 -0
- data/spec/api/container_spec.rb +34 -0
- data/spec/api/item_spec.rb +53 -0
- data/spec/api/metadata_spec.rb +59 -0
- data/spec/api/node_spec.rb +178 -0
- data/spec/api/server_spec.rb +24 -0
- data/spec/api/transcoder_spec.rb +25 -0
- data/spec/beacon_spec.rb +87 -0
- data/spec/cli_spec.rb +227 -0
- data/spec/server_spec.rb +458 -0
- data/spec/spec_helper.rb +123 -0
- data/tivohmo.gemspec +46 -0
- metadata +416 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'tivohmo/adapters/streamio'
|
3
|
+
|
4
|
+
module TivoHMO
|
5
|
+
module Adapters
|
6
|
+
module Filesystem
|
7
|
+
|
8
|
+
# An Application based on a filesystem
|
9
|
+
class Application < FolderContainer
|
10
|
+
include TivoHMO::API::Application
|
11
|
+
include GemLogger::LoggerSupport
|
12
|
+
include MonitorMixin
|
13
|
+
|
14
|
+
def initialize(identifier)
|
15
|
+
super(identifier)
|
16
|
+
self.metadata_class = TivoHMO::Adapters::StreamIO::Metadata
|
17
|
+
self.transcoder_class = TivoHMO::Adapters::StreamIO::Transcoder
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#require 'streamio-ffmpeg'
|
2
|
+
|
3
|
+
module TivoHMO
|
4
|
+
module Adapters
|
5
|
+
module Filesystem
|
6
|
+
|
7
|
+
# An Item based on a filesystem file
|
8
|
+
class FileItem
|
9
|
+
include TivoHMO::API::Item
|
10
|
+
include GemLogger::LoggerSupport
|
11
|
+
|
12
|
+
attr_reader :full_path
|
13
|
+
|
14
|
+
def initialize(identifier)
|
15
|
+
@full_path = File.expand_path(identifier)
|
16
|
+
raise ArgumentError, "Must provide an existing file" unless File.file?(full_path)
|
17
|
+
|
18
|
+
super(full_path)
|
19
|
+
|
20
|
+
self.title = File.basename(self.identifier)
|
21
|
+
self.modified_at = File.mtime(self.identifier)
|
22
|
+
self.created_at = File.ctime(self.identifier)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'listen'
|
3
|
+
|
4
|
+
module TivoHMO
|
5
|
+
module Adapters
|
6
|
+
module Filesystem
|
7
|
+
|
8
|
+
# A Container based on a filesystem folder
|
9
|
+
class FolderContainer
|
10
|
+
include TivoHMO::API::Container
|
11
|
+
include GemLogger::LoggerSupport
|
12
|
+
include MonitorMixin
|
13
|
+
|
14
|
+
VIDEO_EXTENSIONS = %w[
|
15
|
+
tivo mpg avi wmv mov flv f4v vob mp4 m4v mkv
|
16
|
+
ts tp trp 3g2 3gp 3gp2 3gpp amv asf avs bik bix box bsf
|
17
|
+
dat dif divx dmb dpg dv dvr-ms evo eye flc fli flx gvi ivf
|
18
|
+
m1v m21 m2t m2ts m2v m2p m4e mjp mjpeg mod moov movie mp21
|
19
|
+
mpe mpeg mpv mpv2 mqv mts mvb nsv nuv nut ogm qt rm rmvb
|
20
|
+
rts scm smv ssm svi vdo vfw vid viv vivo vp6 vp7 vro webm
|
21
|
+
wm wmd wtv yuv
|
22
|
+
]
|
23
|
+
|
24
|
+
attr_reader :full_path
|
25
|
+
|
26
|
+
attr_accessor :allowed_item_types,
|
27
|
+
:allowed_item_extensions
|
28
|
+
|
29
|
+
def initialize(identifier)
|
30
|
+
@full_path = File.expand_path(identifier)
|
31
|
+
raise ArgumentError,
|
32
|
+
"Must provide an existing directory: #{full_path}" unless File.directory?(full_path)
|
33
|
+
|
34
|
+
super(full_path)
|
35
|
+
|
36
|
+
self.allowed_item_types = %i[file dir]
|
37
|
+
self.allowed_item_extensions = VIDEO_EXTENSIONS
|
38
|
+
|
39
|
+
self.title = File.basename(self.identifier).titleize
|
40
|
+
self.modified_at = File.mtime(self.identifier)
|
41
|
+
self.created_at = File.ctime(self.identifier)
|
42
|
+
|
43
|
+
setup_change_listener
|
44
|
+
end
|
45
|
+
|
46
|
+
def children
|
47
|
+
synchronize do
|
48
|
+
if super.blank?
|
49
|
+
folders = []
|
50
|
+
files = []
|
51
|
+
|
52
|
+
Dir["#{self.full_path}/*"].each do |path|
|
53
|
+
if allowed_container?(path)
|
54
|
+
folders << FolderContainer.new(path)
|
55
|
+
elsif allowed_item?(path)
|
56
|
+
files << FileItem.new(path)
|
57
|
+
else
|
58
|
+
logger.debug "Ignoring: #{path}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
(folders + files).each {|c| add_child(c) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def setup_change_listener
|
72
|
+
logger.debug "Setting up change listener on #{identifier}"
|
73
|
+
@listener = Listen.to(identifier, ignore: /\//) do |modified, added, removed|
|
74
|
+
logger.debug "Detected filesystem change on #{identifier}"
|
75
|
+
logger.debug "modified: #{modified}"
|
76
|
+
logger.debug "added: #{added}"
|
77
|
+
logger.debug "removed: #{removed}"
|
78
|
+
|
79
|
+
# TODO: be more intelligent instead of just wiping children to cause the refresh
|
80
|
+
self.refresh
|
81
|
+
|
82
|
+
# cleanup - not strictly correct as this listener won't necessarily get triggered
|
83
|
+
# if self is removed from the parent
|
84
|
+
@listener.stop unless root.find(title_path)
|
85
|
+
logger.debug "Completed filesystem refresh on #{identifier}"
|
86
|
+
end
|
87
|
+
@listener.start
|
88
|
+
end
|
89
|
+
|
90
|
+
def allowed_container?(path)
|
91
|
+
File.directory?(path) && allowed_item_types.include?(:dir)
|
92
|
+
end
|
93
|
+
|
94
|
+
def allowed_item?(path)
|
95
|
+
ext = File.extname(path).gsub(/^./, '')
|
96
|
+
File.file?(path) &&
|
97
|
+
allowed_item_types.include?(:file) &&
|
98
|
+
allowed_item_extensions.include?(ext)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Application
|
6
|
+
include TivoHMO::API::Application
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :server
|
11
|
+
|
12
|
+
def initialize(identifier)
|
13
|
+
host, port = identifier.to_s.split(':')
|
14
|
+
host ||= 'localhost'
|
15
|
+
port ||= 32400
|
16
|
+
super("Plex[#{host}:#{port}]")
|
17
|
+
|
18
|
+
self.metadata_class = TivoHMO::Adapters::Plex::Metadata
|
19
|
+
self.transcoder_class = TivoHMO::Adapters::Plex::Transcoder
|
20
|
+
self.title = self.identifier
|
21
|
+
|
22
|
+
@server = ::Plex::Server.new(host, port)
|
23
|
+
end
|
24
|
+
|
25
|
+
def children
|
26
|
+
synchronize do
|
27
|
+
if super.blank?
|
28
|
+
Array(server.library.sections).each do |section|
|
29
|
+
add_child(Section.new(section))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Category
|
6
|
+
include TivoHMO::API::Container
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :delegate
|
11
|
+
attr_accessor :category_type, :category_value
|
12
|
+
|
13
|
+
def initialize(delegate, category_type, category_value=nil)
|
14
|
+
# delegate is a Plex::Section
|
15
|
+
@delegate = delegate
|
16
|
+
|
17
|
+
super(delegate.key)
|
18
|
+
|
19
|
+
self.category_type = category_type
|
20
|
+
self.category_value = category_value
|
21
|
+
|
22
|
+
if category_value
|
23
|
+
self.title = category_value[:title]
|
24
|
+
else
|
25
|
+
self.title = category_type.to_s.titleize
|
26
|
+
end
|
27
|
+
|
28
|
+
self.modified_at = Time.at(delegate.updated_at.to_i)
|
29
|
+
self.created_at = Time.at(delegate.updated_at.to_i)
|
30
|
+
end
|
31
|
+
|
32
|
+
def children
|
33
|
+
synchronize do
|
34
|
+
|
35
|
+
delegate.refresh
|
36
|
+
new_modified_at = delegate.updated_at.to_i
|
37
|
+
if new_modified_at > modified_at.to_i
|
38
|
+
logger.info "Plex section was updated, refreshing"
|
39
|
+
self.modified_at = Time.at(new_modified_at)
|
40
|
+
super.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
if super.blank?
|
44
|
+
|
45
|
+
if category_value
|
46
|
+
listing = delegate.send(category_type, category_value[:key])
|
47
|
+
else
|
48
|
+
listing = delegate.send(category_type)
|
49
|
+
end
|
50
|
+
|
51
|
+
Array(listing).each do |media|
|
52
|
+
if media.is_a?(::Plex::Movie)
|
53
|
+
add_child(Movie.new(media))
|
54
|
+
elsif media.is_a?(::Plex::Episode)
|
55
|
+
add_child(Episode.new(media))
|
56
|
+
elsif media.is_a?(::Plex::Show)
|
57
|
+
add_child(Show.new(media))
|
58
|
+
else
|
59
|
+
logger.error "Unknown type for #{media.class} in #{self.title}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Episode
|
6
|
+
include TivoHMO::API::Item
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
|
9
|
+
attr_reader :delegate
|
10
|
+
|
11
|
+
def initialize(delegate)
|
12
|
+
# delegate is a Plex::Episode
|
13
|
+
@delegate = delegate
|
14
|
+
|
15
|
+
super(delegate.key)
|
16
|
+
|
17
|
+
self.title = delegate.title
|
18
|
+
self.modified_at = Time.at(delegate.updated_at.to_i)
|
19
|
+
self.created_at = Time.at(delegate.added_at.to_i)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Metadata
|
6
|
+
include TivoHMO::API::Metadata
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
|
9
|
+
def initialize(item)
|
10
|
+
super
|
11
|
+
|
12
|
+
begin
|
13
|
+
self.description = item.delegate.summary
|
14
|
+
self.duration = (item.delegate.duration.to_i / 1000).to_i
|
15
|
+
rescue => e
|
16
|
+
logger.error "Failed to read plex metadata: #{e}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Movie
|
6
|
+
include TivoHMO::API::Item
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
|
9
|
+
attr_reader :delegate
|
10
|
+
|
11
|
+
def initialize(delegate)
|
12
|
+
# delegate is a Plex::Movie
|
13
|
+
@delegate = delegate
|
14
|
+
|
15
|
+
super(delegate.key)
|
16
|
+
|
17
|
+
self.title = delegate.title
|
18
|
+
self.modified_at = Time.at(delegate.updated_at.to_i)
|
19
|
+
self.created_at = Time.at(delegate.added_at.to_i)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class QualifiedCategory
|
6
|
+
include TivoHMO::API::Container
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :delegate
|
11
|
+
attr_accessor :category_type, :category_qualifier
|
12
|
+
|
13
|
+
def initialize(delegate, category_type, category_qualifier)
|
14
|
+
# delegate is a Plex::Section
|
15
|
+
@delegate = delegate
|
16
|
+
|
17
|
+
super(delegate.key)
|
18
|
+
|
19
|
+
self.category_type = category_type
|
20
|
+
self.category_qualifier = category_qualifier
|
21
|
+
self.title = category_type.to_s.titleize
|
22
|
+
self.modified_at = Time.at(delegate.updated_at.to_i)
|
23
|
+
self.created_at = Time.at(delegate.updated_at.to_i)
|
24
|
+
end
|
25
|
+
|
26
|
+
def children
|
27
|
+
synchronize do
|
28
|
+
|
29
|
+
delegate.refresh
|
30
|
+
new_modified_at = delegate.updated_at.to_i
|
31
|
+
if new_modified_at > modified_at.to_i
|
32
|
+
logger.info "Plex section was updated, refreshing"
|
33
|
+
self.modified_at = Time.at(new_modified_at)
|
34
|
+
super.clear
|
35
|
+
end
|
36
|
+
|
37
|
+
if super.blank?
|
38
|
+
Array(delegate.send(category_qualifier)).each do |category_value|
|
39
|
+
add_child(Category.new(delegate, category_type, category_value))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Season
|
6
|
+
include TivoHMO::API::Container
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :delegate
|
11
|
+
|
12
|
+
def initialize(delegate)
|
13
|
+
# delegate is a Plex::Season
|
14
|
+
@delegate = delegate
|
15
|
+
|
16
|
+
super(delegate.key)
|
17
|
+
|
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)
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
synchronize do
|
25
|
+
if super.blank?
|
26
|
+
Array(delegate.episodes).each do |media|
|
27
|
+
add_child(Episode.new(media))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Section
|
6
|
+
include TivoHMO::API::Container
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :delegate
|
11
|
+
|
12
|
+
def initialize(delegate)
|
13
|
+
# delegate is a Plex::Section
|
14
|
+
@delegate = delegate
|
15
|
+
|
16
|
+
super(delegate.key)
|
17
|
+
|
18
|
+
self.title = delegate.title
|
19
|
+
self.modified_at = Time.at(delegate.updated_at.to_i)
|
20
|
+
self.created_at = Time.at(delegate.updated_at.to_i)
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
synchronize do
|
25
|
+
if super.blank?
|
26
|
+
add_child(Category.new(delegate, :recently_added))
|
27
|
+
#add_child(Category.new(delegate, :unwatched))
|
28
|
+
add_child(Category.new(delegate, :newest))
|
29
|
+
add_child(Category.new(delegate, :on_deck))
|
30
|
+
add_child(Category.new(delegate, :recently_viewed))
|
31
|
+
add_child(Category.new(delegate, :all))
|
32
|
+
add_child(QualifiedCategory.new(delegate, :by_genre, :genres))
|
33
|
+
add_child(QualifiedCategory.new(delegate, :by_year, :years))
|
34
|
+
add_child(QualifiedCategory.new(delegate, :by_first_character, :first_characters))
|
35
|
+
add_child(QualifiedCategory.new(delegate, :by_collection, :collections))
|
36
|
+
add_child(QualifiedCategory.new(delegate, :by_folder, :folders))
|
37
|
+
add_child(QualifiedCategory.new(delegate, :by_content_rating, :content_ratings))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module Plex
|
4
|
+
|
5
|
+
class Show
|
6
|
+
include TivoHMO::API::Container
|
7
|
+
include GemLogger::LoggerSupport
|
8
|
+
include MonitorMixin
|
9
|
+
|
10
|
+
attr_reader :delegate
|
11
|
+
|
12
|
+
def initialize(delegate)
|
13
|
+
# delegate is a Plex::Show
|
14
|
+
@delegate = delegate
|
15
|
+
|
16
|
+
super(delegate.key)
|
17
|
+
|
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)
|
21
|
+
end
|
22
|
+
|
23
|
+
def children
|
24
|
+
synchronize do
|
25
|
+
if super.blank?
|
26
|
+
Array(delegate.seasons).each do |media|
|
27
|
+
add_child(Season.new(media))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'tivohmo/adapters/streamio'
|
2
|
+
|
3
|
+
module TivoHMO
|
4
|
+
module Adapters
|
5
|
+
module Plex
|
6
|
+
|
7
|
+
class Transcoder < TivoHMO::Adapters::StreamIO::Transcoder
|
8
|
+
include GemLogger::LoggerSupport
|
9
|
+
|
10
|
+
def initialize(item)
|
11
|
+
super(item)
|
12
|
+
self.source_filename = item.delegate.medias.first.parts.first.file
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'plex-ruby'
|
2
|
+
require_relative 'plex/movie'
|
3
|
+
require_relative 'plex/episode'
|
4
|
+
require_relative 'plex/season'
|
5
|
+
require_relative 'plex/show'
|
6
|
+
require_relative 'plex/category'
|
7
|
+
require_relative 'plex/qualified_category'
|
8
|
+
require_relative 'plex/section'
|
9
|
+
require_relative 'plex/application'
|
10
|
+
require_relative 'plex/metadata'
|
11
|
+
require_relative 'plex/transcoder'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TivoHMO
|
2
|
+
module Adapters
|
3
|
+
module StreamIO
|
4
|
+
|
5
|
+
# Extracts some basic metadata using the streamio gem
|
6
|
+
class Metadata
|
7
|
+
include TivoHMO::API::Metadata
|
8
|
+
include GemLogger::LoggerSupport
|
9
|
+
|
10
|
+
attr_accessor :movie
|
11
|
+
|
12
|
+
def initialize(item)
|
13
|
+
super(item)
|
14
|
+
begin
|
15
|
+
self.movie = FFMPEG::Movie.new(item.identifier)
|
16
|
+
self.duration = movie.duration.to_i
|
17
|
+
rescue => e
|
18
|
+
logger.error "Failed to read movie metadata: #{e}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|