tivohmo 0.2.1 → 0.2.2
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/lib/tivohmo/adapters/plex/application.rb +6 -1
- data/lib/tivohmo/adapters/plex/category.rb +1 -1
- data/lib/tivohmo/adapters/plex/episode.rb +6 -1
- data/lib/tivohmo/adapters/plex/movie.rb +2 -1
- data/lib/tivohmo/adapters/plex/qualified_category.rb +7 -2
- data/lib/tivohmo/adapters/plex/section.rb +13 -9
- data/lib/tivohmo/beacon.rb +6 -2
- data/lib/tivohmo/server.rb +18 -1
- data/lib/tivohmo/version.rb +1 -1
- data/spec/adapters/plex/category_spec.rb +18 -14
- data/spec/adapters/plex/episode_spec.rb +1 -1
- data/spec/adapters/plex/movie_spec.rb +1 -1
- data/spec/adapters/plex/qualified_category_spec.rb +15 -10
- data/spec/adapters/plex/section_spec.rb +11 -8
- data/spec/beacon_spec.rb +1 -1
- data/spec/server_spec.rb +27 -0
- data/spec/spec_helper.rb +2 -0
- data/tivohmo.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d174442bc2cdc54f0c321af10235ca9b7b0cd5e6
|
4
|
+
data.tar.gz: 9879d1b6792705f0838f2f9a12c7fd00f4b1bc1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82fb8a7e0deb9d7d62be6b8809264dabaf1c23d776d53fa7ea90a0094a99ad354e97ef676663c863b6620ec5a13820b88a8449af865e036f0d7a0e430e4fcd8f
|
7
|
+
data.tar.gz: fdcae9f54909c5122e99fe76528f3690f58f67cbc0853c535b51100faa3a4ff5eb6caf2c3688a84c968ee581e44cc14899d206252351a3764689ff5b88b7c0f7
|
@@ -25,7 +25,12 @@ module TivoHMO
|
|
25
25
|
def children
|
26
26
|
synchronize do
|
27
27
|
if super.blank?
|
28
|
-
Array(server.library.sections)
|
28
|
+
sections = Array(server.library.sections)
|
29
|
+
# Sort by title descending so that creation times are
|
30
|
+
# correct for tivo sort of newest first (Time.now for
|
31
|
+
# created_at in Section)
|
32
|
+
sections = sections.sort_by(&:title).reverse
|
33
|
+
sections.each do |section|
|
29
34
|
add_child(Section.new(section))
|
30
35
|
end
|
31
36
|
end
|
@@ -16,7 +16,8 @@ module TivoHMO
|
|
16
16
|
|
17
17
|
self.title = delegate.title
|
18
18
|
self.modified_at = Time.at(delegate.updated_at.to_i)
|
19
|
-
self.created_at = Time.
|
19
|
+
self.created_at = Time.parse(delegate.originally_available_at) rescue nil
|
20
|
+
self.created_at ||= Time.at(delegate.added_at.to_i)
|
20
21
|
end
|
21
22
|
|
22
23
|
def metadata
|
@@ -41,6 +42,10 @@ module TivoHMO
|
|
41
42
|
# group tv shows under same name if we can extract a seriesId
|
42
43
|
guid = delegate.guid
|
43
44
|
if guid =~ /thetvdb:\/\/(\d+)/
|
45
|
+
# TODO: figure out how to get zap2it series IDs into plex
|
46
|
+
# If we had zap2it ids in plex metadata and extracted them
|
47
|
+
# here, tivo would show a relevant thumbnail image for the
|
48
|
+
# series in the My Shows UI.
|
44
49
|
md.series_id = "SH#{$1}"
|
45
50
|
end
|
46
51
|
|
@@ -16,7 +16,8 @@ module TivoHMO
|
|
16
16
|
|
17
17
|
self.title = delegate.title
|
18
18
|
self.modified_at = Time.at(delegate.updated_at.to_i)
|
19
|
-
self.created_at = Time.
|
19
|
+
self.created_at = Time.parse(delegate.originally_available_at) rescue nil
|
20
|
+
self.created_at ||= Time.at(delegate.added_at.to_i)
|
20
21
|
end
|
21
22
|
|
22
23
|
def metadata
|
@@ -20,7 +20,7 @@ module TivoHMO
|
|
20
20
|
self.category_qualifier = category_qualifier
|
21
21
|
self.title = category_type.to_s.titleize
|
22
22
|
self.modified_at = Time.at(delegate.updated_at.to_i)
|
23
|
-
self.created_at = Time.
|
23
|
+
self.created_at = Time.now
|
24
24
|
end
|
25
25
|
|
26
26
|
def children
|
@@ -35,7 +35,12 @@ module TivoHMO
|
|
35
35
|
end
|
36
36
|
|
37
37
|
if super.blank?
|
38
|
-
Array(delegate.send(category_qualifier))
|
38
|
+
qualified = Array(delegate.send(category_qualifier))
|
39
|
+
# Sort by title descending so that creation times are
|
40
|
+
# correct for tivo sort of newest first (Time.now for
|
41
|
+
# created_at in Category)
|
42
|
+
qualified = qualified.sort_by{|c| c[:title] }.reverse
|
43
|
+
qualified.each do |category_value|
|
39
44
|
add_child(Category.new(delegate, category_type, category_value))
|
40
45
|
end
|
41
46
|
end
|
@@ -17,24 +17,28 @@ module TivoHMO
|
|
17
17
|
|
18
18
|
self.title = delegate.title
|
19
19
|
self.modified_at = Time.at(delegate.updated_at.to_i)
|
20
|
-
self.created_at = Time.
|
20
|
+
self.created_at = Time.now
|
21
21
|
end
|
22
22
|
|
23
23
|
def children
|
24
24
|
synchronize do
|
25
25
|
if super.blank?
|
26
|
-
|
26
|
+
# Tivo time sorting is reverse chronological (newest first), so
|
27
|
+
# order it here in reverse order so the creation time cause the
|
28
|
+
# right sorting ("all" is newest and comes first)
|
29
|
+
add_child(QualifiedCategory.new(delegate, :by_collection, :collections))
|
30
|
+
add_child(QualifiedCategory.new(delegate, :by_content_rating, :content_ratings))
|
31
|
+
add_child(QualifiedCategory.new(delegate, :by_folder, :folders))
|
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
|
+
|
27
36
|
#add_child(Category.new(delegate, :unwatched))
|
28
|
-
add_child(Category.new(delegate, :newest))
|
29
37
|
add_child(Category.new(delegate, :on_deck))
|
38
|
+
add_child(Category.new(delegate, :newest))
|
30
39
|
add_child(Category.new(delegate, :recently_viewed))
|
40
|
+
add_child(Category.new(delegate, :recently_added))
|
31
41
|
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
42
|
end
|
39
43
|
end
|
40
44
|
|
data/lib/tivohmo/beacon.rb
CHANGED
@@ -7,7 +7,7 @@ module TivoHMO
|
|
7
7
|
class Beacon
|
8
8
|
include GemLogger::LoggerSupport
|
9
9
|
|
10
|
-
def initialize(service_port, limit: -1, interval:
|
10
|
+
def initialize(service_port, limit: -1, interval: 10)
|
11
11
|
@interval = interval
|
12
12
|
@limit = limit
|
13
13
|
@uid = SecureRandom.uuid
|
@@ -23,7 +23,11 @@ module TivoHMO
|
|
23
23
|
@running = true
|
24
24
|
@broadcast_thread = Thread.new do
|
25
25
|
while @running
|
26
|
-
|
26
|
+
begin
|
27
|
+
broadcast
|
28
|
+
rescue => e
|
29
|
+
logger.log_exception(e, "Ignoring exception in beacon thread")
|
30
|
+
end
|
27
31
|
sleep(@interval)
|
28
32
|
@limit = @limit - 1
|
29
33
|
break if @limit == 0
|
data/lib/tivohmo/server.rb
CHANGED
@@ -61,6 +61,19 @@ module TivoHMO
|
|
61
61
|
headers['TiVo_TCD_ID']
|
62
62
|
end
|
63
63
|
|
64
|
+
def select_all_items(children)
|
65
|
+
just_items = []
|
66
|
+
all = children.dup
|
67
|
+
all.each do |child|
|
68
|
+
if child.is_a?(TivoHMO::API::Container)
|
69
|
+
all.concat(child.children)
|
70
|
+
else
|
71
|
+
just_items << child
|
72
|
+
end
|
73
|
+
end
|
74
|
+
children = just_items.uniq {|node| node.identifier }
|
75
|
+
end
|
76
|
+
|
64
77
|
def sort(items, sort_order)
|
65
78
|
sort_order = sort_order.split(/\s*,\s*/)
|
66
79
|
|
@@ -181,7 +194,8 @@ module TivoHMO
|
|
181
194
|
item_count = (params['ItemCount'] || 8).to_i
|
182
195
|
|
183
196
|
# Yes or No, default no
|
184
|
-
|
197
|
+
# TODO: fix this when tivo side is fixed or config mechanism added to globally disable recurse
|
198
|
+
recurse = false #(params['Recurse'] == 'Yes')
|
185
199
|
|
186
200
|
# csv of Type, Title, CreationDate, LastChangeDate, Random
|
187
201
|
# reverse with preceding !
|
@@ -216,6 +230,9 @@ module TivoHMO
|
|
216
230
|
halt 404, "No container found for #{container_path}" unless container
|
217
231
|
|
218
232
|
children = container.children
|
233
|
+
|
234
|
+
children = select_all_items(children) if recurse
|
235
|
+
|
219
236
|
children = sort(children, sort_order) if sort_order
|
220
237
|
|
221
238
|
if anchor_item
|
data/lib/tivohmo/version.rb
CHANGED
@@ -8,15 +8,19 @@ describe TivoHMO::Adapters::Plex::Category do
|
|
8
8
|
describe "#initialize" do
|
9
9
|
|
10
10
|
it "should instantiate" do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
now = Time.now
|
12
|
+
Timecop.freeze(now) do
|
13
|
+
category = described_class.new(plex_delegate, :newest)
|
14
|
+
expect(category).to be_a described_class
|
15
|
+
expect(category).to be_a TivoHMO::API::Container
|
16
|
+
expect(category.category_type).to eq(:newest)
|
17
|
+
expect(category.category_value).to be_nil
|
18
|
+
expect(category.title).to eq('Newest')
|
19
|
+
expect(category.identifier).to eq(plex_delegate.key)
|
20
|
+
expect(category.modified_at).to eq(Time.at(plex_delegate.updated_at))
|
21
|
+
expect(category.created_at).to eq(now)
|
22
|
+
end
|
23
|
+
|
20
24
|
end
|
21
25
|
|
22
26
|
it "should use_category_value for title if present" do
|
@@ -32,7 +36,7 @@ describe TivoHMO::Adapters::Plex::Category do
|
|
32
36
|
describe "#children" do
|
33
37
|
|
34
38
|
it "should memoize" do
|
35
|
-
listing = [plex_stub(::Plex::Movie)]
|
39
|
+
listing = [plex_stub(::Plex::Movie, originally_available_at: "2013-01-02")]
|
36
40
|
allow(plex_delegate).to receive(:newest).and_return(listing)
|
37
41
|
section = described_class.new(plex_delegate, :newest)
|
38
42
|
expect(section.children.object_id).to eq(section.children.object_id)
|
@@ -40,8 +44,8 @@ describe TivoHMO::Adapters::Plex::Category do
|
|
40
44
|
|
41
45
|
it "should have children" do
|
42
46
|
listing = [
|
43
|
-
plex_stub(::Plex::Movie),
|
44
|
-
plex_stub(::Plex::Episode),
|
47
|
+
plex_stub(::Plex::Movie, originally_available_at: "2013-01-02"),
|
48
|
+
plex_stub(::Plex::Episode, originally_available_at: "2013-01-02"),
|
45
49
|
plex_stub(::Plex::Show)
|
46
50
|
]
|
47
51
|
allow(plex_delegate).to receive(:newest).and_return(listing)
|
@@ -51,8 +55,8 @@ describe TivoHMO::Adapters::Plex::Category do
|
|
51
55
|
|
52
56
|
it "should use category_value for children" do
|
53
57
|
listing = [
|
54
|
-
plex_stub(::Plex::Movie),
|
55
|
-
plex_stub(::Plex::Episode),
|
58
|
+
plex_stub(::Plex::Movie, originally_available_at: "2013-01-02"),
|
59
|
+
plex_stub(::Plex::Episode, originally_available_at: "2013-01-02"),
|
56
60
|
plex_stub(::Plex::Show)
|
57
61
|
]
|
58
62
|
cval = {title: 'Title', key: 'key'}
|
@@ -20,7 +20,7 @@ describe TivoHMO::Adapters::Plex::Episode do
|
|
20
20
|
expect(episode.title).to eq(plex_delegate.title)
|
21
21
|
expect(episode.identifier).to eq(plex_delegate.key)
|
22
22
|
expect(episode.modified_at).to eq(Time.at(plex_delegate.updated_at))
|
23
|
-
expect(episode.created_at).to eq(Time.
|
23
|
+
expect(episode.created_at).to eq(Time.parse(plex_delegate.originally_available_at))
|
24
24
|
end
|
25
25
|
|
26
26
|
end
|
@@ -16,7 +16,7 @@ describe TivoHMO::Adapters::Plex::Movie do
|
|
16
16
|
expect(movie.title).to eq(plex_delegate.title)
|
17
17
|
expect(movie.identifier).to eq(plex_delegate.key)
|
18
18
|
expect(movie.modified_at).to eq(Time.at(plex_delegate.updated_at))
|
19
|
-
expect(movie.created_at).to eq(Time.
|
19
|
+
expect(movie.created_at).to eq(Time.parse(plex_delegate.originally_available_at))
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -9,15 +9,18 @@ describe TivoHMO::Adapters::Plex::QualifiedCategory do
|
|
9
9
|
describe "#initialize" do
|
10
10
|
|
11
11
|
it "should instantiate" do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
now = Time.now
|
13
|
+
Timecop.freeze(now) do
|
14
|
+
qcat = described_class.new(plex_delegate, :by_year, '2000')
|
15
|
+
expect(qcat).to be_a described_class
|
16
|
+
expect(qcat).to be_a TivoHMO::API::Container
|
17
|
+
expect(qcat.title).to eq("By Year")
|
18
|
+
expect(qcat.category_qualifier).to eq('2000')
|
19
|
+
expect(qcat.title).to eq("By Year")
|
20
|
+
expect(qcat.identifier).to eq(plex_delegate.key)
|
21
|
+
expect(qcat.modified_at).to eq(Time.at(plex_delegate.updated_at))
|
22
|
+
expect(qcat.created_at).to eq(now)
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
end
|
@@ -43,7 +46,9 @@ describe TivoHMO::Adapters::Plex::QualifiedCategory do
|
|
43
46
|
expect(section.children.size).to eq(2)
|
44
47
|
expect(section.children[0]).to be_instance_of(TivoHMO::Adapters::Plex::Category)
|
45
48
|
expect(section.children[0].category_type).to be(:by_year)
|
46
|
-
|
49
|
+
# reverse order due to sorting by title reversed to work with tivo newest first sorting
|
50
|
+
expect(section.children[0].category_value).to eq(listing[1])
|
51
|
+
expect(section.children[1].category_value).to eq(listing[0])
|
47
52
|
end
|
48
53
|
|
49
54
|
end
|
@@ -8,13 +8,16 @@ describe TivoHMO::Adapters::Plex::Section do
|
|
8
8
|
describe "#initialize" do
|
9
9
|
|
10
10
|
it "should instantiate" do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
now = Time.now
|
12
|
+
Timecop.freeze(now) do
|
13
|
+
section = described_class.new(plex_delegate)
|
14
|
+
expect(section).to be_a described_class
|
15
|
+
expect(section).to be_a TivoHMO::API::Container
|
16
|
+
expect(section.title).to eq(plex_delegate.title)
|
17
|
+
expect(section.identifier).to eq(plex_delegate.key)
|
18
|
+
expect(section.modified_at).to eq(Time.at(plex_delegate.updated_at))
|
19
|
+
expect(section.created_at).to eq(now)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
23
|
end
|
@@ -30,7 +33,7 @@ describe TivoHMO::Adapters::Plex::Section do
|
|
30
33
|
section = described_class.new(plex_delegate)
|
31
34
|
expect(section.children.size).to eq(11)
|
32
35
|
classes = [TivoHMO::Adapters::Plex::Category, TivoHMO::Adapters::Plex::QualifiedCategory]
|
33
|
-
expect(section.children.collect(&:class).uniq).to eq(classes)
|
36
|
+
expect(section.children.collect(&:class).uniq.sort_by(&:name)).to eq(classes.sort_by(&:name))
|
34
37
|
end
|
35
38
|
|
36
39
|
end
|
data/spec/beacon_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe TivoHMO::Beacon do
|
|
9
9
|
beacon = described_class.new(1234)
|
10
10
|
expect(beacon).to be_a described_class
|
11
11
|
expect(beacon.instance_variable_get(:@limit)).to eq(-1)
|
12
|
-
expect(beacon.instance_variable_get(:@interval)).to eq(
|
12
|
+
expect(beacon.instance_variable_get(:@interval)).to eq(10)
|
13
13
|
expect(beacon.instance_variable_get(:@services)).to eq(['TiVoMediaServer:1234/http'])
|
14
14
|
end
|
15
15
|
|
data/spec/server_spec.rb
CHANGED
@@ -288,6 +288,33 @@ describe TivoHMO::Server do
|
|
288
288
|
|
289
289
|
end
|
290
290
|
|
291
|
+
describe "recursing" do
|
292
|
+
|
293
|
+
before(:each) do
|
294
|
+
other_container = container.add_child(TestAPI::Container.new("c2"))
|
295
|
+
other_container.add_child(TestAPI::Item.new("i2")) # dupe child
|
296
|
+
other_container.add_child(TestAPI::Item.new("i98")) # unique child
|
297
|
+
|
298
|
+
third_container = other_container.add_child(TestAPI::Container.new("c3"))
|
299
|
+
other_container.add_child(TestAPI::Item.new("i99")) # unique child
|
300
|
+
other_container.add_child(TestAPI::Item.new("i3")) # dupe child
|
301
|
+
|
302
|
+
3.times do |i|
|
303
|
+
container.add_child(TestAPI::Item.new("i#{i + 2}"))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
it "recurses all items uniquely" do
|
308
|
+
skip "recurse disabled because tivo forces it on initially (no memory)"
|
309
|
+
get "/TiVoConnect?Command=QueryContainer&Container=/a1/c1&Recurse=Yes"
|
310
|
+
expect(last_response.status).to eq(200)
|
311
|
+
doc = Nokogiri::XML(last_response.body)
|
312
|
+
child_titles = doc.xpath("/TiVoContainer/Item/Details/Title").collect(&:content)
|
313
|
+
expect(child_titles).to eq(["i1", "i2", "i3", "i4", "i98", "i99"])
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
291
318
|
describe "sorting" do
|
292
319
|
|
293
320
|
before(:each) do
|
data/spec/spec_helper.rb
CHANGED
data/tivohmo.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "rspec"
|
27
27
|
spec.add_development_dependency "rack-test"
|
28
28
|
spec.add_development_dependency "nokogiri"
|
29
|
+
spec.add_development_dependency "timecop"
|
29
30
|
|
30
31
|
# core dependencies
|
31
32
|
spec.add_dependency "activesupport"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tivohmo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Conway
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: timecop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: activesupport
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|