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,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
|