twitchy 0.1.1
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 +7 -0
- data/README.md +28 -0
- data/bin/twitchy +31 -0
- data/lib/twitchy/dstruct.rb +29 -0
- data/lib/twitchy/livestreamer.rb +28 -0
- data/lib/twitchy/twitch_api.rb +72 -0
- data/lib/twitchy/twitchy.rb +278 -0
- data/twitchy.gemspec +24 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31af3d06423eafae96cc9dbf8b82607916ce868d
|
4
|
+
data.tar.gz: 665136e25a38df7a546bc6f9be18e568bfdc5fb3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c8dc487aa64b1cb843c42f742014d0c27f7c3f53feace988bd4864edf44716043f89d8843f270ac02f9544373d5e9015b861b1a4d622e0f75152befa27e8ef4
|
7
|
+
data.tar.gz: 742a133b912a3bc5b290d301fafd219285856820fd8ce3e618c23aa8e9b2f4d97fa6c90df5a4bbbd80901303cf44166dd88d6cf0b63fc496f0ad40a5163a8758
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# twitchy
|
2
|
+
|
3
|
+
A little Ruby wrapper around livestreamer
|
4
|
+
|
5
|
+
## Documentation
|
6
|
+
|
7
|
+
```
|
8
|
+
Usage: twitchy [options] [channel ..]
|
9
|
+
-p, --player PLAYER Set video player
|
10
|
+
-c, --chat Open popout chat with the stream
|
11
|
+
-u, --user USER Show subs for a user
|
12
|
+
-q, --quality QUALITY Set desired quality
|
13
|
+
-v, --videos List archives instead of streams
|
14
|
+
-l, --limit LIMIT Number to fetch at once
|
15
|
+
-g, --game GAME Add top streamers for GAME
|
16
|
+
--highlights Only show highlights when fetching videos
|
17
|
+
-h, --help Show this message
|
18
|
+
```
|
19
|
+
## Dependencies
|
20
|
+
|
21
|
+
Requires [`livestreamer`](https://github.com/chrippa/livestreamer) (obviously), and currently the [`colorize`](https://github.com/fazibear/colorize) gem.
|
22
|
+
|
23
|
+
## Todo
|
24
|
+
|
25
|
+
I plan on gemifying this soon. Also, a lot of usage is unclear (pagination in
|
26
|
+
archives, quality options, https://github.com/fazibear/colorizeetc).
|
27
|
+
|
28
|
+
I have greatly reduced API requests from earlier versions, but I'm always keeping an eye out for ways to bring the number of those calls down further. There's a way to get all the videos from subscriptions of a user, but it requires authentication and isn't very flexible, for example, so right now I'm making calls for each requested channel.
|
data/bin/twitchy
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'twitchy/twitchy'
|
4
|
+
|
5
|
+
trap ("SIGINT") { exit! }
|
6
|
+
|
7
|
+
t = Twitchy.new(ARGV)
|
8
|
+
|
9
|
+
if t.streamers.empty?
|
10
|
+
if t.options.user
|
11
|
+
puts "User follows no streamers"
|
12
|
+
else
|
13
|
+
puts t.banner
|
14
|
+
end
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
if t.options.videos
|
19
|
+
t.get_archives
|
20
|
+
else
|
21
|
+
t.check_status
|
22
|
+
if t.online.empty?
|
23
|
+
puts "All streamers are offline"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
t.puts_streams
|
29
|
+
t.get_choice
|
30
|
+
|
31
|
+
#vim: ft=rb
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class DeepStruct < OpenStruct
|
4
|
+
def initialize(hash=nil)
|
5
|
+
@table = {}
|
6
|
+
@hash_table = {}
|
7
|
+
|
8
|
+
if hash
|
9
|
+
hash.each do |k,v|
|
10
|
+
@table[k.to_sym] = v
|
11
|
+
|
12
|
+
@table[k.to_sym] = v.map do |o|
|
13
|
+
o.is_a?(Hash) ? self.class.new(o) : o
|
14
|
+
end if v.is_a? Array
|
15
|
+
|
16
|
+
@table[k.to_sym] = self.class.new(v) if v.is_a? Hash
|
17
|
+
|
18
|
+
@hash_table[k.to_sym] = v
|
19
|
+
|
20
|
+
new_ostruct_member(k)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_h
|
26
|
+
@hash_table
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Livestreamer
|
4
|
+
@@sort = ["best","source","high","medium","low","mobile","worst"]
|
5
|
+
|
6
|
+
def self.get_available_streams(channel)
|
7
|
+
@@sort & JSON.parse(`livestreamer -j twitch.tv/#{channel}`)["streams"].keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.start_stream(channel, player, quality, chat)
|
11
|
+
stream = "livestreamer -Q -p '#{player}' "\
|
12
|
+
"twitch.tv/#{channel} #{quality} & "
|
13
|
+
popout_chat = "firefox -new-window "\
|
14
|
+
"'http://www.twitch.tv/chat/embed"\
|
15
|
+
"?channel=#{channel}&popout_chat=true' "\
|
16
|
+
"&> /dev/null & "
|
17
|
+
cmd = stream
|
18
|
+
cmd += popout_chat if chat
|
19
|
+
|
20
|
+
exec cmd
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.start_video(url, player)
|
24
|
+
exec "livestreamer -Q -p '#{player}' "\
|
25
|
+
"#{url} best &"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'twitchy/dstruct'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module TwitchAPI
|
6
|
+
@@PREFIX = "https://api.twitch.tv/kraken/"
|
7
|
+
|
8
|
+
def self.get_follow_data(user)
|
9
|
+
catch_exception(OpenURI::HTTPError) do
|
10
|
+
get_as_struct("users/#{user}/follows/channels").follows
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_stream_status(streamers)
|
15
|
+
query = "streams?channel=#{streamers.join(",")}"
|
16
|
+
catch_exception(OpenURI::HTTPError, default: {}) do
|
17
|
+
get_as_struct(query).streams.map do |s|
|
18
|
+
[s.channel.name, s]
|
19
|
+
end.to_h
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.get_stream_data(streamer)
|
24
|
+
catch_exception(OpenURI::HTTPError) do
|
25
|
+
get_as_struct("streams/#{streamer}").stream
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_videos(streamer, limit: 10, offset: 0, highlights: false)
|
30
|
+
catch_exception(OpenURI::HTTPError) do
|
31
|
+
query = "channels/#{streamer}/videos"\
|
32
|
+
"?broadcasts=#{!highlights}"\
|
33
|
+
"&limit=#{limit}&offset=#{offset}"
|
34
|
+
get_as_struct(query).videos.each do |v|
|
35
|
+
v.recorded_at = DateTime.parse(v.recorded_at)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.get_streamers_for_game(game, limit: 10, offset: 0)
|
41
|
+
catch_exception(OpenURI::HTTPError) do
|
42
|
+
query = "streams?game=#{URI.encode(game)}&limit=#{limit}&offset=#{offset}"
|
43
|
+
get_as_struct(query).streams.map{|s| s.channel.name}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def self.catch_exception(exception, default: nil, &block)
|
49
|
+
if block
|
50
|
+
begin
|
51
|
+
yield
|
52
|
+
rescue exception
|
53
|
+
default
|
54
|
+
end
|
55
|
+
else
|
56
|
+
default
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.get(request)
|
61
|
+
open( @@PREFIX + request ).each_line.to_a.join
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.get_as_json(request)
|
65
|
+
JSON.parse(get(request))
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.get_as_struct(request)
|
69
|
+
DeepStruct.new(get_as_json(request))
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'optparse'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'twitchy/livestreamer'
|
5
|
+
require 'twitchy/twitch_api'
|
6
|
+
|
7
|
+
class Twitchy
|
8
|
+
attr_reader :options, :streamers, :online
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
parse_options(args)
|
12
|
+
|
13
|
+
@streamers = args.map{|s| s.dup}
|
14
|
+
@streamers += fetch_subs(@options.user) if @options.user
|
15
|
+
@streamers += TwitchAPI.get_streamers_for_game(
|
16
|
+
@options.game, limit: @options.limit
|
17
|
+
) if @options.game
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_options(args)
|
21
|
+
@options = OpenStruct.new(
|
22
|
+
player: "vlc --no-video-title-show",
|
23
|
+
quality: "best",
|
24
|
+
chat: false,
|
25
|
+
videos: false,
|
26
|
+
limit: 20,
|
27
|
+
highlights: false,
|
28
|
+
game: nil
|
29
|
+
)
|
30
|
+
@optparser = OptionParser.new do |opts|
|
31
|
+
opts.banner = "Usage: twitchy [options] [channel ..]"
|
32
|
+
|
33
|
+
opts.on("-p", "--player PLAYER", "Set video player") do |p|
|
34
|
+
@options.player = p
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-c", "--chat", "Open popout chat with the stream") do
|
38
|
+
@options.chat = true
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("-u", "--user USER", "Show subs for a user") do |u|
|
42
|
+
@options.user = u
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("-q", "--quality QUALITY", "Set desired quality") do |q|
|
46
|
+
@options.quality = q
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("-v", "--videos", "List archives instead of streams") do
|
50
|
+
@options.videos = true
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-l", "--limit LIMIT", Integer, "Number to fetch at once") do |l|
|
54
|
+
@options.limit = l
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-g", "--game GAME", "Add top streamers for GAME") do |g|
|
58
|
+
@options.game = g
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--highlights", "Only show highlights when fetching videos") do
|
62
|
+
@options.highlights = true
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
66
|
+
puts opts
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
@optparser.parse!(args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def fetch_subs(user)
|
75
|
+
puts "Fetching subscriptions".bold
|
76
|
+
TwitchAPI.get_follow_data(user).map do |follow|
|
77
|
+
follow.channel.name
|
78
|
+
end.reverse!
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_status
|
82
|
+
puts "Checking streamer status...".bold
|
83
|
+
@online = TwitchAPI.get_stream_status(@streamers)
|
84
|
+
@online.each do |streamer, data|
|
85
|
+
print "Getting quality options for #{streamer} "
|
86
|
+
data.streams = Livestreamer.get_available_streams(streamer)
|
87
|
+
clearln
|
88
|
+
end
|
89
|
+
@online_streamers = @online.keys.sort_by!{|s| @online[s].viewers}.reverse!
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_archives
|
93
|
+
puts "Requesting archives".bold
|
94
|
+
@archives = []
|
95
|
+
@archive_count = Hash.new(0)
|
96
|
+
streamers.each do |streamer|
|
97
|
+
print streamer
|
98
|
+
vods = TwitchAPI.get_videos(
|
99
|
+
streamer, limit: @options.limit, highlights: @options.highlights
|
100
|
+
)
|
101
|
+
@archives += vods
|
102
|
+
@archive_count[streamer] += vods.to_a.size
|
103
|
+
clearln
|
104
|
+
puts streamer.green
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def banner
|
110
|
+
@optparser.help()
|
111
|
+
end
|
112
|
+
|
113
|
+
def clearln
|
114
|
+
print "\r\e[K"
|
115
|
+
end
|
116
|
+
|
117
|
+
def puts_streams(offset: 0)
|
118
|
+
puts
|
119
|
+
puts "Streams available:".bold
|
120
|
+
index = 0
|
121
|
+
|
122
|
+
if @options.videos
|
123
|
+
@archives.sort_by!{|v| v.recorded_at}.reverse!
|
124
|
+
@archives.drop(offset).take(@options.limit).each do |video|
|
125
|
+
streamer = video.channel.display_name
|
126
|
+
index += 1
|
127
|
+
puts_stream_info(index, streamer, video.title.to_s, video.game.to_s, time: video.length)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
@online_streamers.each do |streamer|
|
131
|
+
info = @online[streamer].channel
|
132
|
+
streamer_name = info.display_name
|
133
|
+
index += 1
|
134
|
+
qualities = @online[streamer].streams
|
135
|
+
qualities = nil if qualities.include? @options.quality
|
136
|
+
puts_stream_info(index, streamer_name, info.status.to_s, info.game.to_s, qualities: qualities)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def puts_stream_info(index, streamer, title, game, qualities: nil, time: nil)
|
142
|
+
def abbrev(str, length=77)
|
143
|
+
short = str.chomp[0...length]
|
144
|
+
short += "..." if str.length > 77
|
145
|
+
short
|
146
|
+
end
|
147
|
+
title = abbrev(title)
|
148
|
+
puts "#{index.to_s.bold}. #{streamer} - "\
|
149
|
+
"#{"(#{format_time(time)}) " if time}"\
|
150
|
+
"'#{title.sub(game, game.underline)}' "\
|
151
|
+
"#{"playing #{game.underline}" unless title.include? game if game}"
|
152
|
+
puts " [ #{qualities.map{ |q|
|
153
|
+
if q == @options.quality then q.bold else q end
|
154
|
+
}.join(' | ')} ]" if qualities
|
155
|
+
end
|
156
|
+
|
157
|
+
def get_choice
|
158
|
+
if @options.videos
|
159
|
+
get_video_choice
|
160
|
+
else
|
161
|
+
get_stream_choice
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_stream_choice
|
166
|
+
prompt = "Select stream number [and quality] to watch: ".bold
|
167
|
+
print prompt
|
168
|
+
|
169
|
+
loop do
|
170
|
+
response = ($stdin.gets || "").chomp.split
|
171
|
+
|
172
|
+
response, quality = case response.length
|
173
|
+
when 0
|
174
|
+
["", nil]
|
175
|
+
when 1
|
176
|
+
[response[0], @options.quality]
|
177
|
+
else
|
178
|
+
response[0,2]
|
179
|
+
end
|
180
|
+
|
181
|
+
break if response.downcase == "q"
|
182
|
+
|
183
|
+
choice = Integer(response) rescue nil
|
184
|
+
if choice && choice > 0 && choice <= @online_streamers.length
|
185
|
+
streamer = @online_streamers[choice - 1]
|
186
|
+
if @online[streamer].streams.include? quality
|
187
|
+
launch_stream(streamer, quality)
|
188
|
+
else
|
189
|
+
print "Requested quality not available, options are "
|
190
|
+
puts "[#{@online[streamer].streams.join(", ")}]"
|
191
|
+
end
|
192
|
+
else
|
193
|
+
print "Invalid choice, try again"
|
194
|
+
end
|
195
|
+
|
196
|
+
#print "\r\e[A\e[K\r" + prompt
|
197
|
+
print prompt
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def get_video_choice
|
202
|
+
prompt = "Select stream number to watch: ".bold
|
203
|
+
print prompt
|
204
|
+
offset = 0
|
205
|
+
streamer_offset = Hash.new{0}
|
206
|
+
loop do
|
207
|
+
response = $stdin.gets.chomp
|
208
|
+
|
209
|
+
break if response.downcase == "q"
|
210
|
+
if response == ":n" or response == ":next"
|
211
|
+
puts "Getting next page".bold
|
212
|
+
streamer_count = @archives.drop(offset).take(@options.limit).group_by{|v| v.channel.name}.map{|k,v| [k, v.size]}
|
213
|
+
offset += @options.limit
|
214
|
+
streamer_count.each do |s, o|
|
215
|
+
streamer_offset[s] += o
|
216
|
+
if streamer_offset[s] + @options.limit > @archive_count[s]
|
217
|
+
@archives += TwitchAPI.get_videos(
|
218
|
+
s, limit: o, offset: streamer_offset[s]
|
219
|
+
)
|
220
|
+
@archive_count[s] += o
|
221
|
+
end
|
222
|
+
end
|
223
|
+
puts_streams(offset: offset)
|
224
|
+
print prompt
|
225
|
+
next
|
226
|
+
end
|
227
|
+
|
228
|
+
if response == ":b" or response == ":back"
|
229
|
+
puts "Getting previous page".bold
|
230
|
+
streamer_count = @archives.drop(offset).take(@options.limit).group_by{|v| v.channel.name}.map{|k,v| [k, v.size]}
|
231
|
+
streamer_count.each do |s, o|
|
232
|
+
streamer_offset[s] -= o
|
233
|
+
streamer_offset[s] = 0 if streamer_offset[s] < 0
|
234
|
+
end
|
235
|
+
offset -= @options.limit
|
236
|
+
offset = 0 if offset < 0
|
237
|
+
puts_streams(offset: offset)
|
238
|
+
print prompt
|
239
|
+
next
|
240
|
+
end
|
241
|
+
|
242
|
+
choice = Integer(response) rescue nil
|
243
|
+
if choice && 0 < choice && choice <= 20
|
244
|
+
stream = @archives[offset + choice - 1]
|
245
|
+
launch_video(stream.url)
|
246
|
+
else
|
247
|
+
print "Invalid choice, try again"
|
248
|
+
end
|
249
|
+
|
250
|
+
print "\r\e[A\e[K\r" + prompt
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def launch_stream(streamer, quality)
|
255
|
+
opts = @options.dup
|
256
|
+
opts.quality = quality
|
257
|
+
Livestreamer.start_stream(
|
258
|
+
streamer, @options.player, @options.quality, @options.chat
|
259
|
+
)
|
260
|
+
end
|
261
|
+
|
262
|
+
def launch_video(url)
|
263
|
+
Livestreamer.start_video(url, @options.player)
|
264
|
+
end
|
265
|
+
|
266
|
+
def format_time(seconds)
|
267
|
+
s = seconds % 60
|
268
|
+
minutes = seconds / 60
|
269
|
+
m = minutes % 60
|
270
|
+
h = minutes / 60
|
271
|
+
if h > 0
|
272
|
+
"%d:%02d:%02d" % [h,m,s]
|
273
|
+
else
|
274
|
+
"%d:%02d" % [m,s]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
data/twitchy.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'twitchy'
|
3
|
+
s.version = '0.1.1'
|
4
|
+
s.date = '2014-02-11'
|
5
|
+
s.summary = "A Ruby wrapper around livestreamer"
|
6
|
+
s.description = "Twitchy provides for a system to query the TwitchAPI "\
|
7
|
+
"for use with livestreamer. List online channels, "\
|
8
|
+
"archived videos, and more."
|
9
|
+
s.authors = ["blkbsstt"]
|
10
|
+
s.email = 'blkbsstt+twitchy@gmail.com'
|
11
|
+
s.executables << 'twitchy'
|
12
|
+
s.files = [
|
13
|
+
"README.md",
|
14
|
+
"twitchy.gemspec",
|
15
|
+
"lib/twitchy/twitchy.rb",
|
16
|
+
"lib/twitchy/livestreamer.rb",
|
17
|
+
"lib/twitchy/dstruct.rb",
|
18
|
+
"lib/twitchy/twitch_api.rb",
|
19
|
+
"bin/twitchy"
|
20
|
+
]
|
21
|
+
s.add_runtime_dependency "colorize", "~> 0.6"
|
22
|
+
s.homepage = 'http://www.github.com/blkbsstt/twitchy'
|
23
|
+
s.license = 'MIT'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twitchy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- blkbsstt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
description: Twitchy provides for a system to query the TwitchAPI for use with livestreamer.
|
28
|
+
List online channels, archived videos, and more.
|
29
|
+
email: blkbsstt+twitchy@gmail.com
|
30
|
+
executables:
|
31
|
+
- twitchy
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- README.md
|
36
|
+
- bin/twitchy
|
37
|
+
- lib/twitchy/dstruct.rb
|
38
|
+
- lib/twitchy/livestreamer.rb
|
39
|
+
- lib/twitchy/twitch_api.rb
|
40
|
+
- lib/twitchy/twitchy.rb
|
41
|
+
- twitchy.gemspec
|
42
|
+
homepage: http://www.github.com/blkbsstt/twitchy
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.2.2
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: A Ruby wrapper around livestreamer
|
66
|
+
test_files: []
|