kappa 0.1.5.pre → 0.2.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.
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # <img src="https://raw.github.com/schmich/kappa/master/assets/kappa.png" /> Kappa
2
2
 
3
- Kappa is a Ruby library for interfacing with the [Twitch.tv API](https://github.com/justintv/Twitch-API).
3
+ Kappa is the Ruby library for interfacing with the [Twitch.tv API](https://github.com/justintv/Twitch-API).
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/kappa.png)](https://badge.fury.io/rb/kappa.png)
6
- [![Build Status](https://secure.travis-ci.org/schmich/tome.png)](http://travis-ci.org/schmich/tome)
5
+ [![Gem Version](https://badge.fury.io/rb/kappa.png)](http://rubygems.org/gems/kappa)
6
+ [![Build Status](https://secure.travis-ci.org/schmich/kappa.png)](http://travis-ci.org/schmich/kappa)
7
7
  [![Dependency Status](https://gemnasium.com/schmich/kappa.png)](https://gemnasium.com/schmich/kappa)
8
8
  [![Coverage Status](https://coveralls.io/repos/schmich/kappa/badge.png?branch=master)](https://coveralls.io/r/schmich/kappa?branch=master)
9
9
  [![Code Climate](https://codeclimate.com/github/schmich/kappa.png)](https://codeclimate.com/github/schmich/kappa)
10
10
 
11
11
  ## Getting Started
12
12
 
13
- `gem install kappa --pre`
13
+ `gem install kappa`
14
14
 
15
15
  ```ruby
16
16
  require 'kappa'
@@ -23,60 +23,124 @@ puts grubby.streaming?
23
23
 
24
24
  ## Examples
25
25
 
26
- ### Channels
26
+ ### <a id="channels"></a>Channels
27
27
 
28
28
  Channels serve as the home location for a [user's](#users) content. Channels have a [stream](#streams), can run commercials, store [videos](#videos), display information and status, and have a customized page including banners and backgrounds.
29
29
 
30
+ See also [`Kappa::V2::Channel`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Channel) documentation.
31
+
30
32
  ```ruby
31
33
  c = Channel.get('destiny')
32
-
33
- c.nil? # Does the requested channel not exist? (ex: false)
34
- c.name # Unique Twitch name (ex: "destiny")
35
- c.display_name # Display name, e.g. name used for page title (ex: "Destiny")
36
- c.stream # The currently live stream for this channel (ex: #<Kappa::V2::Stream> object)
37
- c.url # The URL for the channel's main page (ex: "http://www.twitch.tv/destiny")
38
- c.status # Current status (ex: "Destiny - Diamond I ADC - Number 1 Draven player in the entire Omaha (NE) metro area -watch from http://www.destiny.gg")
39
- c.streaming? # Is the channel currently streaming? (ex: true)
40
- c.game_name # Name of the primary game for this channel (ex: "League of Legends")
41
- c.mature? # Does the channel have mature content? (ex: true)
42
- c.id # Unique Twitch ID (ex: 18074328)
43
- c.created_at # When the channel was created (ex: #<DateTime: 2010-11-22T04:14:56+00:00 ((2455523j,15296s,0n),+0s,2299161j)>)
44
- c.updated_at # When the channel was last updated, e.g. last stream time (ex: #<DateTime: 2013-05-11T19:57:29+00:00 ((2456424j,71849s,0n),+0s,2299161j)>)
45
- c.background_url # URL for background image (ex: "http://static-cdn.jtvnw.net/jtv_user_pictures/destiny-channel_background_image-ab706db77853e079.jpeg")
46
- c.banner_url # URL for banner image (ex: "http://static-cdn.jtvnw.net/jtv_user_pictures/destiny-channel_header_image-d2b9b2452f67ec00-640x125.jpeg")
47
- c.logo_url # URL for logo image (ex: "http://static-cdn.jtvnw.net/jtv_user_pictures/destiny-profile_image-168e66661c484c5e-300x300.jpeg")
48
- c.video_banner_url # URL for the image shown when the stream is offline (ex: "http://static-cdn.jtvnw.net/jtv_user_pictures/destiny-channel_offline_image-7a21fd1bd88c4ac3-640x360.jpeg")
49
-
50
- c.videos
51
- c.teams
52
- c.subscribers
53
- c.editors
54
- c.followers
55
- c.has_subscriber?
34
+ c.nil? # => false (channel exists)
35
+ c.stream # => #<Kappa::V2::Stream> (current live stream)
36
+ c.url # => "http://www.twitch.tv/destiny"
37
+ c.status # => "Destiny - Diamond I ADC - Number 1 Draven player..."
38
+ c.teams # => [#<Kappa::V2::Team>]
39
+ c.videos # => [#<Kappa::V2::Video>, ...]
40
+ c.followers # => [#<Kappa::V2::User>, ...]
56
41
  ```
57
42
 
58
- ### Streams
43
+ ### <a id="streams"></a>Streams
44
+
45
+ Streams are video broadcasts that are currently live. They belong to a [user](#users) and are part of a [channel](#channels).
59
46
 
60
- Streams are video broadcasts that are currently live. They have a [broadcaster](#users) and are part of a [channel](#channels).
47
+ See also [`Kappa::V2::Stream`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Stream) and [`Kappa::V2::Streams`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Streams) documentation.
61
48
 
62
- ### Users
49
+ ```ruby
50
+ s = Stream.get('idrajit')
51
+ s.nil? # => false (currently live)
52
+ s.game_name # => "StarCraft II: Heart of the Swarm"
53
+ s.viewer_count # => 7267
54
+ s.channel.url # => "http://www.twitch.tv/idrajit"
55
+ ```
56
+
57
+ ### <a id="users"></a>Users
63
58
 
64
59
  These are members of the Twitch community who have a Twitch account. If broadcasting, they can own a [stream](#streams) that they can broadcast on their [channel](#channels). If mainly viewing, they might follow or subscribe to channels.
65
60
 
66
- ### Videos
61
+ See also [`Kappa::V2::User`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/User) documentation.
62
+
63
+ ```ruby
64
+ u = User.get('snoopeh')
65
+ u.nil? # => false (user exists)
66
+ u.channel # => #<Kappa::V2::Channel>
67
+ u.following.map(&:name) # => ["national_esl1", "dreamhacklol", "riotgames"]
68
+ ```
69
+
70
+ ### <a id="videos"></a>Videos
67
71
 
68
72
  Videos are broadcasts or highlights owned by a [channel](#channels). Broadcasts are unedited videos that are saved after a streaming session. Highlights are videos edited from broadcasts by the channel's owner.
69
73
 
70
- ### Teams
74
+ See also [`Kappa::V2::Video`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Video) and [`Kappa::V2::Videos`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Videos) documentation.
75
+
76
+ ```ruby
77
+ v = Video.get('a395995729')
78
+ v.nil? # => false (video exists)
79
+ v.title # => "DreamHack Open Stockholm 26-27 April"
80
+ v.game_name # => "StarCraft II: Heart of the Swarm"
81
+ v.recorded_at # => #<DateTime: 2013-04-26T18:33:48+00:00>
82
+ v.view_count # => 12506
83
+ ```
84
+
85
+ ### <a id="teams"></a>Teams
71
86
 
72
87
  Teams are an organization of [channels](#channels).
73
88
 
74
- ### Games
89
+ See also [`Kappa::V2::Team`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Team) documentation.
90
+
91
+ ```ruby
92
+ t = Team.get('teamliquid')
93
+ t.display_name # => "TeamLiquid"
94
+ t.info # => "TeamLiquid is awesome. and esports. video games. \n\n"
95
+ t.updated_at # => #<DateTime: 2013-04-27T16:58:55+00:00>
96
+ ```
97
+
98
+ ### <a id="games"></a>Games
75
99
 
76
100
  Games are categories (e.g. League of Legends, Diablo 3) used by [streams](#streams) and [channels](#channels). Games can be searched for by query.
77
101
 
102
+ See also [`Kappa::V2::Game`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Game), [`Kappa::V2::Games`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/Games), and [`Kappa::V2::GameSuggestion`](http://rdoc.info/github/schmich/kappa/master/Kappa/V2/GameSuggestion) documentation.
103
+
104
+ ```ruby
105
+ top = Games.top(:limit => 3)
106
+ top.map(&:name) # => ["League of Legends", "Dota 2", "StarCraft II: Heart of the Swarm"]
107
+ ```
108
+
109
+ ```ruby
110
+ g = Games.top(:limit => 1).first
111
+ g.name # => "League of Legends"
112
+ g.channel_count # => 906
113
+ g.viewer_count # => 79223
114
+ g.box_images.medium_url # =>"http://static-cdn.jtvnw.net/ttv-boxart/League%20of%20Legends-272x380.jpg"
115
+ ```
116
+
117
+ ```ruby
118
+ s = Games.find(:name => 'diablo', :live => true)
119
+ s.map(&:name) # => ["Diablo III", "Diablo II", "Diablo", "Diablo II: Lord of Destruction"]
120
+ s.map(&:popularity) # => [120, 4, 1, 1]
121
+ ```
122
+
123
+ ## Documentation
124
+
125
+ Detailed API documentation is available at [http://rdoc.info/github/schmich/kappa/master/frames](http://rdoc.info/github/schmich/kappa/master/frames).
126
+
127
+ ## Versioning
128
+
129
+ - TODO: Semantic versioning
130
+ - TODO: Twitch API versions
131
+
78
132
  ## Contributing
79
133
 
134
+ - [Fork and clone the repo.](http://help.github.com/fork-a-repo/)
135
+ - [Create a branch for your changes.](http://learn.github.com/p/branching.html)
136
+ - Run `bundle install` to install development requirements.
137
+ - Implement your feature or bug fix.
138
+ - Add specs under the `spec` folder to prevent regressions or to test new code.
139
+ - Add [YARD](http://rubydoc.info/docs/yard/file/docs/GettingStarted.md) documentation for new features. Run `rake yard` to view documentation.
140
+ - Run `rake coverage` to run specs with code coverage. All specs must pass; coverage must remain at 100%.
141
+ - Commit and push your changes.
142
+ - [Submit a pull request.](http://help.github.com/send-pull-requests/)
143
+
80
144
  ## License
81
145
 
82
146
  Copyright &copy; 2013 Chris Schmich
data/lib/kappa.rb CHANGED
@@ -9,81 +9,3 @@ require 'kappa/user'
9
9
  require 'kappa/images'
10
10
  require 'kappa/twitch'
11
11
  require 'kappa/version'
12
-
13
- # TODO
14
- # https://github.com/justintv/Twitch-API
15
- # Blocks
16
- # GET /users/:login/blocks
17
- # PUT /users/:user/blocks/:target
18
- # DELETE /users/:user/blocks/:target
19
- # Channels
20
- # - GET /channels/:channel
21
- # GET /channel
22
- # GET /channels/:channel/editors
23
- # PUT /channels/:channel
24
- # GET /channels/:channel/videos
25
- # GET /channels/:channel/follows
26
- # DELETE /channels/:channel/stream_key
27
- # POST /channels/:channel/commercial
28
- # Chat
29
- # GET /chat/:channel
30
- # GET /chat/emoticons
31
- # Follows
32
- # GET /channels/:channel/follows
33
- # GET /users/:user/follows/channels
34
- # GET /users/:user/follows/channels/:target
35
- # PUT /users/:user/follows/channels/:target
36
- # DELETE /users/:user/follows/channels/:target
37
- # Games
38
- # - GET /games/top
39
- # Ingests
40
- # GET /ingests
41
- # Root
42
- # GET /
43
- # Search
44
- # GET /search/streams
45
- # - GET /search/games
46
- # Streams
47
- # - GET /streams/:channel
48
- # - GET /streams
49
- # GET /streams/featured
50
- # GET /streams/summary
51
- # GET /streams/followed
52
- # Subscriptions
53
- # GET /channels/:channel/subscriptions
54
- # GET /channels/:channel/subscriptions/:user
55
- # Teams
56
- # GET /teams
57
- # GET /teams/:team
58
- # Users
59
- # GET /users/:user
60
- # GET /user
61
- # GET /streams/followed
62
- # Videos
63
- # GET /videos/:id
64
- # GET /videos/top
65
- # GET /channels/:channel/videos
66
-
67
- # Overarching
68
- # - Common query syntax
69
- # - Access to raw properties (e.g. stream['game'] or stream.raw('game'))
70
- # - Paginated results take a block to allow for intermediate processing/termination
71
-
72
- # t = Kappa::Client.new
73
- # c = t.channel('lagtvmaximusblack')
74
- # c.editors -> [...]
75
- # c.videos -> [...]
76
- # c.followers -> [...]
77
- # c.subscriptions
78
- # c.start_commercial
79
- # c.reset_stream_key
80
- # c... ; c.save!
81
- # TODO: current user channel
82
-
83
- # t = Kappa::Client.new
84
- # t.streams.all
85
- # t.streams.all(:limit => 10)
86
- # t.streams.featured
87
- # t.streams.where(:channel => 'lagtvmaximusblack')
88
- # t.streams.where(:channel => [...], :game => '...', :embeddable => t/f, :hls => t/f)
89
- # t.stream_summary
data/lib/kappa/channel.rb CHANGED
@@ -1,24 +1,13 @@
1
1
  require 'cgi'
2
2
 
3
- module Kappa
4
- # @private
5
- class ChannelBase
6
- include IdEquality
7
-
8
- def self.get(channel_name)
9
- encoded_name = CGI.escape(channel_name)
10
- json = connection.get("channels/#{encoded_name}")
11
- if !json || json['status'] == 404
12
- nil
13
- else
14
- new(json)
15
- end
16
- end
17
- end
18
- end
19
-
20
3
  module Kappa::V2
21
- class Channel < Kappa::ChannelBase
4
+ # Channels serve as the home location for a user's content. Channels have a stream, can run
5
+ # commercials, store videos, display information and status, and have a customized page including
6
+ # banners and backgrounds.
7
+ # @see .get Channel.get
8
+ # @see Stream
9
+ # @see User
10
+ class Channel
22
11
  # TODO:
23
12
  # c.subscriptions
24
13
  # c.start_commercial
@@ -26,7 +15,9 @@ module Kappa::V2
26
15
  # c.foo = 'bar' ; c.save!
27
16
  # Current user's channel
28
17
  include Connection
18
+ include Kappa::IdEquality
29
19
 
20
+ # @private
30
21
  def initialize(hash)
31
22
  @id = hash['_id']
32
23
  @background_url = hash['background']
@@ -49,7 +40,22 @@ module Kappa::V2
49
40
  end
50
41
  end
51
42
 
52
- # This flag is specified by the owner of the channel.
43
+ # Get a channel by name.
44
+ # @param channel_name [String] The name of the channel to get. This is the same as the stream or user name.
45
+ # @return [Channel] A valid `Channel` object if the channel exists, `nil` otherwise.
46
+ def self.get(channel_name)
47
+ encoded_name = CGI.escape(channel_name)
48
+ json = connection.get("channels/#{encoded_name}")
49
+
50
+ # HTTP 422 can happen if the channel is associated with a Justin.tv account.
51
+ if !json || json['status'] == 404 || json['status'] == 422
52
+ nil
53
+ else
54
+ new(json)
55
+ end
56
+ end
57
+
58
+ # Does this channel have mature content? This flag is specified by the owner of the channel.
53
59
  # @return [Boolean] `true` if the channel has mature content, `false` otherwise.
54
60
  def mature?
55
61
  @mature
@@ -62,8 +68,8 @@ module Kappa::V2
62
68
  Stream.get(@name)
63
69
  end
64
70
 
65
- # This makes a separate request to get the channel's stream. If you want to actually use
66
- # the stream object, you should call `#stream` instead.
71
+ # Does this channel currently have a live stream?
72
+ # @note This makes a separate request to get the channel's stream. If you want to actually use the stream object, you should call `#stream` instead.
67
73
  # @return [Boolean] `true` if the channel currently has a live stream, `false` otherwise.
68
74
  # @see #stream
69
75
  def streaming?
@@ -74,6 +80,8 @@ module Kappa::V2
74
80
  # GET /channels/:channel/editors
75
81
  # https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschanneleditors
76
82
  #
83
+ # @private
84
+ # Private until implemented.
77
85
  def editors
78
86
  # TODO
79
87
  end
@@ -82,19 +90,41 @@ module Kappa::V2
82
90
  # GET /channels/:channels/videos
83
91
  # https://github.com/justintv/Twitch-API/blob/master/v2_resources/videos.md#get-channelschannelvideos
84
92
  #
93
+ # @private
94
+ # Private until implemented.
85
95
  def videos(params = {})
86
96
  # TODO
87
97
  end
88
98
 
99
+ # TODO: Requires authentication.
100
+ # @private
101
+ # Private until implemented.
102
+ def subscribers
103
+ end
104
+
89
105
  #
90
- # GET /channels/:channel/follows
91
- # https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannelfollows
92
- # TODO: Warning: this set can be very large, this can run for very long time, recommend using :limit/:offset.
106
+ # GET /channels/:channel/subscriptions/:user
107
+ # https://github.com/justintv/Twitch-API/blob/master/v2_resources/subscriptions.md#get-channelschannelsubscriptionsuser
93
108
  #
94
- def followers(args = {})
109
+ # TODO: Requires authentication.
110
+ # @private
111
+ # Private until implemented.
112
+ def has_subscriber?(user)
113
+ # Support User object or username (string)
114
+ end
115
+
116
+ # Get the users following this channel.
117
+ # @note The number of followers is potentially very large, so it's recommended that you specify a `:limit`.
118
+ # @param options [Hash] Filter criteria.
119
+ # @option options [Fixnum] :limit (none) Limit on the number of results returned.
120
+ # @option options [Fixnum] :offset (0) Offset into the result set to begin enumeration.
121
+ # @see User
122
+ # @see https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannelfollows GET /channels/:channel/follows
123
+ # @return [Array<User>] List of users following this channel.
124
+ def followers(options = {})
95
125
  params = {}
96
126
 
97
- limit = args[:limit]
127
+ limit = options[:limit]
98
128
  if limit && (limit < 100)
99
129
  params[:limit] = limit
100
130
  else
@@ -112,19 +142,6 @@ module Kappa::V2
112
142
  )
113
143
  end
114
144
 
115
- # TODO: Requires authentication.
116
- def subscribers
117
- end
118
-
119
- #
120
- # GET /channels/:channel/subscriptions/:user
121
- # https://github.com/justintv/Twitch-API/blob/master/v2_resources/subscriptions.md#get-channelschannelsubscriptionsuser
122
- #
123
- # TODO: Requires authentication.
124
- def has_subscriber?(user)
125
- # Support User object or username (string)
126
- end
127
-
128
145
  # @return [Fixnum] Unique Twitch ID.
129
146
  attr_reader :id
130
147
 
@@ -137,7 +154,7 @@ module Kappa::V2
137
154
  # @return [DateTime] When the channel was created.
138
155
  attr_reader :created_at
139
156
 
140
- # @return [String] Display name, e.g. name used for page title.
157
+ # @return [String] User-friendly display name. This name is used for the channel's page title.
141
158
  attr_reader :display_name
142
159
 
143
160
  # @return [String] Name of the primary game for this channel.
@@ -149,10 +166,10 @@ module Kappa::V2
149
166
  # @return [String] Unique Twitch name.
150
167
  attr_reader :name
151
168
 
152
- # @return [String] Current status.
169
+ # @return [String] Current status set by the channel's owner.
153
170
  attr_reader :status
154
171
 
155
- # @return [DateTime] When the channel was last updated, e.g. last stream time.
172
+ # @return [DateTime] When the channel was last updated. When a stream is started, its channel is updated.
156
173
  attr_reader :updated_at
157
174
 
158
175
  # @return [String] The URL for the channel's main page.
@@ -161,6 +178,7 @@ module Kappa::V2
161
178
  # @return [String] URL for the image shown when the stream is offline.
162
179
  attr_reader :video_banner_url
163
180
 
181
+ # @return [Array<Team>] The list of teams that this channel is associated with. Not all channels have associated teams.
164
182
  attr_reader :teams
165
183
  end
166
184
  end
@@ -13,11 +13,13 @@ module Kappa
13
13
  def initialize(base_url = DEFAULT_BASE_URL)
14
14
  @base_url = Addressable::URI.parse(base_url)
15
15
 
16
+ # TODO: Expose client_id so clients of the library can (optionally) set this
17
+ # themselves and avoid rate limiting. Clients should still have the option to
18
+ # not set this and use a randomly generated ID.
19
+
16
20
  uuid = SecureRandom.uuid
17
21
  # TODO: Use current library version.
18
22
  @client_id = "Kappa-v1-#{uuid}"
19
-
20
- @last_request_time = Time.now - RATE_LIMIT_SEC
21
23
  end
22
24
 
23
25
  def get(path, query = nil)
@@ -27,25 +29,26 @@ module Kappa
27
29
  'Client-ID' => @client_id,
28
30
  }.merge(custom_headers)
29
31
 
30
- response = rate_limit do
31
- self.class.get(request_url, :headers => headers, :query => query)
32
- end
32
+ response = self.class.get(request_url, :headers => headers, :query => query)
33
33
 
34
34
  # TODO: Handle non-JSON response
35
35
  # TODO: Handle invalid JSON
36
36
  # TODO: Handle non-200 codes
37
+ # TODO: Include HTTP status code in the return value
37
38
 
38
39
  json = response.body
39
40
  return JSON.parse(json)
40
41
  end
41
42
 
42
- def accumulate(args)
43
- path = args[:path]
44
- params = args[:params]
45
- json = args[:json]
46
- sub_json = args[:sub_json]
47
- klass = args[:class]
48
- limit = args[:limit]
43
+ def accumulate(options)
44
+ path = options[:path]
45
+ params = options[:params] || {}
46
+ json = options[:json]
47
+ sub_json = options[:sub_json]
48
+ klass = options[:class]
49
+
50
+ total_limit = options[:limit]
51
+ page_limit = params[:limit] || 100
49
52
 
50
53
  objects = []
51
54
  ids = Set.new
@@ -57,13 +60,13 @@ module Kappa
57
60
  object = klass.new(object_json)
58
61
  if ids.add?(object.id)
59
62
  objects << object
60
- if objects.count == limit
63
+ if objects.count == total_limit
61
64
  return objects
62
65
  end
63
66
  end
64
67
  end
65
68
 
66
- !current_objects.empty?
69
+ !current_objects.empty? && (current_objects.count >= page_limit)
67
70
  end
68
71
 
69
72
  return objects
@@ -106,20 +109,6 @@ module Kappa
106
109
  end
107
110
 
108
111
  private
109
- def rate_limit
110
- delta = Time.now - @last_request_time
111
- delay = [RATE_LIMIT_SEC - delta, 0].max
112
-
113
- sleep delay if delay > 0
114
-
115
- begin
116
- return yield
117
- ensure
118
- @last_request_time = Time.now
119
- end
120
- end
121
-
122
- RATE_LIMIT_SEC = 1
123
112
  DEFAULT_BASE_URL = 'https://api.twitch.tv/kraken/'
124
113
  end
125
114
  end