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