plex-ruby 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/README.md +8 -0
- data/lib/plex-ruby.rb +10 -1
- data/lib/plex-ruby/client.rb +55 -7
- data/lib/plex-ruby/episode.rb +2 -0
- data/lib/plex-ruby/library.rb +54 -0
- data/lib/plex-ruby/movie.rb +2 -0
- data/lib/plex-ruby/parser.rb +7 -0
- data/lib/plex-ruby/season.rb +47 -7
- data/lib/plex-ruby/section.rb +14 -1
- data/lib/plex-ruby/server.rb +26 -6
- data/lib/plex-ruby/show.rb +48 -12
- data/lib/plex-ruby/version.rb +1 -1
- metadata +2 -2
- data/lib/plex-ruby/libary.rb +0 -25
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,3 +3,14 @@
|
|
3
3
|
* Initial Release
|
4
4
|
* Browsing a library works
|
5
5
|
* Sending commands to a clients works intermittently, needs lots of work
|
6
|
+
|
7
|
+
## 0.1.0
|
8
|
+
|
9
|
+
* Gem released of RubyGems
|
10
|
+
|
11
|
+
## 0.2.0
|
12
|
+
|
13
|
+
* Added documentation
|
14
|
+
* Added bang methods that clears caches
|
15
|
+
* Fix naming of 'libary' to 'library'
|
16
|
+
* Moved static content into Constants (Wow!\s)
|
data/README.md
CHANGED
@@ -13,6 +13,14 @@ Add to your `Gemfile` and run the `bundle` command
|
|
13
13
|
gem 'plex-ruby'
|
14
14
|
```
|
15
15
|
|
16
|
+
Or
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem install plex-ruby
|
20
|
+
|
21
|
+
require 'plex-ruby'
|
22
|
+
```
|
23
|
+
|
16
24
|
I developed this using Ruby 1.9.2 so no guaranties that it will work with
|
17
25
|
lesser versions of Ruby.
|
18
26
|
|
data/lib/plex-ruby.rb
CHANGED
@@ -4,6 +4,11 @@ require 'cgi'
|
|
4
4
|
|
5
5
|
module Plex
|
6
6
|
|
7
|
+
# Converts camel case names that are commonly found in the Plex APIs into
|
8
|
+
# ruby friendly names. I.E. playMedia -> play_media
|
9
|
+
#
|
10
|
+
# @param [String] camel case name to be converted
|
11
|
+
# @return [String] snake case form
|
7
12
|
def self.snake_case(string)
|
8
13
|
string.gsub(/::/, '/').
|
9
14
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
@@ -12,6 +17,10 @@ module Plex
|
|
12
17
|
downcase
|
13
18
|
end
|
14
19
|
|
20
|
+
# The base url of the Plex Media Server, I.E. 'http://localhost:32400'
|
21
|
+
# !WARNING! This method will most likely be replaced in future versions
|
22
|
+
#
|
23
|
+
# @return [String] bases url of the Plex Media Server
|
15
24
|
def self.url
|
16
25
|
@@base_url
|
17
26
|
end
|
@@ -25,7 +34,7 @@ end
|
|
25
34
|
require 'plex-ruby/parser'
|
26
35
|
require 'plex-ruby/server'
|
27
36
|
require 'plex-ruby/client'
|
28
|
-
require 'plex-ruby/
|
37
|
+
require 'plex-ruby/library'
|
29
38
|
require 'plex-ruby/section'
|
30
39
|
require 'plex-ruby/video'
|
31
40
|
require 'plex-ruby/media'
|
data/lib/plex-ruby/client.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
module Plex
|
2
2
|
class Client
|
3
3
|
|
4
|
+
NAV_METHODS = %w(moveUp moveDown moveLeft moveRight pageUp pageDown nextLetter
|
5
|
+
previousLetter select back contextMenu toggleOSD)
|
6
|
+
|
7
|
+
PLAYBACK_METHODS = %w(play pause stop rewind fastForward stepForward
|
8
|
+
bigStepForward stepBack bigStepBack skipNext skipPrevious)
|
9
|
+
|
4
10
|
attr_reader :name, :host, :address, :port, :machine_identifier, :version
|
5
11
|
|
6
12
|
def initialize(node)
|
@@ -12,8 +18,12 @@ module Plex
|
|
12
18
|
@version = node.attr('version')
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
|
21
|
+
# Navigation methods
|
22
|
+
# Sends a movement command to the client to move around menus and such
|
23
|
+
#
|
24
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
25
|
+
# the console for the error message
|
26
|
+
NAV_METHODS.each { |nav|
|
17
27
|
class_eval %(
|
18
28
|
def #{Plex.snake_case(nav)}
|
19
29
|
ping player_url+'/navigation/#{nav}'
|
@@ -21,8 +31,12 @@ module Plex
|
|
21
31
|
)
|
22
32
|
}
|
23
33
|
|
24
|
-
|
25
|
-
|
34
|
+
# Playback methods
|
35
|
+
# Sends a playback command to the client to play / pause videos and such
|
36
|
+
#
|
37
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
38
|
+
# the console for the error message
|
39
|
+
PLAYBACK_METHODS.each { |playback|
|
26
40
|
class_eval %(
|
27
41
|
def #{Plex.snake_case(playback)}
|
28
42
|
ping player_url+'/playback/#{playback}'
|
@@ -34,6 +48,15 @@ module Plex
|
|
34
48
|
ping player_url+"/application/playFile"
|
35
49
|
end
|
36
50
|
|
51
|
+
# Plays a video that is in the library
|
52
|
+
#
|
53
|
+
# @param [String] the key of the video that we want to play. (see
|
54
|
+
# Episode#key) (see Movie#key)
|
55
|
+
# @param [String] no clue what this does, its the Plex Remote Command API though
|
56
|
+
# @param [String] no clue what this does, its the Plex Remote Command API though
|
57
|
+
# @param [String] no clue what this does, its the Plex Remote Command API though
|
58
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
59
|
+
# the console for the error message
|
37
60
|
def play_media(key, user_agent = nil, http_cookies = nil, view_offset = nil)
|
38
61
|
url = player_url+'/application/playMedia?'
|
39
62
|
url += "path=#{CGI::escape(Plex.url+key)}"
|
@@ -45,22 +68,47 @@ module Plex
|
|
45
68
|
ping url
|
46
69
|
end
|
47
70
|
|
71
|
+
# Take a screenshot of whats on the Plex Client
|
72
|
+
#
|
73
|
+
# @param [String, Fixnum] width of the screenshot
|
74
|
+
# @param [String, Fixnum] height of the screenshot
|
75
|
+
# @param [String, Fixnum] quality of the screenshot
|
76
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
77
|
+
# the console for the error message
|
48
78
|
def screenshot(width, height, quality)
|
49
79
|
url = player_url+'/application/screenshot?'
|
50
80
|
url += "width=#{width}"
|
51
81
|
url += "&height=#{height}"
|
82
|
+
url += "&quality=#{quality}"
|
83
|
+
|
84
|
+
ping url
|
52
85
|
end
|
53
86
|
|
87
|
+
# Sends a string message to the Plex Client
|
88
|
+
#
|
89
|
+
# @param [String] message to send
|
90
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
91
|
+
# the console for the error message
|
54
92
|
def send_string(text)
|
55
|
-
ping player_url+"/application/sendString?text=#{CGI::escape(text)}"
|
93
|
+
ping player_url+"/application/sendString?text=#{CGI::escape(text.to_s)}"
|
56
94
|
end
|
57
95
|
|
96
|
+
# Sends a key code to the Plex Client. Key codes represent key presses on
|
97
|
+
# a keyboard. Codes are the ASCII value of the letter one wants pressed.
|
98
|
+
# It should be noted that the Plex devs have told people to try and avoid
|
99
|
+
# using this method when writing plugins, as different users can have
|
100
|
+
# different key mappings.
|
101
|
+
#
|
102
|
+
# @param [String, Fixnum] key code to send
|
103
|
+
# @return [True, nil] true if it worked, nil if something went wrong check
|
104
|
+
# the console for the error message
|
58
105
|
def send_key(code)
|
59
|
-
ping player_url+"/application/sendKey?code=#{CGI::escape(code)}"
|
106
|
+
ping player_url+"/application/sendKey?code=#{CGI::escape(code.to_s)}"
|
60
107
|
end
|
61
108
|
|
109
|
+
# (see #send_key)
|
62
110
|
def send_virtual_key(code)
|
63
|
-
ping player_url+"/application/sendVirtualKey?code=#{CGI::escape(code)}"
|
111
|
+
ping player_url+"/application/sendVirtualKey?code=#{CGI::escape(code.to_s)}"
|
64
112
|
end
|
65
113
|
|
66
114
|
private
|
data/lib/plex-ruby/episode.rb
CHANGED
@@ -7,6 +7,8 @@ module Plex
|
|
7
7
|
@key = key
|
8
8
|
end
|
9
9
|
|
10
|
+
# Delegates all method calls to the video object that represents this
|
11
|
+
# episode, if that video object responds to the method.
|
10
12
|
def method_missing(method, *args, &block)
|
11
13
|
if video.respond_to? method
|
12
14
|
video.send(method, *args, &block)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Plex
|
2
|
+
class Library
|
3
|
+
|
4
|
+
# Grab a specific section
|
5
|
+
#
|
6
|
+
# @param [String, Fixnum] key of the section we want
|
7
|
+
# @return [Section] section with that key
|
8
|
+
def section(id)
|
9
|
+
search_sections(xml_doc, id).first
|
10
|
+
end
|
11
|
+
|
12
|
+
# Cache busting version of #section
|
13
|
+
def section!(id)
|
14
|
+
search_sections(xml_doc!, id).first
|
15
|
+
end
|
16
|
+
|
17
|
+
# A list of sections that are located in this library
|
18
|
+
#
|
19
|
+
# @return [Array] list of sections
|
20
|
+
def sections
|
21
|
+
@sections ||= search_sections(xml_doc)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Cache busting version of #sections
|
25
|
+
def sections!
|
26
|
+
@sections = search_sections(xml_doc!)
|
27
|
+
end
|
28
|
+
|
29
|
+
def key
|
30
|
+
"/library/sections"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def search_sections(doc, key = nil)
|
36
|
+
term = key ? "Directory[@key='#{key}']" : 'Directory'
|
37
|
+
doc.search(term).map { |m| Plex::Section.new(m.attributes) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def xml_doc
|
41
|
+
@xml_doc ||= base_doc
|
42
|
+
end
|
43
|
+
|
44
|
+
def xml_doc!
|
45
|
+
@xml_doc = base_doc
|
46
|
+
end
|
47
|
+
|
48
|
+
def base_doc
|
49
|
+
Nokogiri::XML( open(Plex.url+key) )
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/plex-ruby/movie.rb
CHANGED
@@ -7,6 +7,8 @@ module Plex
|
|
7
7
|
@key = key
|
8
8
|
end
|
9
9
|
|
10
|
+
# Delegates all method calls to the video object that represents this
|
11
|
+
# movie, if that video object responds to the method.
|
10
12
|
def method_missing(method, *args, &block)
|
11
13
|
if video.respond_to? method
|
12
14
|
video.send(method, *args, &block)
|
data/lib/plex-ruby/parser.rb
CHANGED
@@ -7,6 +7,13 @@ module Plex
|
|
7
7
|
@node = node
|
8
8
|
end
|
9
9
|
|
10
|
+
# Parses a XML node and returns the structure it represents. This is
|
11
|
+
# currently used to parse Sections as we don't know whether a Section holds
|
12
|
+
# a list of Shows or a list of Movies. The parsing is done recursively.
|
13
|
+
#
|
14
|
+
# @return [Array, Movie, Episode, Show] depending on what node it is given,
|
15
|
+
# will return an Array of Movies or Shows, a single Movie, and single
|
16
|
+
# Episode, or a single Show
|
10
17
|
def parse
|
11
18
|
case node.name
|
12
19
|
when 'document'
|
data/lib/plex-ruby/season.rb
CHANGED
@@ -1,38 +1,78 @@
|
|
1
1
|
module Plex
|
2
|
-
# Found at /
|
2
|
+
# Found at /library/metadata/:key
|
3
3
|
class Season
|
4
4
|
|
5
|
+
ATTRIBUTES = %w(ratingKey guid type title summary index thumb leafCount
|
6
|
+
viewedLeafCount addedAt updatedAt)
|
7
|
+
|
5
8
|
attr_reader :key
|
6
9
|
|
7
10
|
def initialize(key)
|
8
11
|
@key = key
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
# A Season has a key, which allows us to do lazy loading. A season will
|
15
|
+
# not be fully loaded unless one of its attributes is called. Then the
|
16
|
+
# Season will load itself from its key. Once loaded it caches its self.
|
17
|
+
# For every attribute there is a cache busting version wich is just the
|
18
|
+
# name of the attribute followed by '!'. For exsample <tt>season.type</tt>
|
19
|
+
# and <tt>season.type!</tt>
|
20
|
+
ATTRIBUTES.each { |method|
|
13
21
|
class_eval %(
|
14
22
|
def #{Plex.snake_case(method)}; directory.attr('#{method}') end
|
23
|
+
def #{Plex.snake_case(method)}!; directory!.attr('#{method}') end
|
15
24
|
)
|
16
25
|
}
|
17
26
|
|
27
|
+
# Returns the list of episodes in the library that are a part of this Season
|
28
|
+
#
|
29
|
+
# @return [Array] list of episodes in this season that are on the server
|
18
30
|
def episodes
|
19
|
-
@episodes ||=
|
20
|
-
|
31
|
+
@episodes ||= episodes_from_video(children)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Cache busting version of #episodes
|
35
|
+
def episodes!
|
36
|
+
@episodes = episodes_from_video(children!)
|
21
37
|
end
|
22
38
|
|
23
39
|
private
|
24
40
|
|
41
|
+
def base_doc
|
42
|
+
Nokogiri::XML( open(Plex.url+key) )
|
43
|
+
end
|
44
|
+
|
45
|
+
def base_children_doc
|
46
|
+
Nokogiri::XML( open(Plex.url+key+'/children') )
|
47
|
+
end
|
48
|
+
|
25
49
|
def xml_doc
|
26
|
-
@xml_doc ||=
|
50
|
+
@xml_doc ||= base_doc
|
51
|
+
end
|
52
|
+
|
53
|
+
def xml_doc!
|
54
|
+
@xml_doc = base_doc
|
27
55
|
end
|
28
56
|
|
29
57
|
def children
|
30
|
-
@children ||=
|
58
|
+
@children ||= base_children_doc
|
59
|
+
end
|
60
|
+
|
61
|
+
def children!
|
62
|
+
@children = base_children_doc
|
63
|
+
end
|
64
|
+
|
65
|
+
def episodes_from_video(node)
|
66
|
+
node.search("Video").map { |m| Plex::Episode.new(m.attr('key')) }
|
31
67
|
end
|
32
68
|
|
33
69
|
def directory
|
34
70
|
@directory ||= xml_doc.search("Directory").first
|
35
71
|
end
|
36
72
|
|
73
|
+
def directory!
|
74
|
+
@directory = xml_doc!.search("Directory").first
|
75
|
+
end
|
76
|
+
|
37
77
|
end
|
38
78
|
end
|
data/lib/plex-ruby/section.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Plex
|
2
2
|
class Section
|
3
3
|
|
4
|
+
GROUPS = %w(all unwatched newest recentlyAdded recentlyViewed onDeck)
|
5
|
+
|
4
6
|
attr_reader :refreshing, :type, :title, :art, :agent, :scanner, :language,
|
5
7
|
:updated_at
|
6
8
|
|
@@ -16,11 +18,22 @@ module Plex
|
|
16
18
|
@updated_at = options['updatedAt'].value
|
17
19
|
end
|
18
20
|
|
21
|
+
# NOT IMPLEMENTED
|
19
22
|
def refresh(deep = false, force = false)
|
20
23
|
end
|
21
24
|
|
22
25
|
|
23
|
-
|
26
|
+
# Returns a list of shows or movies that are in this Section.
|
27
|
+
#
|
28
|
+
# all - all videos in this Section
|
29
|
+
# unwatched - videos unwatched in this Section
|
30
|
+
# newest - most recent videos in this Section
|
31
|
+
# recently_added - recently added videos in this Section
|
32
|
+
# recently_viewed - recently viewed videos in this Section
|
33
|
+
# on_deck - videos that are "on deck" in this Section
|
34
|
+
#
|
35
|
+
# @return [Array] list of Shows or Movies in that group
|
36
|
+
GROUPS.each { |method|
|
24
37
|
class_eval %(
|
25
38
|
def #{Plex.snake_case(method)}
|
26
39
|
Plex::Parser.new( Nokogiri::XML(open(Plex.url+key+'/#{method}')) ).parse
|
data/lib/plex-ruby/server.rb
CHANGED
@@ -9,21 +9,41 @@ module Plex
|
|
9
9
|
Plex.url = "http://#{host}:#{port}"
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
# The library of this server
|
13
|
+
#
|
14
|
+
# @return [Library] this Servers library
|
15
|
+
def library
|
16
|
+
@library ||= Plex::Libary.new
|
13
17
|
end
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
# The Plex clients that are connected to this Server
|
20
|
+
#
|
21
|
+
# @return [Array] list of Clients connected to this server
|
22
|
+
def clients
|
23
|
+
@clients ||= search_clients clients_doc
|
17
24
|
end
|
18
25
|
|
19
|
-
|
20
|
-
|
26
|
+
# Cache busting version of #clients
|
27
|
+
def clients!
|
28
|
+
@clients = search_clients clients_doc!
|
21
29
|
end
|
22
30
|
|
23
31
|
private
|
24
32
|
|
33
|
+
def clients_base
|
34
|
+
Nokogiri::XML( open(Plex.url+'/clients') )
|
35
|
+
end
|
36
|
+
|
25
37
|
def clients_doc
|
26
|
-
@clients_doc ||=
|
38
|
+
@clients_doc ||= clients_base
|
39
|
+
end
|
40
|
+
|
41
|
+
def clients_doc!
|
42
|
+
@clients_doc = clients_base
|
43
|
+
end
|
44
|
+
|
45
|
+
def search_clients(node)
|
46
|
+
node.search('Server').map { |m| Plex::Client.new(m) }
|
27
47
|
end
|
28
48
|
|
29
49
|
end
|
data/lib/plex-ruby/show.rb
CHANGED
@@ -1,40 +1,76 @@
|
|
1
1
|
module Plex
|
2
|
-
# Found at /
|
2
|
+
# Found at /library/metadata/:key
|
3
3
|
class Show
|
4
4
|
|
5
|
+
ATTRIBUTES = %w(guid studio title contentRating summary index rating year thumb
|
6
|
+
art banner theme duration originallyAvailableAt leafCount
|
7
|
+
viewedLeafCount addedAt updatedAt)
|
8
|
+
|
5
9
|
attr_reader :key
|
6
10
|
|
7
11
|
def initialize(key)
|
8
12
|
@key = key
|
9
13
|
end
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
# A Show has a key, which allows us to do lazy loading. A Show will
|
16
|
+
# not be fully loaded unless one of its attributes is called. Then the
|
17
|
+
# Show will load itself from its key. Once loaded it caches its self.
|
18
|
+
ATTRIBUTES.each { |method|
|
14
19
|
class_eval %(
|
15
20
|
def #{Plex.snake_case(method)}; @#{method} ||= directory.attr('#{method}') end
|
21
|
+
def #{Plex.snake_case(method)}!; @#{method} = directory!.attr('#{method}') end
|
16
22
|
)
|
17
23
|
}
|
18
24
|
|
25
|
+
# The list of seasons in the library that belong to this Show
|
26
|
+
#
|
27
|
+
# @return [Array] list of Seasons that are a part of this Show
|
19
28
|
def seasons
|
20
|
-
@seasons ||=
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
@seasons ||= search_children children
|
30
|
+
end
|
31
|
+
|
32
|
+
def seasons!
|
33
|
+
@seasons = search_children children!
|
24
34
|
end
|
25
35
|
|
26
36
|
private
|
27
37
|
|
38
|
+
def base_doc
|
39
|
+
Nokogiri::XML( open(Plex.url+key) )
|
40
|
+
end
|
41
|
+
|
42
|
+
def children_base
|
43
|
+
Nokogiri::XML( open(Plex.url+key+'/children') )
|
44
|
+
end
|
45
|
+
|
46
|
+
def xml_doc
|
47
|
+
@xml_doc ||= base_doc
|
48
|
+
end
|
49
|
+
|
50
|
+
def xml_doc!
|
51
|
+
@xml_doc = base_doc
|
52
|
+
end
|
53
|
+
|
28
54
|
def children
|
29
|
-
@children ||=
|
55
|
+
@children ||= children_base
|
56
|
+
end
|
57
|
+
|
58
|
+
def children!
|
59
|
+
@children = children_base
|
30
60
|
end
|
31
61
|
|
32
62
|
def directory
|
33
63
|
@directory ||= xml_doc.search('Directory').first
|
34
64
|
end
|
35
|
-
|
36
|
-
def
|
37
|
-
@
|
65
|
+
|
66
|
+
def directory!
|
67
|
+
@directory = xml_doc!.search('Directory').first
|
68
|
+
end
|
69
|
+
|
70
|
+
def search_children(node)
|
71
|
+
node.search('Directory').map do |season|
|
72
|
+
Plex::Season.new(season.attr('key')[0..-10]) # Remove /children
|
73
|
+
end
|
38
74
|
end
|
39
75
|
|
40
76
|
end
|
data/lib/plex-ruby/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: plex-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Eric Koslow
|
@@ -65,7 +65,7 @@ files:
|
|
65
65
|
- lib/plex-ruby.rb
|
66
66
|
- lib/plex-ruby/client.rb
|
67
67
|
- lib/plex-ruby/episode.rb
|
68
|
-
- lib/plex-ruby/
|
68
|
+
- lib/plex-ruby/library.rb
|
69
69
|
- lib/plex-ruby/media.rb
|
70
70
|
- lib/plex-ruby/movie.rb
|
71
71
|
- lib/plex-ruby/null.rb
|
data/lib/plex-ruby/libary.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Plex
|
2
|
-
class Libary
|
3
|
-
|
4
|
-
def section(id)
|
5
|
-
xml_doc.search("Directory[@key='#{id}']").map do |m|
|
6
|
-
Plex::Section.new(m.attributes)
|
7
|
-
end.first
|
8
|
-
end
|
9
|
-
|
10
|
-
def sections
|
11
|
-
xml_doc.search('Directory').map { |m| Plex::Section.new(m.attributes) }
|
12
|
-
end
|
13
|
-
|
14
|
-
def key
|
15
|
-
"/library/sections"
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def xml_doc
|
21
|
-
@xml_doc ||= Nokogiri::XML( open(Plex.url+key) )
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|