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,69 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module TivoHMO
|
4
|
+
|
5
|
+
# Provides a mechanism for broadcasting the presence of a
|
6
|
+
# TivoHMO::Server to Tivo dvrs on the local network
|
7
|
+
class Beacon
|
8
|
+
include GemLogger::LoggerSupport
|
9
|
+
|
10
|
+
def initialize(service_port, limit: -1, interval: 60)
|
11
|
+
@interval = interval
|
12
|
+
@limit = limit
|
13
|
+
@uid = SecureRandom.uuid
|
14
|
+
@socket = UDPSocket.new(Socket::AF_INET)
|
15
|
+
@socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
16
|
+
@services = ['TiVoMediaServer:%s/http' % service_port]
|
17
|
+
@running = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
if ! @running
|
22
|
+
logger.info "Starting beacon(limit=#@limit, interval=#@interval) for #{@services.inspect}"
|
23
|
+
@running = true
|
24
|
+
@broadcast_thread = Thread.new do
|
25
|
+
while @running
|
26
|
+
broadcast
|
27
|
+
sleep(@interval)
|
28
|
+
@limit = @limit - 1
|
29
|
+
break if @limit == 0
|
30
|
+
end
|
31
|
+
@running = false
|
32
|
+
logger.info "Beacon thread exiting"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def stop
|
38
|
+
logger.info "Stopping beacon"
|
39
|
+
@running = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def join
|
43
|
+
@broadcast_thread.join
|
44
|
+
end
|
45
|
+
|
46
|
+
def beacon_data(method)
|
47
|
+
payload = {
|
48
|
+
tivoconnect: 1,
|
49
|
+
method: method,
|
50
|
+
identity: "{#{@uid}}",
|
51
|
+
machine: Socket.gethostname,
|
52
|
+
platform: 'pc/tivohmo',
|
53
|
+
services: @services.join(';'),
|
54
|
+
swversion: TivoHMO::VERSION
|
55
|
+
}
|
56
|
+
data = payload.collect {|k,v| "#{k}=#{v}" }.join("\n") << "\n"
|
57
|
+
data
|
58
|
+
end
|
59
|
+
|
60
|
+
def broadcast
|
61
|
+
bcast_ip = '<broadcast>' #'255.255.255.255'
|
62
|
+
bcast_port = 2190
|
63
|
+
packet = beacon_data('broadcast')
|
64
|
+
logger.debug "Sending beacon packet: #{packet}"
|
65
|
+
@socket.send(packet, 0, bcast_ip, bcast_port)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/tivohmo/cli.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'active_support/core_ext/string'
|
4
|
+
require 'sigdump/setup'
|
5
|
+
require 'tivohmo'
|
6
|
+
require 'open-uri'
|
7
|
+
|
8
|
+
module TivoHMO
|
9
|
+
|
10
|
+
# The command line interface to tivohmo
|
11
|
+
class CLI < Clamp::Command
|
12
|
+
include GemLogger::LoggerSupport
|
13
|
+
|
14
|
+
def self.description
|
15
|
+
desc = <<-DESC
|
16
|
+
Runs a HMO server. Specify one or more applications to show up as top level
|
17
|
+
shares in the TiVo Now Playing view. The application, identifier,
|
18
|
+
transcoder, metadata options can be given in groups to apply the transcoder
|
19
|
+
and metadata to each application - uses the application's default if not given
|
20
|
+
|
21
|
+
e.g.
|
22
|
+
|
23
|
+
tivohmo -a TivoHMO::Adapters::Filesystem::Application -i ~/Video/Movies \\
|
24
|
+
-a TivoHMO::Adapters::Filesystem::Application -i ~/Video/TV
|
25
|
+
|
26
|
+
to run two top level filesystem video serving apps for different dirs,
|
27
|
+
or
|
28
|
+
|
29
|
+
tivohmo -a TivoHMO::Adapters::Filesystem::Application -t Vids -i ~/Video
|
30
|
+
|
31
|
+
to run the single filesystem app with a custom title
|
32
|
+
DESC
|
33
|
+
desc.split("\n").collect(&:strip).join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
option ["-d", "--debug"],
|
37
|
+
:flag, "debug output\n",
|
38
|
+
default: false
|
39
|
+
|
40
|
+
option ["-r", "--preload"],
|
41
|
+
:flag, "Preloads all lazy container listings\n",
|
42
|
+
default: false
|
43
|
+
|
44
|
+
option ["-l", "--logfile"],
|
45
|
+
"FILE", "log to given file\n"
|
46
|
+
|
47
|
+
option ["-p", "--port"],
|
48
|
+
"PORT", "run server using PORT\n",
|
49
|
+
default: 9032 do |s|
|
50
|
+
Integer(s)
|
51
|
+
end
|
52
|
+
|
53
|
+
option ["-f", "--configuration"],
|
54
|
+
"FILE", "load configuration from given filename\n"
|
55
|
+
|
56
|
+
option ["-a", "--application"],
|
57
|
+
"CLASSNAME", "use the given application class\n",
|
58
|
+
multivalued: true
|
59
|
+
|
60
|
+
option ["-i", "--identifier"],
|
61
|
+
"IDENTIFIER", "use the given application identifier\n" +
|
62
|
+
"a string that has meaning to the application\n",
|
63
|
+
multivalued: true
|
64
|
+
|
65
|
+
option ["-t", "--title"],
|
66
|
+
"TITLE", "use the given title for the application\n",
|
67
|
+
multivalued: true
|
68
|
+
|
69
|
+
option ["-T", "--transcoder"],
|
70
|
+
"CLASSNAME", "override the application's transcoder class\n",
|
71
|
+
multivalued: true
|
72
|
+
|
73
|
+
option ["-M", "--metadata"],
|
74
|
+
"CLASSNAME", "override the application's metadata class\n",
|
75
|
+
multivalued: true
|
76
|
+
|
77
|
+
option ["-b", "--beacon"],
|
78
|
+
"LIMIT:INTERVAL", "configure beacon limit and/or interval\n"
|
79
|
+
|
80
|
+
def execute
|
81
|
+
setup_logging
|
82
|
+
|
83
|
+
if configuration
|
84
|
+
config = YAML.load_file(configuration)
|
85
|
+
|
86
|
+
# allow cli option to override config file
|
87
|
+
set_if_default(:port, config['port'].to_i)
|
88
|
+
end
|
89
|
+
|
90
|
+
signal_usage_error "at least one application is required" unless application_list.present?
|
91
|
+
signal_usage_error "an initializer is needed for each application" unless
|
92
|
+
application_list.size == identifier_list.size
|
93
|
+
|
94
|
+
(application_list + transcoder_list + metadata_list).each do |c|
|
95
|
+
if c && c.starts_with?('TivoHMO::Adapters::')
|
96
|
+
path = c.downcase.split('::')[0..-2].join('/')
|
97
|
+
require path
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
server = TivoHMO::API::Server.new
|
102
|
+
|
103
|
+
apps_with_config = application_list.zip(identifier_list,
|
104
|
+
title_list,
|
105
|
+
transcoder_list,
|
106
|
+
metadata_list)
|
107
|
+
|
108
|
+
apps_with_config.each do |app_classname, identifier, title, transcoder, metadata|
|
109
|
+
app_class = app_classname.constantize
|
110
|
+
app = app_class.new(identifier)
|
111
|
+
|
112
|
+
if title
|
113
|
+
app.title = title
|
114
|
+
else
|
115
|
+
app.title = "#{app.title} on #{server.title}"
|
116
|
+
end
|
117
|
+
|
118
|
+
app.transcoder_class = transcoder.constantize if transcoder
|
119
|
+
app.metadata_class = metadata.constantize if metadata
|
120
|
+
server.add_child(app)
|
121
|
+
end
|
122
|
+
|
123
|
+
preload_containers(server) if preload?
|
124
|
+
|
125
|
+
opts = {}
|
126
|
+
if beacon.present?
|
127
|
+
limit, interval = beacon.split(":")
|
128
|
+
opts[:limit] = limit.to_i if limit.present?
|
129
|
+
opts[:interval] = interval.to_i if interval.present?
|
130
|
+
end
|
131
|
+
notifier = TivoHMO::Beacon.new(port, **opts)
|
132
|
+
|
133
|
+
TivoHMO::Server.start(server, port) do |s|
|
134
|
+
wait_for_server { notifier.start }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def setup_logging
|
141
|
+
Logging.logger.root.level = :debug if debug?
|
142
|
+
|
143
|
+
if logfile.present?
|
144
|
+
Logging.logger.root.appenders = Logging.appenders.file(
|
145
|
+
logfile,
|
146
|
+
layout: Logging.layouts.pattern(
|
147
|
+
pattern: Logging.appenders.stdout.layout.pattern
|
148
|
+
)
|
149
|
+
)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_if_default(attr, new_value)
|
154
|
+
self.send("#{attr}=", new_value) if self.send(attr) == self.send("default_#{attr}")
|
155
|
+
end
|
156
|
+
|
157
|
+
def preload_containers(server)
|
158
|
+
logger.info "Preloading lazily cached containers"
|
159
|
+
queue = server.children.dup
|
160
|
+
queue.each do |i|
|
161
|
+
logger.debug("Loading children for #{i.title_path}")
|
162
|
+
queue.concat(i.children)
|
163
|
+
end
|
164
|
+
logger.info "Preload complete"
|
165
|
+
end
|
166
|
+
|
167
|
+
def wait_for_server
|
168
|
+
Thread.new do
|
169
|
+
while true
|
170
|
+
begin
|
171
|
+
open("http://localhost:#{port}/TiVoConnect?Command=QueryServer") {}
|
172
|
+
yield
|
173
|
+
break
|
174
|
+
rescue Exception => e
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'gem_logger'
|
2
|
+
require 'logging'
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
Logging.format_as :inspect
|
6
|
+
Logging.backtrace true
|
7
|
+
|
8
|
+
Logging.color_scheme(
|
9
|
+
'bright',
|
10
|
+
levels: {
|
11
|
+
info: :green,
|
12
|
+
warn: :yellow,
|
13
|
+
error: :red,
|
14
|
+
fatal: [:white, :on_red]
|
15
|
+
},
|
16
|
+
date: :blue,
|
17
|
+
logger: :cyan,
|
18
|
+
message: :magenta
|
19
|
+
)
|
20
|
+
|
21
|
+
Logging.appenders.stdout(
|
22
|
+
'stdout',
|
23
|
+
layout: Logging.layouts.pattern(
|
24
|
+
pattern: '[%d] %-5l %c{2} %m\n',
|
25
|
+
color_scheme: 'bright'
|
26
|
+
)
|
27
|
+
)
|
28
|
+
|
29
|
+
Logging.logger.root.appenders = Logging.appenders.stdout
|
30
|
+
Logging.logger.root.level = :info
|
31
|
+
|
32
|
+
module TivoHMO
|
33
|
+
module LoggingConcern
|
34
|
+
extend ActiveSupport::Concern
|
35
|
+
|
36
|
+
def logger
|
37
|
+
Logging.logger[self.class]
|
38
|
+
end
|
39
|
+
|
40
|
+
module ClassMethods
|
41
|
+
def logger
|
42
|
+
Logging.logger[self]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
GemLogger.default_logger = Logging.logger.root
|
50
|
+
GemLogger.logger_concern = TivoHMO::LoggingConcern
|
@@ -0,0 +1,19 @@
|
|
1
|
+
xml.Item do
|
2
|
+
|
3
|
+
xml.Details do
|
4
|
+
xml.Title container.title
|
5
|
+
xml.ContentType container.content_type
|
6
|
+
xml.SourceFormat container.source_format
|
7
|
+
xml.UniqueId format_uuid(container.uuid)
|
8
|
+
xml.TotalItems container.children.size
|
9
|
+
xml.LastCaptureDate format_date(container.created_at) if container.created_at
|
10
|
+
end
|
11
|
+
|
12
|
+
xml.Links do
|
13
|
+
xml.Content do
|
14
|
+
xml.Url container_url(container)
|
15
|
+
xml.ContentType "x-tivo-container/folder"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
xml.Item do
|
2
|
+
|
3
|
+
md = item.metadata
|
4
|
+
|
5
|
+
xml.Details do
|
6
|
+
xml.Title item.title
|
7
|
+
xml.ContentType item.content_type
|
8
|
+
xml.SourceFormat item.source_format
|
9
|
+
|
10
|
+
if md
|
11
|
+
xml.SourceSize md.source_size if md.source_size
|
12
|
+
xml.Duration md.duration.to_i if md.duration
|
13
|
+
xml.Description md.description if md.description
|
14
|
+
|
15
|
+
xml.SourceChannel md.channel[:major_number] if md.channel
|
16
|
+
xml.SourceStation md.channel[:callsign] if md.channel
|
17
|
+
|
18
|
+
xml.SeriesId md.series_id
|
19
|
+
xml.ShowingBits md.showing_bits if md.showing_bits
|
20
|
+
# xml.CopyProtected 'Yes' if md.valid?
|
21
|
+
|
22
|
+
xml.EpisodeTitle md.episode_title if md.is_episode && md.episode_title
|
23
|
+
xml.EpisodeNumber md.episode_number if md.episode_number
|
24
|
+
|
25
|
+
xml.ProgramId md.program_id if md.program_id
|
26
|
+
|
27
|
+
xml.TvRating md.tv_rating[:name] if md.tv_rating
|
28
|
+
xml.MpaaRating md.mpaa_rating[:name] if md.mpaa_rating
|
29
|
+
end
|
30
|
+
|
31
|
+
xml.CaptureDate format_date(item.created_at) if item.created_at
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
xml.Links do
|
36
|
+
xml.Content do
|
37
|
+
xml.ContentType item.content_type
|
38
|
+
xml.Url item_url(item)
|
39
|
+
end
|
40
|
+
|
41
|
+
xml.CustomIcon do
|
42
|
+
xml.ContentType "image/*"
|
43
|
+
xml.AcceptsParams "No"
|
44
|
+
xml.Url "urn:tivo:image:save-until-i-delete-recording"
|
45
|
+
end
|
46
|
+
|
47
|
+
if md
|
48
|
+
xml.TiVoVideoDetails do
|
49
|
+
xml.ContentType "text/xml"
|
50
|
+
xml.AcceptsParams "No"
|
51
|
+
xml.Url item_detail_url(item)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
xml.TiVoContainer do
|
2
|
+
|
3
|
+
paginated_children = children[item_start, item_count]
|
4
|
+
|
5
|
+
xml.ItemStart item_start
|
6
|
+
xml.ItemCount paginated_children.size
|
7
|
+
|
8
|
+
xml.Details do
|
9
|
+
xml.Title container.title_path
|
10
|
+
xml.ContentType "x-tivo-container/folder"
|
11
|
+
xml.SourceFormat "x-tivo-container/folder"
|
12
|
+
xml.TotalItems children.size
|
13
|
+
xml.UniqueId format_uuid(container.uuid)
|
14
|
+
end
|
15
|
+
|
16
|
+
paginated_children.each do |child|
|
17
|
+
if child.is_a?(TivoHMO::API::Container)
|
18
|
+
builder :_container, layout: false, locals: { xml: xml, container: child }
|
19
|
+
elsif child.is_a?(TivoHMO::API::Item)
|
20
|
+
builder :_item, layout: false, locals: { xml: xml, item: child }
|
21
|
+
else
|
22
|
+
raise "Invalid child, needs to be item or container"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
xml.TvBusMarshalledStruct :TvBusEnvelope,
|
2
|
+
'xmlns:xs' => "http://www.w3.org/2001/XMLSchema-instance",
|
3
|
+
'xmlns:TvBusMarshalledStruct' => "http://tivo.com/developer/xml/idl/TvBusMarshalledStruct",
|
4
|
+
'xmlns:TvPgdRecording' => "http://tivo.com/developer/xml/idl/TvPgdRecording",
|
5
|
+
'xmlns:TvBusDuration' => "http://tivo.com/developer/xml/idl/TvBusDuration",
|
6
|
+
'xmlns:TvPgdShowing' => "http://tivo.com/developer/xml/idl/TvPgdShowing",
|
7
|
+
'xmlns:TvDbShowingBit' => "http://tivo.com/developer/xml/idl/TvDbShowingBit",
|
8
|
+
'xmlns:TvBusDateTime' => "http://tivo.com/developer/xml/idl/TvBusDateTime",
|
9
|
+
'xmlns:TvPgdProgram' => "http://tivo.com/developer/xml/idl/TvPgdProgram",
|
10
|
+
'xmlns:TvDbColorCode' => "http://tivo.com/developer/xml/idl/TvDbColorCode",
|
11
|
+
'xmlns:TvPgdSeries' => "http://tivo.com/developer/xml/idl/TvPgdSeries",
|
12
|
+
'xmlns:TvDbShowType' => "http://tivo.com/developer/xml/idl/TvDbShowType",
|
13
|
+
'xmlns:TvPgdChannel' => "http://tivo.com/developer/xml/idl/TvPgdChannel",
|
14
|
+
'xmlns:TvDbTvRating' => "http://tivo.com/developer/xml/idl/TvDbTvRating",
|
15
|
+
'xmlns:TvDbRecordQuality' => "http://tivo.com/developer/xml/idl/TvDbRecordQuality",
|
16
|
+
'xmlns:TvDbBitstreamFormat' => "http://tivo.com/developer/xml/idl/TvDbBitstreamFormat",
|
17
|
+
'xs:schemaLocation' => "http://tivo.com/developer/xml/idl/TvBusMarshalledStruct TvBusMarshalledStruct.xsd http://tivo.com/developer/xml/idl/TvPgdRecording TvPgdRecording.xsd http://tivo.com/developer/xml/idl/TvBusDuration TvBusDuration.xsd http://tivo.com/developer/xml/idl/TvPgdShowing TvPgdShowing.xsd http://tivo.com/developer/xml/idl/TvDbShowingBit TvDbShowingBit.xsd http://tivo.com/developer/xml/idl/TvBusDateTime TvBusDateTime.xsd http://tivo.com/developer/xml/idl/TvPgdProgram TvPgdProgram.xsd http://tivo.com/developer/xml/idl/TvDbColorCode TvDbColorCode.xsd http://tivo.com/developer/xml/idl/TvPgdSeries TvPgdSeries.xsd http://tivo.com/developer/xml/idl/TvDbShowType TvDbShowType.xsd http://tivo.com/developer/xml/idl/TvPgdChannel TvPgdChannel.xsd http://tivo.com/developer/xml/idl/TvDbTvRating TvDbTvRating.xsd http://tivo.com/developer/xml/idl/TvDbRecordQuality TvDbRecordQuality.xsd http://tivo.com/developer/xml/idl/TvDbBitstreamFormat TvDbBitstreamFormat.xsd",
|
18
|
+
'xs:type' => "TvPgdRecording:TvPgdRecording" do
|
19
|
+
|
20
|
+
md = item.metadata
|
21
|
+
|
22
|
+
xml.recordedDuration format_iso_duration(md.duration)
|
23
|
+
xml.vActualShowing md.actual_showing
|
24
|
+
xml.vBookmark md.bookmark
|
25
|
+
xml.recordingQuality md.recording_quality[:name], :value => md.recording_quality[:value]
|
26
|
+
|
27
|
+
xml.showing do
|
28
|
+
|
29
|
+
xml.showingBits :value => md.showing_bits
|
30
|
+
xml.time format_iso_date(md.time)
|
31
|
+
xml.duration format_iso_duration(md.duration)
|
32
|
+
if md.part_count and md.part_index
|
33
|
+
xml.partCount md.part_count
|
34
|
+
xml.partIndex md.part_index
|
35
|
+
end
|
36
|
+
|
37
|
+
xml.program do
|
38
|
+
|
39
|
+
xml.vActor do
|
40
|
+
Array(md.actors).each do |actor|
|
41
|
+
xml.element actor
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
xml.vAdvisory md.advisory
|
46
|
+
|
47
|
+
xml.vChoreographer do
|
48
|
+
Array(md.choreographers).each do |choreographer|
|
49
|
+
xml.element choreographer
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
xml.colorCode md.color_code.try(:[], :name),
|
54
|
+
:value => md.color_code.try(:[], :value)
|
55
|
+
xml.description md.description
|
56
|
+
|
57
|
+
xml.vDirector do
|
58
|
+
Array(md.directors).each do |director|
|
59
|
+
xml.element director
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
xml.episodeNumber md.episode_number if md.episode_number
|
64
|
+
xml.episodeTitle md.episode_title if md.is_episode && md.episode_title
|
65
|
+
|
66
|
+
xml.vExecProducer do
|
67
|
+
Array(md.executive_producers).each do |executive_producer|
|
68
|
+
xml.element executive_producer
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
xml.vProgramGenre do
|
73
|
+
Array(md.program_genres).each do |program_genre|
|
74
|
+
xml.element program_genre
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
xml.vGuestStar do
|
79
|
+
Array(md.guest_stars).each do |guest_star|
|
80
|
+
xml.element guest_star
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
xml.vHost do
|
85
|
+
Array(md.hosts).each do |host|
|
86
|
+
xml.element host
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
xml.isEpisode md.is_episode
|
91
|
+
|
92
|
+
if md.movie_year
|
93
|
+
xml.movieYear md.movie_year
|
94
|
+
else
|
95
|
+
xml.originalAirDate format_iso_date(md.original_air_date || md.time)
|
96
|
+
end
|
97
|
+
|
98
|
+
if md.mpaa_rating
|
99
|
+
xml.mpaaRating md.mpaa_rating[:name], :value => md.mpaa_rating[:value]
|
100
|
+
end
|
101
|
+
|
102
|
+
xml.vProducer do
|
103
|
+
Array(md.producers).each do |producer|
|
104
|
+
xml.element producer
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
xml.series do
|
109
|
+
xml.isEpisodic md.is_episode
|
110
|
+
xml.vSeriesGenre do
|
111
|
+
Array(md.series_genres).each do |series_genre|
|
112
|
+
xml.element series_genre
|
113
|
+
end
|
114
|
+
end
|
115
|
+
xml.seriesTitle md.series_title
|
116
|
+
xml.uniqueId md.series_id if md.series_id
|
117
|
+
end
|
118
|
+
|
119
|
+
xml.showType md.show_type.try(:[], :name),
|
120
|
+
:value => md.show_type.try(:[], :value)
|
121
|
+
|
122
|
+
if md.star_rating
|
123
|
+
xml.starRating md.star_rating[:name], :value => md.star_rating[:value]
|
124
|
+
end
|
125
|
+
|
126
|
+
xml.title md.series_title ? md.series_title : (md.title || item.title)
|
127
|
+
|
128
|
+
xml.vWriter do
|
129
|
+
Array(md.writers).each do |writer|
|
130
|
+
xml.element writer
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
xml.uniqueId md.program_id if md.program_id
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
xml.channel do
|
139
|
+
xml.displayMajorNumber md.channel.try(:[], :major_number)
|
140
|
+
xml.displayMinorNumber md.channel.try(:[], :minor_number)
|
141
|
+
xml.callsign md.channel.try(:[], :callsign)
|
142
|
+
end
|
143
|
+
|
144
|
+
if md.tv_rating
|
145
|
+
xml.tvRating md.tv_rating[:name], :value => md.tv_rating[:value]
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
xml.startTime format_iso_date(md.start_time)
|
151
|
+
xml.stopTime format_iso_date(md.stop_time)
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
xml.TiVoContainer do
|
2
|
+
|
3
|
+
xml.Details do
|
4
|
+
xml.Title container.title
|
5
|
+
xml.ContentType container.content_type
|
6
|
+
xml.SourceFormat container.source_format
|
7
|
+
xml.TotalItems children.size
|
8
|
+
end
|
9
|
+
|
10
|
+
paginated_children = children[item_start, item_count]
|
11
|
+
paginated_children.each do |child|
|
12
|
+
builder :_container, layout: false, locals: { xml: xml, container: child }
|
13
|
+
end
|
14
|
+
|
15
|
+
xml.ItemStart item_start
|
16
|
+
xml.ItemCount paginated_children.size
|
17
|
+
|
18
|
+
end
|