kappa 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +28 -0
  2. data/lib/kappa.rb +858 -0
  3. data/lib/kappa/version.rb +1 -0
  4. metadata +112 -0
@@ -0,0 +1,28 @@
1
+ # <img src="http://static-cdn.jtvnw.net/jtv_user_pictures/chansub-global-emoticon-ddc6e3a8732cb50f-25x28.png" /> Kappa
2
+
3
+ Kappa is a Ruby library for interfacing with the [Twitch.tv Kraken API](https://github.com/justintv/Twitch-API).
4
+
5
+ ## Getting Started
6
+ ============
7
+
8
+ `gem install kappa`
9
+
10
+ ```ruby
11
+ require 'kappa'
12
+
13
+ twitch = Kappa::Client.new
14
+ grubby = twitch.channel('followgrubby')
15
+ puts grubby.streaming?
16
+ ```
17
+
18
+ ## API
19
+ ============
20
+
21
+ ## Contributing
22
+ ============
23
+
24
+ ## License
25
+
26
+ Copyright &copy; 2013 Chris Schmich
27
+ <br />
28
+ MIT License, see LICENSE for details.
@@ -0,0 +1,858 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'addressable/uri'
4
+ require 'securerandom'
5
+ require 'set'
6
+
7
+ # TODO
8
+ # https://github.com/justintv/Twitch-API
9
+ # Blocks
10
+ # GET /users/:login/blocks
11
+ # PUT /users/:user/blocks/:target
12
+ # DELETE /users/:user/blocks/:target
13
+ # Channels
14
+ # - GET /channels/:channel
15
+ # GET /channel
16
+ # GET /channels/:channel/editors
17
+ # PUT /channels/:channel
18
+ # GET /channels/:channel/videos
19
+ # GET /channels/:channel/follows
20
+ # DELETE /channels/:channel/stream_key
21
+ # POST /channels/:channel/commercial
22
+ # Chat
23
+ # GET /chat/:channel
24
+ # GET /chat/emoticons
25
+ # Follows
26
+ # GET /channels/:channel/follows
27
+ # GET /users/:user/follows/channels
28
+ # GET /users/:user/follows/channels/:target
29
+ # PUT /users/:user/follows/channels/:target
30
+ # DELETE /users/:user/follows/channels/:target
31
+ # Games
32
+ # - GET /games/top
33
+ # Ingests
34
+ # GET /ingests
35
+ # Root
36
+ # GET /
37
+ # Search
38
+ # GET /search/streams
39
+ # - GET /search/games
40
+ # Streams
41
+ # - GET /streams/:channel
42
+ # - GET /streams
43
+ # GET /streams/featured
44
+ # GET /streams/summary
45
+ # GET /streams/followed
46
+ # Subscriptions
47
+ # GET /channels/:channel/subscriptions
48
+ # GET /channels/:channel/subscriptions/:user
49
+ # Teams
50
+ # GET /teams
51
+ # GET /teams/:team
52
+ # Users
53
+ # GET /users/:user
54
+ # GET /user
55
+ # GET /streams/followed
56
+ # Videos
57
+ # GET /videos/:id
58
+ # GET /videos/top
59
+ # GET /channels/:channel/videos
60
+
61
+ # Overarching
62
+ # - Common query syntax
63
+ # - Access to raw properties (e.g. stream['game'] or stream.raw('game'))
64
+ # - Paginated results take a block to allow for intermediate processing/termination
65
+
66
+ # t = Kappa::Client.new
67
+ # c = t.channel('lagtvmaximusblack')
68
+ # c.editors -> [...]
69
+ # c.videos -> [...]
70
+ # c.followers -> [...]
71
+ # c.subscriptions
72
+ # c.start_commercial
73
+ # c.reset_stream_key
74
+ # c... ; c.save!
75
+ # TODO: current user channel
76
+
77
+ # t = Kappa::Client.new
78
+ # t.streams.all
79
+ # t.streams.all(:limit => 10)
80
+ # t.streams.featured
81
+ # t.streams.where(:channel => 'lagtvmaximusblack')
82
+ # t.streams.where(:channel => [...], :game => '...', :embeddable => t/f, :hls => t/f)
83
+ # t.stream_summary
84
+
85
+ module Kappa
86
+ class Connection
87
+ include HTTParty
88
+ debug_output $stdout
89
+
90
+ def initialize(base_url)
91
+ @base_url = Addressable::URI.parse(base_url)
92
+
93
+ uuid = SecureRandom.uuid
94
+ # TODO: Embed current library version.
95
+ @client_id = "Kappa-v1-#{uuid}"
96
+
97
+ @last_request_time = Time.now - RATE_LIMIT_SEC
98
+ end
99
+
100
+ def get(path, query = nil)
101
+ # TODO: Rate-limiting.
102
+
103
+ request_url = @base_url + path
104
+
105
+ # Handle non-JSON response
106
+ # Handle invalid JSON
107
+ # Handle non-200 codes
108
+
109
+ headers = {
110
+ 'Client-ID' => @client_id,
111
+ 'Accept' => 'application/vnd.twitchtv.v2+json'
112
+ }
113
+
114
+ response = rate_limit do
115
+ self.class.get(request_url, :headers => headers, :query => query)
116
+ end
117
+
118
+ json = response.body
119
+ return JSON.parse(json)
120
+ end
121
+
122
+ def paginated(path, params = {})
123
+ limit = [params[:limit] || 100, 100].min
124
+ offset = params[:offset] || 0
125
+
126
+ path_uri = Addressable::URI.parse(path)
127
+ query = { 'limit' => limit, 'offset' => offset }
128
+ path_uri.query_values ||= {}
129
+ path_uri.query_values = path_uri.query_values.merge(query)
130
+
131
+ request_url = path_uri.to_s
132
+
133
+ params = params.dup
134
+ params.delete(:limit)
135
+ params.delete(:offset)
136
+
137
+ # TODO: Hande request retry.
138
+ loop do
139
+ json = get(request_url, params)
140
+
141
+ if json['error'] && (json['status'] == 503)
142
+ break
143
+ end
144
+
145
+ break if !yield(json)
146
+
147
+ links = json['_links']
148
+ next_url = links['next']
149
+
150
+ next_uri = Addressable::URI.parse(next_url)
151
+ offset = next_uri.query_values['offset'].to_i
152
+
153
+ total = json['_total']
154
+ break if total && (offset > total)
155
+
156
+ request_url = next_url
157
+ end
158
+ end
159
+
160
+ private
161
+ def rate_limit
162
+ delta = Time.now - @last_request_time
163
+ delay = [RATE_LIMIT_SEC - delta, 0].max
164
+
165
+ sleep delay if delay > 0
166
+
167
+ begin
168
+ return yield
169
+ ensure
170
+ @last_request_time = Time.now
171
+ end
172
+ end
173
+
174
+ RATE_LIMIT_SEC = 1
175
+ end
176
+
177
+ module IdEquality
178
+ def hash
179
+ @id.hash
180
+ end
181
+
182
+ def eql?(other)
183
+ other && (self.id == other.id)
184
+ end
185
+
186
+ def ==(other)
187
+ eql?(other)
188
+ end
189
+ end
190
+
191
+ class Client
192
+ def initialize(opts = {})
193
+ base_url = opts[:base_url] || DEFAULT_BASE_URL
194
+ @conn = Connection.new(base_url)
195
+ end
196
+
197
+ #
198
+ # GET /users/:user
199
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/users.md#get-usersuser
200
+ #
201
+ def user(user_name)
202
+ encoded_name = Addressable::URI.encode(user_name)
203
+ User.new(@conn.get("users/#{encoded_name}"), @conn)
204
+ end
205
+
206
+ #
207
+ # GET /channels/:channel
208
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannel
209
+ #
210
+ def channel(channel_name)
211
+ encoded_name = Addressable::URI.encode(channel_name)
212
+ Channel.new(@conn.get("channels/#{encoded_name}"), @conn)
213
+ end
214
+
215
+ #
216
+ # GET /streams/:channel
217
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/streams.md#get-streamschannel
218
+ #
219
+ def stream(channel_name)
220
+ encoded_name = Addressable::URI.encode(channel_name)
221
+ json = @conn.get("streams/#{encoded_name}")
222
+ stream_json = json['stream']
223
+ if stream_json
224
+ Stream.new(json['stream'], @conn)
225
+ else
226
+ nil
227
+ end
228
+ end
229
+
230
+ def streams
231
+ @streams ||= StreamQuery.new(@conn)
232
+ end
233
+
234
+ def games
235
+ @games ||= GameQuery.new(@conn)
236
+ end
237
+
238
+ #
239
+ # GET /teams/:team
240
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/teams.md#get-teamsteam
241
+ #
242
+ def team(team_name)
243
+ encoded_name = Addressable::URI.encode(team_name)
244
+ json = @conn.get("teams/#{encoded_name}")
245
+ Team.new(json, @conn)
246
+ end
247
+
248
+ def teams
249
+ @teams ||= TeamQuery.new(@conn)
250
+ end
251
+
252
+ #
253
+ # GET /videos/:id
254
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/videos.md#get-videosid
255
+ #
256
+ def video(video_id)
257
+ Video.new(@conn.get("videos/#{video_id}"), @conn)
258
+ end
259
+
260
+ def videos
261
+ @videos ||= VideoQuery.new(@conn)
262
+ end
263
+
264
+ # TODO: Move?
265
+ def stats
266
+ Stats.new(@conn.get('streams/summary'), @conn)
267
+ end
268
+
269
+ DEFAULT_BASE_URL = 'https://api.twitch.tv/kraken/'
270
+ end
271
+
272
+ class Channel
273
+ def initialize(json, conn)
274
+ @conn = conn
275
+
276
+ @id = json['_id']
277
+ @background_url = json['background']
278
+ @banner_url = json['banner']
279
+ @created_at = DateTime.parse(json['created_at'])
280
+ @stream_delay_sec = json['delay']
281
+ @display_name = json['display_name']
282
+ @game = json['game']
283
+ @logo_url = json['logo']
284
+ @mature = json['mature'] || false
285
+ @name = json['name']
286
+ @status = json['status']
287
+ @updated_at = DateTime.parse(json['updated_at'])
288
+ @url = json['url']
289
+ @video_banner = json['video_banner']
290
+ end
291
+
292
+ include IdEquality
293
+
294
+ def mature?
295
+ @mature
296
+ end
297
+
298
+ def stream
299
+ encoded_name = Addressable::URI.encode(@name)
300
+ json = @conn.get("streams/#{encoded_name}")
301
+ stream_json = json['stream']
302
+ if stream_json
303
+ Stream.new(json['stream'], @conn)
304
+ else
305
+ nil
306
+ end
307
+ end
308
+
309
+ def streaming?
310
+ !self.stream.nil?
311
+ end
312
+
313
+ #
314
+ # GET /channels/:channel/editors
315
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschanneleditors
316
+ #
317
+ def editors
318
+ # TODO: ...
319
+ end
320
+
321
+ #
322
+ # GET /channels/:channels/videos
323
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/videos.md#get-channelschannelvideos
324
+ #
325
+ def videos(params = {})
326
+ # TODO: ...
327
+ end
328
+
329
+ #
330
+ # GET /channels/:channel/follows
331
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannelfollows
332
+ # TODO: Warning: this set can be very large, this can run for very long time, recommend using :limit/:offset.
333
+ #
334
+ def followers(params = {})
335
+ limit = params[:limit] || 0
336
+
337
+ followers = []
338
+ ids = Set.new
339
+
340
+ @conn.paginated("channels/#{@name}/follows", params) do |json|
341
+ current_followers = json['follows']
342
+ current_followers.each do |follow_json|
343
+ user_json = follow_json['user']
344
+ user = User.new(user_json, @conn)
345
+ if ids.add?(user.id)
346
+ followers << user
347
+ if followers.count == limit
348
+ return followers
349
+ end
350
+ end
351
+ end
352
+
353
+ !current_followers.empty?
354
+ end
355
+
356
+ followers
357
+ end
358
+
359
+ # TODO: Requires authentication.
360
+ def subscribers
361
+ end
362
+
363
+ #
364
+ # GET /channels/:channel/subscriptions/:user
365
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/subscriptions.md#get-channelschannelsubscriptionsuser
366
+ #
367
+ # TODO: Requires authentication.
368
+ def has_subscriber?(user)
369
+ # Support User object or username (string)
370
+ end
371
+
372
+ # t = Kappa::Client.new
373
+ # c = t.channel('lagtvmaximusblack')
374
+ # c.followers -> [...]
375
+ # c.subscriptions
376
+ # c.start_commercial
377
+ # c.reset_stream_key
378
+ # c... ; c.save!
379
+ # TODO: current user channel
380
+
381
+ attr_reader :id
382
+ attr_reader :background_url
383
+ attr_reader :banner_url
384
+ attr_reader :created_at
385
+ attr_reader :stream_delay_sec
386
+ attr_reader :display_name
387
+ attr_reader :game
388
+ attr_reader :logo_url
389
+ attr_reader :name
390
+ attr_reader :status
391
+ attr_reader :updated_at
392
+ attr_reader :url
393
+ attr_reader :video_banner
394
+ end
395
+
396
+ class TeamQuery
397
+ def initialize(conn)
398
+ @conn = conn
399
+ end
400
+
401
+ #
402
+ # GET /teams
403
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/teams.md#get-teams
404
+ #
405
+ def all(params = {})
406
+ limit = params[:limit] || 0
407
+
408
+ teams = []
409
+ ids = Set.new
410
+
411
+ @conn.paginated('teams', params) do |json|
412
+ teams_json = json['teams']
413
+ teams_json.each do |team_json|
414
+ team = Team.new(team_json, @conn)
415
+ if ids.add?(team.id)
416
+ teams << team
417
+ if teams.count == limit
418
+ return teams
419
+ end
420
+ end
421
+ end
422
+
423
+ !teams_json.empty?
424
+ end
425
+
426
+ teams
427
+ end
428
+ end
429
+
430
+ class StreamQuery
431
+ def initialize(conn)
432
+ @conn = conn
433
+ end
434
+
435
+ def all
436
+ end
437
+
438
+ #
439
+ # GET /streams
440
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/streams.md
441
+ # :game (single, string), :channel (string array), :limit (int), :offset (int), :embeddable (bool), :hls (bool)
442
+ #
443
+ def where(params = {})
444
+ limit = params[:limit] || 0
445
+
446
+ params = params.dup
447
+ if params[:channel]
448
+ params[:channel] = params[:channel].join(',')
449
+ end
450
+
451
+ streams = []
452
+ ids = Set.new
453
+
454
+ @conn.paginated('streams', params) do |json|
455
+ current_streams = json['streams']
456
+ current_streams.each do |stream_json|
457
+ stream = Stream.new(stream_json, @conn)
458
+ if ids.add?(stream.id)
459
+ streams << stream
460
+ if streams.count == limit
461
+ return streams
462
+ end
463
+ end
464
+ end
465
+
466
+ !current_streams.empty?
467
+ end
468
+
469
+ streams
470
+ end
471
+
472
+ #
473
+ # GET /streams/featured
474
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/streams.md#get-streamsfeatured
475
+ #
476
+ def featured(params = {})
477
+ limit = params[:limit] || 0
478
+
479
+ streams = []
480
+ ids = Set.new
481
+
482
+ @conn.paginated('streams/featured', params) do |json|
483
+ current_streams = json['featured']
484
+ current_streams.each do |featured_json|
485
+ # TODO: Capture more information from the featured_json structure (need a FeaturedStream class?)
486
+ stream_json = featured_json['stream']
487
+ stream = Stream.new(stream_json, @conn)
488
+ if ids.add?(stream.id)
489
+ streams << stream
490
+ if streams.count == limit
491
+ return streams
492
+ end
493
+ end
494
+ end
495
+
496
+ !current_streams.empty?
497
+ end
498
+
499
+ streams
500
+ end
501
+ end
502
+
503
+ class Stream
504
+ def initialize(json, conn)
505
+ @conn = conn
506
+
507
+ @id = json['_id']
508
+ @broadcaster = json['broadcaster']
509
+ @game_name = json['game']
510
+ @name = json['name']
511
+ @viewer_count = json['viewers']
512
+ @preview_url = json['preview']
513
+ @channel = Channel.new(json['channel'], @conn)
514
+ end
515
+
516
+ include IdEquality
517
+
518
+ def channel
519
+ end
520
+
521
+ attr_reader :id
522
+ attr_reader :broadcaster
523
+ attr_reader :game_name
524
+ attr_reader :name
525
+ attr_reader :viewer_count
526
+ attr_reader :preview_url
527
+ attr_reader :channel
528
+ end
529
+
530
+ class GameQuery
531
+ def initialize(conn)
532
+ @conn = conn
533
+ end
534
+
535
+ #
536
+ # GET /games/top
537
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/games.md#get-gamestop
538
+ #
539
+ def top(params = {})
540
+ limit = params[:limit] || 0
541
+
542
+ games = []
543
+ ids = Set.new
544
+
545
+ @conn.paginated('games/top', params) do |json|
546
+ current_games = json['top']
547
+ current_games.each do |game_json|
548
+ game = Game.new(game_json)
549
+ if ids.add?(game.id)
550
+ games << game
551
+ if games.count == limit
552
+ return games
553
+ end
554
+ end
555
+ end
556
+ end
557
+
558
+ games
559
+ end
560
+
561
+ #
562
+ # GET /search/games
563
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/search.md#get-searchgames
564
+ #
565
+ def search(params = {})
566
+ live = params[:live] || false
567
+ name = params[:name]
568
+
569
+ games = []
570
+ ids = Set.new
571
+
572
+ json = @conn.get('search/games', :query => name, :type => 'suggest', :live => live)
573
+ all_games = json['games']
574
+ all_games.each do |game_json|
575
+ game = GameSuggestion.new(game_json)
576
+ if ids.add?(game.id)
577
+ games << game
578
+ end
579
+ end
580
+
581
+ games
582
+ end
583
+ end
584
+
585
+ class VideoQuery
586
+ # ...
587
+ def top
588
+ end
589
+ end
590
+
591
+ class Game
592
+ def initialize(json)
593
+ @channel_count = json['channels']
594
+ @viewer_count = json['viewers']
595
+
596
+ game = json['game']
597
+ @id = game['_id']
598
+ @name = game['name']
599
+ @giantbomb_id = game['giantbomb_id']
600
+ @box_images = Images.new(game['box'])
601
+ @logo_images = Images.new(game['logo'])
602
+ end
603
+
604
+ include IdEquality
605
+
606
+ attr_reader :id
607
+ attr_reader :name
608
+ attr_reader :giantbomb_id
609
+ attr_reader :box_images
610
+ attr_reader :logo_images
611
+ attr_reader :channel_count
612
+ attr_reader :viewer_count
613
+ end
614
+
615
+ class GameSuggestion
616
+ def initialize(json)
617
+ @id = json['_id']
618
+ @name = json['name']
619
+ @giantbomb_id = json['giantbomb_id']
620
+ @popularity = json['popularity']
621
+ @box_images = Images.new(json['box'])
622
+ @logo_images = Images.new(json['logo'])
623
+ end
624
+
625
+ include IdEquality
626
+
627
+ attr_reader :id
628
+ attr_reader :name
629
+ attr_reader :giantbomb_id
630
+ attr_reader :popularity
631
+ attr_reader :box_images
632
+ attr_reader :logo_images
633
+ end
634
+
635
+ class Images
636
+ def initialize(json)
637
+ @large_url = json['large']
638
+ @medium_url = json['medium']
639
+ @small_url = json['small']
640
+ @template_url = json['template']
641
+ end
642
+
643
+ def url(width, height)
644
+ @template_url.gsub('{width}', width.to_s, '{height}', height.to_s)
645
+ end
646
+
647
+ attr_reader :large_url
648
+ attr_reader :medium_url
649
+ attr_reader :small_url
650
+ attr_reader :template_url
651
+ end
652
+
653
+ class User
654
+ def initialize(json, conn)
655
+ @conn = conn
656
+
657
+ # TODO: nil checks
658
+ @id = json['_id']
659
+ @created_at = DateTime.parse(json['created_at'])
660
+ @display_name = json['display_name']
661
+ @logo_url = json['logo']
662
+ @name = json['name']
663
+ @type = json['type']
664
+ @updated_at = DateTime.parse(json['updated_at'])
665
+ end
666
+
667
+ #
668
+ # GET /channels/:channel/subscriptions/:user
669
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/subscriptions.md#get-channelschannelsubscriptionsuser
670
+ #
671
+ # TODO: Requires authentication.
672
+ def subscribed_to?(channel_name)
673
+ end
674
+
675
+ #
676
+ # GET /streams/followed
677
+ # TODO: Authenticate.
678
+ # TODO: Only valid for authenticated user, might not belong here.
679
+ #
680
+ # GET /users/:user/follows/channels
681
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/follows.md#get-usersuserfollowschannels
682
+ #
683
+ def following(params = {})
684
+ limit = params[:limit] || 0
685
+
686
+ channels = []
687
+ ids = Set.new
688
+
689
+ @conn.paginated("users/#{@name}/follows/channels", params) do |json|
690
+ current_channels = json['follows']
691
+ current_channels.each do |follow_json|
692
+ channel_json = follow_json['channel']
693
+ channel = Channel.new(channel_json, @conn)
694
+ if ids.add?(channel.id)
695
+ channels << channel
696
+ if channels.count == limit
697
+ return channels
698
+ end
699
+ end
700
+ end
701
+
702
+ !current_channels.empty?
703
+ end
704
+
705
+ channels
706
+ end
707
+
708
+ #
709
+ # GET /users/:user/follows/:channels/:target
710
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/follows.md#get-usersuserfollowschannelstarget
711
+ #
712
+ def following?(channel_name)
713
+ encoded_name = Addressable::URI.encode(channel_name)
714
+ json = @conn.get("users/#{@name}/follows/channels/#{encoded_name}")
715
+ status = json['status']
716
+ return !status || (status != 404)
717
+ end
718
+
719
+ attr_reader :id
720
+ attr_reader :created_at
721
+ attr_reader :display_name
722
+ attr_reader :logo_url
723
+ attr_reader :name
724
+ attr_reader :type
725
+ attr_reader :updated_at
726
+ end
727
+
728
+ class Stats
729
+ def initialize(json, conn)
730
+ @viewer_count = json['viewers']
731
+ @stream_count = json['channels']
732
+ end
733
+
734
+ attr_reader :viewer_count
735
+ attr_reader :stream_count
736
+ end
737
+
738
+ class Team
739
+ def initialize(json, conn)
740
+ @id = json['_id']
741
+ @info = json['info']
742
+ @background_url = json['background']
743
+ @banner_url = json['banner']
744
+ @logo_url = json['logo']
745
+ @name = json['name']
746
+ @display_name = json['display_name']
747
+ @updated_at = DateTime.parse(json['updated_at'])
748
+ @created_at = DateTime.parse(json['created_at'])
749
+ end
750
+
751
+ include IdEquality
752
+
753
+ attr_reader :id
754
+ attr_reader :info
755
+ attr_reader :background_url
756
+ attr_reader :banner_url
757
+ attr_reader :logo_url
758
+ attr_reader :name
759
+ attr_reader :display_name
760
+ attr_reader :updated_at
761
+ attr_reader :created_at
762
+ end
763
+
764
+ class Video
765
+ def initialize(json, conn)
766
+ @conn = conn
767
+
768
+ @id = json['id']
769
+ @title = json['title']
770
+ @recorded_at = DateTime.parse(json['recorded_at'])
771
+ @url = json['url']
772
+ @view_count = json['views']
773
+ @description = json['description']
774
+ @length_sec = json['length']
775
+ @game_name = json['game']
776
+ @preview_url = json['preview']
777
+ @channel_name = json['channel']['name']
778
+ # @channel_display_name = json['channel']['display_name']
779
+ end
780
+
781
+ include IdEquality
782
+
783
+ def channel
784
+ Channel.new(@conn.get("channels/#{@channel_name}"), @conn)
785
+ end
786
+
787
+ attr_reader :id
788
+ attr_reader :title
789
+ attr_reader :recorded_at
790
+ attr_reader :url
791
+ attr_reader :view_count
792
+ attr_reader :description
793
+ # TODO: Is this actually in seconds? Doesn't seem to match up with video length.
794
+ attr_reader :length_sec
795
+ attr_reader :game_name
796
+ attr_reader :preview_url
797
+ # TODO: Move this under "v.channel.name" and force the query if other attributes are requested.
798
+ attr_reader :channel_name
799
+ end
800
+ end
801
+
802
+ =begin
803
+ k = Kappa::Client.new
804
+ v = k.video('a396294648')
805
+ =end
806
+
807
+ =begin
808
+ u.following.each do |channel|
809
+ puts channel.display_name
810
+ end
811
+ =end
812
+
813
+ =begin
814
+ streams = t.streams.where(:channel => ['psystarcraft', 'destiny', 'combatex', 'eghuk', 'eg_idra', 'day9tv', 'fxoqxc', 'colqxc', 'liquidnony', 'demuslim', 'whitera', 'khaldor', 'acermma'])
815
+ streams.each do |stream|
816
+ puts "#{stream.channel.display_name}: #{stream.viewer_count}"
817
+ puts "#{stream.channel.status}"
818
+ end
819
+ =end
820
+
821
+ =begin
822
+ streams = t.streams.where(:game => 'StarCraft II: Heart of the Swarm')
823
+ streams.each do |stream|
824
+ puts stream.channel.display_name
825
+ end
826
+ =end
827
+
828
+ =begin
829
+ puts t.stats.viewer_count
830
+ puts t.stats.stream_count
831
+ =end
832
+
833
+ =begin
834
+ s = t.streams.where(:channel => ['lagtvmaximusblack', 'sc2tv_ru'])
835
+ s.each do |stream|
836
+ puts stream.channel.name
837
+ end
838
+ =end
839
+
840
+ =begin
841
+ s = t.streams.featured
842
+ s.each do |stream|
843
+ puts stream.channel.name
844
+ end
845
+ =end
846
+
847
+ =begin
848
+ games = t.games.top(:limit => 150).sort_by(&:viewer_count).reverse
849
+ puts games.count
850
+
851
+ games.each do |game|
852
+ puts "#{game.name}: #{game.viewer_count}"
853
+ end
854
+ =end
855
+
856
+ #c = t.channel('lagtvmaximusblack')
857
+ #s = c.stream
858
+ #puts s.id
@@ -0,0 +1 @@
1
+ $version = '0.1.0.pre'
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kappa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Chris Schmich
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.9.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.9.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: addressable/uri
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 2.3.3
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.3
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0.9'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0.9'
78
+ description: ! " A Ruby library for interfacing with the Twitch.tv Kraken API\n
79
+ \ including users, channels, streams, and followers.\n"
80
+ email: schmch@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - lib/kappa/version.rb
86
+ - lib/kappa.rb
87
+ - README.md
88
+ homepage: https://github.com/schmich/kappa
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>'
104
+ - !ruby/object:Gem::Version
105
+ version: 1.3.1
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.24
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Ruby library for interfacing with the Twitch.tv Kraken API.
112
+ test_files: []