kodi_client 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kodi_client/global_types/item_types'
4
+ require 'kodi_client/global_types/global_types'
5
+ require 'kodi_client/util/comparable'
6
+ require 'kodi_client/util/iterable'
7
+
8
+ module KodiClient
9
+ module Types
10
+ module Player
11
+
12
+ # enum for in/out zoom
13
+ module Zoom
14
+ extend Iterable
15
+
16
+ IN = 'in'
17
+ OUT = 'out'
18
+ end
19
+
20
+ # Player.ViewMode https://kodi.wiki/view/JSON-RPC_API/v12#Player.ViewMode
21
+ module ViewMode
22
+ extend Iterable
23
+ NORMAL = 'normal'
24
+ ZOOM = 'zoom'
25
+ STRETCH_4x3 = 'strech4x3'
26
+ WIDE_ZOOM = 'widezoom'
27
+ STRETCH_16x9 = 'stretch16x9'
28
+ ORIGINAL = 'original'
29
+ STRETCH_16x9_NONLIN = 'stretch16x9nonline'
30
+ ZOOM_120_WIDTH = 'zoom120width'
31
+ ZOOM_110_WIDTH = 'zoom110width'
32
+ end
33
+
34
+ # player types
35
+ module PlayerVisibilityType
36
+ extend Iterable
37
+ INTERNAL = 'internal'
38
+ EXTERNAL = 'external'
39
+ REMOTE = 'remote'
40
+ end
41
+
42
+ # Player.Type https://kodi.wiki/view/JSON-RPC_API/v12#Player.Type
43
+ module PlayerType
44
+ extend Iterable
45
+
46
+ VIDEO = 'video'
47
+ AUDIO = 'audio'
48
+ PICTURE = 'picture'
49
+ end
50
+
51
+ # Player.Repeat https://kodi.wiki/view/JSON-RPC_API/v12#Player.Repeat
52
+ module PlayerRepeat
53
+ extend Iterable
54
+
55
+ OFF = 'off'
56
+ ONE = 'one'
57
+ ALL = 'all'
58
+ CYCLE = 'cycle'
59
+ end
60
+
61
+ # player speed enum, -32 to 32
62
+ module PlayerSpeed
63
+ extend Iterable
64
+
65
+ MINUS_32 = -32
66
+ MINUS_16 = -16
67
+ MINUS_8 = -8
68
+ MINUS_4 = -4
69
+ MINUS_2 = -2
70
+ MINUS_1 = -1
71
+ NEUTRAL = 0
72
+ PLUS_1 = 1
73
+ PLUS_2 = 2
74
+ PLUS_4 = 4
75
+ PLUS_8 = 8
76
+ PLUS_16 = 16
77
+ PLUS_32 = 32
78
+ end
79
+
80
+ # player id and type
81
+ class Player
82
+ include Comparable
83
+
84
+ attr_reader :player_id, :player_type, :type, :name, :plays_audio, :plays_video
85
+
86
+ def initialize(hash)
87
+ @player_id = hash['playerid']
88
+ @player_type = hash['playertype']
89
+ @type = hash['type']
90
+ @name = hash['name']
91
+ @plays_audio = hash['playsaudio']
92
+ @plays_video = hash['playsvideo']
93
+ end
94
+
95
+ def ==(other)
96
+ compare(self, other)
97
+ end
98
+ end
99
+
100
+ # Player Subtitle https://kodi.wiki/view/JSON-RPC_API/v12#Player.Subtitle
101
+ class Subtitle
102
+ include Comparable
103
+
104
+ attr_reader :index, :is_default, :is_forced, :is_impaired, :language, :name
105
+
106
+ def initialize(hash)
107
+ @index = hash['index']
108
+ @is_default = hash['isdefault']
109
+ @is_forced = hash['isforced']
110
+ @is_impaired = hash['isimpaired']
111
+ @language = hash['language']
112
+ @name = hash['name']
113
+ end
114
+
115
+ def ==(other)
116
+ compare(self, other)
117
+ end
118
+ end
119
+
120
+ # Player.Audio.Stream https://kodi.wiki/view/JSON-RPC_API/v12#Player.Audio.Stream
121
+ class AudioStream
122
+ include Comparable
123
+
124
+ attr_reader :bitrate, :channels, :codec, :index, :is_default, :is_forced, :is_original, :language,
125
+ :name, :sample_rate
126
+
127
+ def initialize(hash)
128
+ @bitrate = hash['bitrate']
129
+ @channels = hash['channels']
130
+ @codec = hash['codec']
131
+ @index = hash['index']
132
+ @is_default = hash['isdefault']
133
+ @is_forced = hash['isforced']
134
+ @is_original = hash['isoriginal']
135
+ @language = hash['language']
136
+ @name = hash['name']
137
+ @sample_rate = hash['samplerate']
138
+ end
139
+
140
+ def ==(other)
141
+ compare(self, other)
142
+ end
143
+ end
144
+
145
+ # Player.Video.Stream https://kodi.wiki/view/JSON-RPC_API/v12#Player.Video.Stream
146
+ class VideoStream
147
+ include Comparable
148
+
149
+ attr_reader :codec, :height, :index, :language, :name, :width, :duration, :aspect
150
+
151
+ def initialize(hash)
152
+ @codec = hash['codec']
153
+ @height = hash['height']
154
+ @index = hash['index']
155
+ @language = hash['language']
156
+ @name = hash['name']
157
+ @width = hash['width']
158
+ @duration = hash['duration']
159
+ @aspect = hash['aspect']
160
+ end
161
+
162
+ def ==(other)
163
+ compare(self, other)
164
+ end
165
+ end
166
+
167
+ # Player.Property.Name https://kodi.wiki/view/JSON-RPC_API/v12#Player.Property.Name
168
+ module PropertyName
169
+ extend Iterable
170
+
171
+ AUDIOSTREAMS = 'audiostreams'
172
+ CANCHANGESPEED = 'canchangespeed'
173
+ CANMOVE = 'canmove'
174
+ CANREPEAT = 'canrepeat'
175
+ CANROTATE = 'canrotate'
176
+ CANSEEK = 'canseek'
177
+ CANSHUFFLE = 'canshuffle'
178
+ CANZOOM = 'canzoom'
179
+ CURRENTAUDIOSTREAM = 'currentaudiostream'
180
+ CURRENTSUBTITLE = 'currentsubtitle'
181
+ CURRENTVIDEOSTREAM = 'currentvideostream'
182
+ LIVE = 'live'
183
+ PARTYMODE = 'partymode'
184
+ PERCENTAGE = 'percentage'
185
+ PLAYLISTID = 'playlistid'
186
+ POSITION = 'position'
187
+ REPEAT = 'repeat'
188
+ SHUFFLED = 'shuffled'
189
+ SPEED = 'speed'
190
+ SUBTITLEENABLED = 'subtitleenabled'
191
+ SUBTITLES = 'subtitles'
192
+ TIME = 'time'
193
+ TOTALTIME = 'totaltime'
194
+ TYPE = 'type'
195
+ VIDEOSTREAMS = 'videostreams'
196
+ end
197
+
198
+ # Player.Property.Value https://kodi.wiki/view/JSON-RPC_API/v12#Player.Property.Name
199
+ class PropertyValue
200
+ include Comparable
201
+
202
+ def initialize(hash)
203
+ @audio_streams = hash['audiostreams'].map { |it| AudioStream.new(it) }
204
+ @cache_percentage = hash['cachepercentage']
205
+ @can_change_speed = hash['canchangespeed']
206
+ @can_move = hash['canmove']
207
+ @can_repeat = hash['canrepeat']
208
+ @can_rotate = hash['canrotate']
209
+ @can_seek = hash['canseek']
210
+ @can_shuffle = hash['canshuffle']
211
+ @can_zoom = hash['canzoom']
212
+ @current_audio_stream = AudioStream.new(hash['currentaudiostream'])
213
+ @current_subtitle = Subtitle.new(hash['currentsubtitle'])
214
+ @current_video_stream = VideoStream.new(hash['currentvideostream'])
215
+ @live = hash['live']
216
+ @party_mode = hash['partymode']
217
+ @percentage = hash['percentage']
218
+ @playlist_id = hash['playlistid'].nil? ? -1 : hash['playlistid']
219
+ @position = hash['position'].nil? ? -1 : hash['position']
220
+ @position = hash['position'].nil? ? -1 : hash['position']
221
+ @repeat = hash['repeat'].nil? ? PlayerRepeat::OFF : hash['repeat']
222
+ @shuffled = hash['shuffled']
223
+ @speed = hash['speed']
224
+ @subtitle_enabled = hash['subtitleenabled']
225
+ @subtitles = hash['subtitles'].map { |it| Subtitle.new(it) }
226
+ @time = Types::Global::GlobalTime.new(hash['time'])
227
+ @total_time = Types::Global::GlobalTime.new(hash['totaltime'])
228
+ @type = hash['type'].nil? ? PlayerType::VIDEO : hash['type']
229
+ @video_streams = hash['videostreams'].map { |it| VideoStream.new(it) }
230
+ end
231
+
232
+ def ==(other)
233
+ compare(self, other)
234
+ end
235
+ end
236
+
237
+ # return value for Player.GetViewMode
238
+ class PlayerViewMode
239
+ include Comparable
240
+
241
+ attr_reader :nonlinear_stretch, :pixel_ratio, :vertical_shift, :view_mode, :zoom
242
+
243
+ def initialize(hash)
244
+ @nonlinear_stretch = hash['nonlinearstretch']
245
+ @pixel_ratio = hash['pixelratio']
246
+ @vertical_shift = hash['verticalshift']
247
+ @view_mode = hash['viewed']
248
+ @zoom = hash['zoom']
249
+ end
250
+
251
+ def ==(other)
252
+ compare(self, other)
253
+ end
254
+ end
255
+
256
+ # Player.Position.Time https://kodi.wiki/view/JSON-RPC_API/v12#Player.Position.Time
257
+ class PlayerPositionTime
258
+ include Comparable
259
+
260
+ attr_reader :hours, :minutes, :seconds, :milliseconds
261
+
262
+ def initialize(hash)
263
+ @hours = hash['hours']
264
+ @minutes = hash['minutes']
265
+ @seconds = hash['seconds']
266
+ @milliseconds = hash['milliseconds']
267
+ end
268
+
269
+ def ==(other)
270
+ compare(self, other)
271
+ end
272
+ end
273
+
274
+ # defines the jump size for seek
275
+ module SeekJump
276
+ include Iterable
277
+
278
+ SMALL_FORWARD = 'smallforward'
279
+ SMALL_BACKWARD = 'smallbackward'
280
+ BIG_FORWARD = 'bigforward'
281
+ BIG_BACKWARD = 'bigbackward'
282
+ end
283
+
284
+ # return value of Player.Seek
285
+ class SeekReturnValue
286
+ include Comparable
287
+
288
+ def initialize(hash)
289
+ @percentage = hash['percentage']
290
+ @time = Types::Global::GlobalTime.new(hash['time'])
291
+ @total_time = Types::Global::GlobalTime.new(hash['totaltime'])
292
+ end
293
+
294
+ def ==(other)
295
+ compare(self, other)
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kodi_client/util/iterable'
4
+
5
+ module KodiClient
6
+ module Types
7
+ module PVR
8
+
9
+ # PVR.Channel.Type https://kodi.wiki/view/JSON-RPC_API/v12#PVR.Channel.Type
10
+ module ChannelType
11
+ TV = 'TV'
12
+ RADIO = 'Radio'
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kodi_client/global_types/media_types'
4
+ require 'kodi_client/global_types/player_type'
5
+ require 'kodi_client/util/comparable'
6
+
7
+ module KodiClient
8
+ module Types
9
+ module Video
10
+
11
+ # Video.Details.Base https://kodi.wiki/view/JSON-RPC_API/v12#Video.Details.Base
12
+ module VideoDetailsBase
13
+ include Media::MediaDetailsBase
14
+
15
+ attr_reader :art, :play_count
16
+
17
+ def video_details_base(hash)
18
+ @art = Types::Media::MediaArtwork.new(hash['art'])
19
+ @play_count = ['playcount']
20
+ media_details_base(hash)
21
+ end
22
+ end
23
+
24
+ # Video.Cast https://kodi.wiki/view/JSON-RPC_API/v12#Video.Cast
25
+ class VideoCast
26
+ include Comparable
27
+
28
+ attr_reader :name, :order, :role, :thumbnail
29
+
30
+ def initialize(hash)
31
+ @name = hash['name']
32
+ @order = hash['order']
33
+ @role = hash['role']
34
+ @thumbnail = hash['thumbnail']
35
+ end
36
+
37
+ def ==(other)
38
+ compare(self, other)
39
+ end
40
+ end
41
+
42
+ # Video.Details.Media https://kodi.wiki/view/JSON-RPC_API/v12#Video.Details.Media
43
+ module VideoDetailsMedia
44
+ include VideoDetailsBase
45
+
46
+ attr_reader :title
47
+
48
+ def video_details_media(hash)
49
+ @title = hash['title']
50
+ video_details_base(hash)
51
+ end
52
+ end
53
+
54
+ # Video.Details.Item https://kodi.wiki/view/JSON-RPC_API/v12#Video.Details.Item
55
+ module VideoDetailsItem
56
+ include VideoDetailsMedia
57
+
58
+ attr_reader :date_added, :file, :last_played, :plot
59
+
60
+ def video_details_item(hash)
61
+ @date_added = hash['dateadded']
62
+ @file = hash['file']
63
+ @last_played = hash['lastplayed']
64
+ @plot = hash['plot']
65
+ video_details_media(hash)
66
+ end
67
+ end
68
+
69
+ # Video.Details.File https://kodi.wiki/view/JSON-RPC_API/v12#Video.Details.File
70
+ module VideoDetailsFile
71
+ include VideoDetailsItem
72
+
73
+ attr_reader :director, :resume, :runtime, :stream_details
74
+
75
+ def video_details_file(hash)
76
+ @director = hash['director']
77
+ @resume = VideoResume.new(hash['resume'])
78
+ @runtime = hash['runtime']
79
+ @stream_details = Streams.new(hash['streamdetails'])
80
+ video_details_item(hash)
81
+ end
82
+ end
83
+
84
+ # Video.Resume https://kodi.wiki/view/JSON-RPC_API/v12#Video.Resume
85
+ class VideoResume
86
+ include Comparable
87
+
88
+ attr_reader :position, :total
89
+
90
+ def initialize(hash)
91
+ @position = hash['position']
92
+ @total = hash['total']
93
+ end
94
+
95
+ def ==(other)
96
+ compare(self, other)
97
+ end
98
+ end
99
+
100
+ # Video.Streams https://kodi.wiki/view/JSON-RPC_API/v12#Video.Streams
101
+ class Streams
102
+ include Comparable
103
+
104
+ attr_reader :audio, :subtitle, :video
105
+
106
+ def initialize(hash)
107
+ @audio = hash['audio'].nil? ? [] : hash['audio'].map { |it| Types::Player::AudioStream.new(it) }
108
+ @subtitle = hash['subtitle'].nil? ? [] : hash['subtitle'].map { |it| Types::Player::Subtitle.new(it) }
109
+ @video = hash['video'].nil? ? [] : hash['video'].map { |it| Types::Player::VideoStream.new(it) }
110
+ end
111
+
112
+ def ==(other)
113
+ compare(self, other)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'http'
4
+ require 'kodi_client/util/comparable'
5
+
6
+ module KodiClient
7
+
8
+ # the client that stores
9
+ class KodiModule
10
+
11
+ def apply_options(options)
12
+ @endpoint_url = "#{options.tls ? 'https://' : 'http://'}#{options.ip}:#{options.port}/jsonrpc"
13
+ @options = options
14
+ end
15
+
16
+ def invoke_api(request)
17
+ h = request.instance_variables.each_with_object({}) do |var, hash|
18
+ hash[var.to_s.delete('@')] = request.instance_variable_get(var)
19
+ end
20
+ @http_client ||= build_client
21
+ response = @http_client.post(@endpoint_url, json: h).to_s
22
+ JSON.parse(response)
23
+ end
24
+
25
+ def build_client
26
+ client = HTTP.headers(Content_Type: 'application/json')
27
+ if !@options.username.nil? && !@options.password.nil?
28
+ client = client.basic_auth({ user: @options.username, pass: @options.password })
29
+ end
30
+
31
+ client
32
+ end
33
+
34
+ private :build_client
35
+ end
36
+
37
+ # represents an error response
38
+ class KodiError
39
+
40
+ attr_reader :code, :message
41
+
42
+ def initialize(hash)
43
+ @code = hash['code']
44
+ @message = hash['message']
45
+ end
46
+
47
+ def ==(other)
48
+ @code == other.code && @message == other.message
49
+ end
50
+ end
51
+
52
+ # represents a kodi requests
53
+ class KodiRequest
54
+
55
+ attr_accessor :id, :jsonrpc, :method, :params
56
+
57
+ def initialize(kodi_id, method, params = {}, jsonrpc = '2.0')
58
+ @id = kodi_id
59
+ @jsonrpc = jsonrpc
60
+ @method = method
61
+ @params = params
62
+ end
63
+ end
64
+
65
+ # represents a kodi response
66
+ class KodiResponse
67
+ include Comparable
68
+
69
+ attr_reader :id, :jsonrpc, :result, :error
70
+
71
+ def initialize(hash)
72
+ @id = hash['id']
73
+ @jsonrpc = hash['jsonrpc']
74
+ @result = hash['result']
75
+ @error = KodiError.new(hash['error']) unless hash['error'].nil?
76
+ end
77
+
78
+ def error?
79
+ !@error.nil?
80
+ end
81
+
82
+ def ==(other)
83
+ compare(self, other)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kodi_client/kodi_module'
4
+ require 'kodi_client/global_types/addon_types'
5
+ require 'kodi_client/global_types/list_types'
6
+
7
+ module KodiClient
8
+ module Modules
9
+ # contains all Kodi Application methods
10
+ class Addons < KodiModule
11
+
12
+ EXECUTE_ADDON = 'Addons.ExecuteAddon'
13
+ GET_ADDON_DETAILS = 'Addons.GetAddonDetails'
14
+ GET_ADDONS = 'Addons.GetAddons'
15
+ SET_ADDON_ENABLED = 'Addons.SetAddonEnabled'
16
+
17
+ def get_addons(type = Types::Addons::AddonTypes::UNKNOWN,
18
+ content = Types::Addons::AddonContent::UNKNOWN,
19
+ enabled = nil, properties = [],
20
+ limit = Types::List::ListLimits.new(0, 50), installed = nil, kodi_id = 1)
21
+ request = KodiRequest.new(kodi_id, GET_ADDONS,
22
+ {
23
+ 'type' => type,
24
+ 'content' => content,
25
+ 'enabled' => enabled.nil? || !enabled ? 'all' : 'enabled',
26
+ 'properties' => properties,
27
+ 'limits' => { 'start' => limit.list_start, 'end' => limit.list_end },
28
+ 'installed' => installed.nil? || !installed ? 'all' : 'installed'
29
+ })
30
+ json = invoke_api(request)
31
+
32
+ result = json['result'].nil? ? nil : Types::Addons::Addons.new(json['result'])
33
+ json['result'] = result
34
+ KodiResponse.new(json)
35
+ end
36
+
37
+ def get_addon_details(addon_id = nil, properties = [], kodi_id = 1)
38
+ request = KodiRequest.new(kodi_id, GET_ADDON_DETAILS,
39
+ {
40
+ 'addonid' => addon_id,
41
+ 'properties' => properties
42
+ })
43
+ json = invoke_api(request)
44
+
45
+ result = json['result'].nil? ? nil : Types::Addons::Addon.new(json['result'])
46
+ json['result'] = result
47
+ KodiResponse.new(json)
48
+ end
49
+
50
+ def execute_addon(addon_id = nil, params = '', wait = false, kodi_id = 1)
51
+ request = KodiRequest.new(kodi_id, EXECUTE_ADDON,
52
+ {
53
+ 'addonid' => addon_id,
54
+ 'params' => params,
55
+ 'wait' => wait
56
+ })
57
+ json = invoke_api(request)
58
+ KodiResponse.new(json)
59
+ end
60
+
61
+ def set_addon_enabled(addon_id = nil, enabled = Types::Global::Toggle::TOGGLE, kodi_id = 1)
62
+ request = KodiRequest.new(kodi_id, SET_ADDON_ENABLED,
63
+ {
64
+ 'addonid' => addon_id,
65
+ 'enabled' => enabled
66
+ })
67
+ json = invoke_api(request)
68
+ KodiResponse.new(json)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kodi_client/kodi_module'
4
+ require 'kodi_client/global_types/application_types'
5
+
6
+ module KodiClient
7
+ module Modules
8
+ # contains all Kodi Application methods
9
+ class Application < KodiModule
10
+
11
+ QUIT = 'Application.Quit'
12
+ SET_MUTE = 'Application.SetMute'
13
+ SET_VOLUME = 'Application.SetVolume'
14
+ GET_PROPERTIES = 'Application.GetProperties'
15
+
16
+ def quit(kodi_id = 1)
17
+ request = KodiRequest.new(kodi_id, QUIT)
18
+ json = invoke_api(request)
19
+ KodiResponse.new(json)
20
+ end
21
+
22
+ def mute(kodi_id = 1, mute_state: Types::Global::Toggle::TOGGLE)
23
+ request = KodiRequest.new(kodi_id, SET_MUTE, { 'mute' => mute_state })
24
+ json = invoke_api(request)
25
+ KodiResponse.new(json)
26
+ end
27
+
28
+ def get_properties(properties = Types::Application::PropertyName.all_properties, kodi_id = 1)
29
+ request = KodiRequest.new(kodi_id, GET_PROPERTIES, { 'properties' => properties })
30
+ json = invoke_api(request)
31
+ result = json['result'].nil? ? nil : Types::Application::PropertyValue.new(json['result'])
32
+ json['result'] = result
33
+ KodiResponse.new(json)
34
+ end
35
+
36
+ def set_volume(volume, kodi_id = 1)
37
+ set_volume_incr_decr_vol(volume, kodi_id)
38
+ end
39
+
40
+ def increment_volume(kodi_id = 1)
41
+ set_volume_incr_decr_vol(Types::Global::IncrementDecrement::INCREMENT, kodi_id)
42
+ end
43
+
44
+ def decrement_volume(kodi_id = 1)
45
+ set_volume_incr_decr_vol(Types::Global::IncrementDecrement::INCREMENT, kodi_id)
46
+ end
47
+
48
+ def set_volume_incr_decr_vol(volume, kodi_id = 1)
49
+ if volume.is_a?(Integer) && (volume > 100 || volume.negative?)
50
+ throw ArgumentError.new('Volume must be between 0 and 100.')
51
+ end
52
+ request = KodiRequest.new(kodi_id, SET_VOLUME, { 'volume' => volume })
53
+ json = invoke_api(request)
54
+ KodiResponse.new(json)
55
+ end
56
+
57
+ private :set_volume_incr_decr_vol
58
+ end
59
+ end
60
+ end