Update: It appears that Twitch has capped views to ten per IP. While this method still works, you’ll need to supplement it with proxies or multiple IP’s. It’s still a good read though 🙂
An intro to Twitch:
Twitch is the largest video game broadcasting community. Most professional gamers live stream onto Twitch and almost every major eSporting event is broadcast through Twitch. There are hundreds of thousands of fans at any given time, all watching live streams.
Since there are hundreds of broadcasters simultaneously streaming, only the top broadcasters get featured on the first page of the channel browser. This position is determined by the number of live viewers watching the live stream. As you can see in the picture below, if you are not ranked in the top 7, you get put in the ominous “View All” button.
In most cases, only the well known broadcasters (usually pro-gamers with large fan bases) are featured on the front page, with all the others hidden away. Because of this, it is extremely hard for new streamers to get their content featured and get more fans. This is a huge catch-22, but according to Twitch, it’s the best way to ensure that only good content gets displayed.
Reverse Engineering Twitch’s View Counter
Although I do not personally play video games or broadcast on Twitch, I wanted to see if there was a way to fake the number of live viewers on a stream in order to be featured on the front page.
The first thing I tried was just to open a stream on different web browsers and private browsing/incognito. As it turns out, it worked. From that, I was fairly certain that views could be faked on a single computer.
The easy way to fake views would just be to make a program that opens a thousand tabs of the live stream, but that would be very resource intensive. Each page load is upwards of 3 MB and there’s the obvious problem of having a lot of live video streams playing at the same time. The bandwidth requirement would be too high.
The better way, of course, is to find out what mechanism keeps track of views. When a stream is loaded with Chrome dev tools open, I found queries to many hostnames, like mp.twitch.tv, usher.twitch.tv, api.twitch.tv, etc… To narrow down the results, I decided to block these hostnames one at a time to see if they were important. I ended up with a few required ones, namely usher.twitch.tv. Requests sent to this hostname returned “tokens”, which I assumed were session variables. Doing some quick Google searching reveals that usher.twitch.tv is used by many 3rd party programs to play Twitch broadcasts.
The program I ended up using is called livestreamer, which is a pip module used to launch streams in VLC player. What’s great about livestreamer is that it queries Twitch’s server and is able to return the result in json format. In this data is a URL that contains data about the video chunks of the live stream.
Faking viewers on Twitch
When a request is sent to the URL received from livestreamer, Twitch thinks a client is watching the live stream. With this in mind, I wrote a simple Python script that gets builds Twitch viewing tokens and queries using a HEAD request to mimic a viewer using the lowest amount of bandwidth possible.
In initial tests, I was only able to fake about ~100 users. But tweaking the number of concurrent threads yielded significant results.
To fake 1000 users using this script only took about 200 KB/sec – a ridiculously low amount of bandwidth. In fact, opening one live stream in the web browser would use more bandwidth than that. The bottleneck is now the CPU, rather than the network (cPython isn’t the most cpu efficient language).
Here are some results:
I decided to see the maximum number of viewers I could fake. I spun up the script on the best hardware I had, and here are the results:
Strangely enough, when there are thousands of fake viewers, the bottleneck actually switches back to the network from the CPU. This time, however, the issue isn’t bandwidth. It’s the number of requests that are being sent out. My guess is that my network throttled the number of packets per machine, and I simply couldn’t send enough requests out fast enough.
Conclusions
Being able to fake thousands of viewers on Twitch is definitely pretty cool, and if one were to do this, he would probably benefit.
A broadcaster can apply for a “partnership” with Twitch, which basically means that he can choose when to play video advertisements throughout his stream. This ad revenue is also shared with the broadcaster. Most large Twitch broadcasters are partners and some are earning estimated figures of $20,000 per year. A major requirement for being accepted as a partner for Twitch is to have a consistently high viewership. I’ve been told that having more than 500 live viewers is enough.
The issue with faking users is that it’s extremely obvious. Instantly gaining hundreds/thousands of viewers from one IP address is clearly going to raise some flags — if Twitch actually checks. I imagine it’s possible for Twitch to check, but does their backend keep track of everything? And for how long?
All in all, being able to fake viewers is definitely going to give a broadcaster a boost. A genuinely interesting broadcaster who doesn’t have a fan-base can instantly rise to stardom by faking views temporarily to bring his channel to the top of the rankings. Nothing too disruptive could happen with this Twitch bot. There are a few very competitive games, such as League of Legends, in which the top broadcaster usually has 50,000+ live viewers. Using a single fake viewer bot won’t make a dent, and it would probably require a few extra computers and a solid network to reach that level of fake viewers.
And for those who were wondering where this script I was talking about this whole time was, so here it is. It’s rather poorly commented but should be simple enough to follow along. You’ll need Python 2.x and the pip modules requests and livestreamer.
quinn it works for me contact me for helpon skype: amirjut707
email me
make it into an exe and ill pay you 100 via paypal.
E-Mail me at twitchbotservice [at] gmail {dot} com
I’ve been struggling to make this work and from what I can tell Twitch removed the critical ‘url’ data from the JSON? Is that correct?
I’ve had the same issue. Is there a workaround for it?
works great ty for the post. These guys need to learn python
getting this now :S
C:\Python27\Scripts>twitch.py 100 10
Traceback (most recent call last):
File “C:\Python27\Scripts\Twitch.py”, line 44, in
for i in range(0, builderThreads):
TypeError: range() integer end argument expected, got str.
thanks for your help also 🙂
Okay so I keep getting a json error for the [‘url’]
Traceback (most recent call last):
File “C:\Python27\lib\threading.py”, line 808, in __bootstrap_inner
self.run()
File “C:\Python27\lib\threading.py”, line 761, in run
self.__target(*self.__args, **self.__kwargs)
File “C:\Users\Jason\Desktop\requests-2.0.0\requests-2.0.0\twitch.py”, line 29, in build
urls.append(getURL())
File “C:\Users\Jason\Desktop\requests-2.0.0\requests-2.0.0\twitch.py”, line 20, in getURL
return json.loads(output)[‘streams’][‘worst’][‘url’] #Parse json and return the URL parameter
KeyError: ‘url’
——————————————————————————————————————————-
I even tried printing the JSON data to the console and it doesn’t even show a “[‘url’]” dict object. Any ideas?
its the tabs instead of spaces try this… import requests import subprocess import json import threading import time from Queue import Queue import sys numberOfViewers = (sys.argv[1]) builderThreads = (sys.argv[2]) startTime = time.time() numberOfSockets = 1 concurrent = 25 urls = [] urlsUsed = [] def getURL(): # Get tokens output = subprocess.Popen([“livestreamer”, “twitch.tv/CHANNELNAMEHERE”, “-j”], stdout=subprocess.PIPE).communicate()[0] return json.loads(output)[‘streams’][‘worst’][‘url’] # Parse json and return the URL parameter def build(): # Builds a set of tokens, aka viewers global numberOfSockets global numberOfViewers while True: if numberOfSockets < numberOfViewers: numberOfSockets += 1 print "Building viewers " + str(numberOfSockets) + "/" + str(numberOfViewers) urls.append(getURL())… Read more »
I have the same problem, now ;[
Same problem here. Not sure how to get it rolling. Not missing a library?
This is the error I got, can someone help?
Exception in thread Thread-1:
Traceback (most recent call last):
File “C:\Python27\lib\threading.py”, line 808, in __bootstrap_inner
self.run()
File “C:\Python27\lib\threading.py”, line 761, in run
self.__target(*self.__args, **self.__kwargs)
File “C:\Python27\Scripts\Obs.py”, line 28, in build
urls.append(getURL())
File “C:\Python27\Scripts\Obs.py”, line 18, in getURL
output = subprocess.Popen([“livestreamer”, “twitch.tv/Obsessedwlol”, “-j”], stdout=subprocess.PIPE).communicate()[0]
File “C:\Python27\lib\subprocess.py”, line 711, in __init__
errread, errwrite)
File “C:\Python27\lib\subprocess.py”, line 948, in _execute_child
startupinfo)
WindowsError: [Error 2] The system cannot find the file specified
getting this now :S
C:\Python27\Scripts>twitch.py 100 10
Traceback (most recent call last):
File “C:\Python27\Scripts\Twitch.py”, line 44, in
for i in range(0, builderThreads):
TypeError: range() integer end argument expected, got str.
thanks for your help also 🙂