Streamlink: Can someone update/fix the afreeca.py plugin?

Created on 25 Apr 2017  ·  76Comments  ·  Source: streamlink/streamlink

Checklist

  • [x] This is a bug report.
  • [ ] This is a feature request.
  • [ ] This is a plugin (improvement) request.
  • [ ] I have read the contribution guidelines.

Description

Plugin seems to no longer work for me. Also, seems this plugin's line 10 was never updated. Rtmp streams almost never show up as available (some channels do), mostly only hls streams. But rtmp streams can be fixed. Line 10 can be changed to:
"rtmp": "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html",
(I did not figure this out, I don't know how to do any of this. I was still using Livestreamer and someone else had the fix in Livestreamer issues... so I never bothered to use Streamlink.)

However, hls streams aren't working now (worked fine yesterday). Any help? There were way to many versions/fixes of this plugin floating around with Livestreamer. I just want ONE simple plugin that will work and regularly be updated.

Edit: Weird... sometimes hls will work if I'm lucky, but I didn't have problems until recent. Otherwise I get the error below.

Environment details

Operating system and version: Windows 10 Pro
Streamlink version: 0.5.0

Comments, logs, screenshots, etc.

This is what I get:

C:UsersUser>streamlink http://play.afreeca.com/ch1716 live_hls
[cli][info] Found matching plugin afreeca for URL http://play.afreeca.com/ch1716
[cli][info] Available streams: live_hls, live (worst, best)
[cli][info] Opening stream: live_hls (hls)
[cli][error] Try 1/1: Could not open stream http://chromecast.afreeca.gscdn.com/livestream-28/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.bUMFaDsLjB6UQ89wrGN6JCEZOm7dvh-EQZ94HZ2CYXtsWb_fyxh4el48lL2zKVSR4nAIiSJ26NDVNI0-RkboTLpRqJaLKUgvWs-gfGbl3a8SOq4y0u6ft5MKfbaK9gLo (500 Server Error: Internal Server Error for url: http://chromecast.afreeca.gscdn.com/livestream-28/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.bUMFaDsLjB6UQ89wrGN6JCEZOm7dvh-EQZ94HZ2CYXtsWb_fyxh4el48lL2zKVSR4nAIiSJ26NDVNI0-RkboTLpRqJaLKUgvWs-gfGbl3a8SOq4y0u6ft5MKfbaK9gLo&aid=.A32.7bbT56vyHM9fKZk.bUMFaDsLjB6UQ89wrGN6JCEZOm7dvh-EQZ94HZ2CYXtsWb_fyxh4el48lL2zKVSR4nAIiSJ26NDVNI0-RkboTLpRqJaLKUgvWs-gfGbl3a8SOq4y0u6ft5MKfbaK9gLo))
error: Could not open stream

plugin issue

Most helpful comment

Here's another update (one that I hope @CaneCraft will be happy about 😄):

This new version can now auto-select between the AWS and GS CDNs based on what AfreecaTV's load balancer thinks is more appropriate for your location. Instead of aws_sd, aws_hd, etc. there's now only sd, hd and original. During tests, I have noticed that GS is mostly selected for connections from within Korea and AWS for my home location (Europe).

This does of course open up the possibility to have 6 quality options like in the older scripts, but I personally think that restricting the selection to only SD, HD and ORIGINAL is more in line with with the other streamlink plugins. Additionally, 6 quality options would require to hardcore the resourcemanager address which lends itself to breakage, like we've experienced these past few days. The current version however should be stable until AfreecaTV overhauls its internal API completely, which I hope is still a long way away.

@gravyboat If the script works for a few weeks without issue, I'd like to make a pull request and finally merge it into the official branch.


import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_API_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"
STREAM_INFO_URLS = "{rmd}/broad_stream_assign.html"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "original": 1080,
    "hd": 720,
    "sd": 480
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            validate.optional("BNO"): validate.text,
            validate.optional("RMD"): validate.text,
            validate.optional("AID"): validate.text,
            validate.optional("CDN"): validate.text
        }
    },
    validate.get("CHANNEL")
)

_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        ),
        "stream_status": validate.text
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        data = {
            "bid": username,
            "mode": "landing",
            "player_type": "html5"
        }

        res = http.post(CHANNEL_API_URL, data=data)
        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(CHANNEL_API_URL, data=data, headers=headers)
        return http.json(res, schema=_channel_schema)

    def _get_stream_info(self, broadcast, quality, cdn, rmd):
        params={
            "return_type": cdn,
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS.format(rmd=rmd), params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn, rmd):
        keyjson = self._get_hls_key(broadcast, username, quality)

        if keyjson["RESULT"] != CHANNEL_RESULT_OK:
            return
        key = keyjson["AID"]

        info = self._get_stream_info(broadcast, quality, cdn, rmd)

        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        (broadcast, rmd, cdn) = (channel["BNO"], channel["RMD"], channel["CDN"])
        if not (broadcast and rmd and cdn):
            return

        for qkey in QUALITYS:
            hls_stream = self._get_hls_stream(broadcast, username, qkey, cdn, rmd)
            if hls_stream:
                yield qkey, hls_stream

__plugin__ = AfreecaTV

All 76 comments

I can confirm that this is an issue with v0.5 of Streamlink. Tested on both Mac and Windows.

import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_INFO_URL = "http://live.afreecatv.com:8057/api/get_broad_state_list.php"
KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
STREAM_INFO_URLS = {
    "rtmp": "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html",
    "hls": "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html"
}
HLS_KEY_URL = "http://api.m.afreecatv.com/broad/a/watch"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

_url_re = re.compile(r"http(s)?://(\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BROAD_INFOS": [{
                "list": [{
                    "nBroadNo": validate.text
                }]
            }]
        }
    },
    validate.get("CHANNEL")
)
_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_channel_info(self, username):
        headers = {
            "Referer": self.url
        }
        params = {
            "uid": username
        }
        res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)

        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username):
        headers = {
            "Referer": self.url
        }
        data = {
            "bj_id": username,
            "broad_no": broadcast
        }
        res = http.post(HLS_KEY_URL, data=data, headers=headers)

        return http.json(res)

    def _get_stream_info(self, broadcast, type):        
        params={
            "rtmp":{
                "return_type": "gs_cdn",
                "use_cors": "true",
                "cors_origin_url": "m.afreecatv.com",
                "broad_key": "{broadcast}-flash-hd-{type}".format(**locals())
            },
            "hls":{
                "return_type": "gs_cdn",
                "use_cors": "true",
                "cors_origin_url": "m.afreecatv.com",
                "broad_no": "{broadcast}-mobileweb-hd-{type}".format(**locals())
            }
        }
        res = http.get(STREAM_INFO_URLS[type], params=params[type])
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username):
        keyjson = self._get_hls_key(broadcast, username)
        if keyjson["result"] != CHANNEL_RESULT_OK:
            return
        key = keyjson["data"]["hls_authentication_key"]
        info = self._get_stream_info(broadcast, "hls")
        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))

    def _get_rtmp_stream(self, broadcast):
        info = self._get_stream_info(broadcast, "rtmp")
        if "view_url" in info:
            params = dict(rtmp=info["view_url"])
            return RTMPStream(self.session, params=params, redirect=True)

    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BROAD_INFOS"][0]["list"][0]["nBroadNo"]
        if not broadcast:
            return

        flash_stream = self._get_rtmp_stream(broadcast)
        if flash_stream:
            yield "live", flash_stream

        mobile_stream = self._get_hls_stream(broadcast, username)
        if mobile_stream:
            yield "live", mobile_stream

__plugin__ = AfreecaTV

@trocknet
Please open a PR with your changes instead of posting it here, so that there can be a discussion regarding your code submission. If it gets merged you will also be credited for the fix instead of another person.

Sorry, I am not a programmer and I do not know how to use github.

Hi trocknet, is this meant to fix the problem with HLS-streams? Those streams still doesn't work for me.

As far as I can see you just changed the URL for RTMP-streams right?

@trocknet Thanks... I'm actually getting a lot of HLS streams to work than not, but there are still some that I get errors with. And with the RTMP fix I mentioned, I should've said that it wasn't perfect. It crashes/doesn't load streams sometimes, and sometimes I even get a "rtmpdump.exe has stopped working".

Here are two errors I got with a 19+ RTMP stream (HLS streams aren't showing up for 19+).

C:UsersUser>streamlink http://play.afreeca.com/cztaemin best
[cli][info] Found matching plugin afreeca for URL http://play.afreeca.com/cztaemin
[cli][info] Available streams: live (worst, best)
[cli][info] Opening stream: live (rtmp)
[cli][info] Starting player: 'C:Program FilesVideoLANVLCvlc.exe'
[cli][info] Stream ended

C:UsersUser>streamlink http://play.afreeca.com/cztaemin best
[cli][info] Found matching plugin afreeca for URL http://play.afreeca.com/cztaemin
[cli][info] Available streams: live (best, worst)
[cli][info] Opening stream: live (rtmp)
[cli][error] Try 1/1: Could not open stream (No data returned from stream)
error: Could not open stream , tried 1 times, exiting
[cli][info] Closing currently open stream...

Thanks @trocknet. Same experience as @tenminute and @JohnDoh666 -- lots more Afreeca streams are supported now, but still quite a few newer ones, like http://afreeca.com/byflash, are not...

Would appreciate any further revisions, thanks

@prech does HLS-streams work on the regular for you? For me they work like 1 in 50 tries...

@JohnDoh666 Yep, most HLS streams work fine for me. I only watch esports/Starcraft streams, and most work well now with @trocknet's update. I'd say 90%+

Aight I mainly watch the BJ's, that may be the difference. I still think its weird though, if they dedicate servers to different content types or something.

@JohnDoh666 What @prech said... most work fine. Even the BJ's... but a few.

Ok, well... as of right now it's broken again. Nothing works.

C:UsersUser>streamlink http://play.afreeca.com/ch1716
[cli][info] Found matching plugin afreeca for URL http://play.afreeca.com/ch1716
error: Unable to open URL: http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html (400 Client Error: Bad Request for url: http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html?broad_key=191565960-flash-hd-rtmp&return_type=gs_cdn&cors_origin_url=m.afreecatv.com&use_cors=true)

Change :
"rtmp": "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html",
to
"rtmp": "http://sessionmanager01.afreeca.tv:6060/broad_stream_assign.html",

@tyu38 I still get borked HLS streams while it works fine for RTMP. Do you have any idea what causes this?

[cli][info] Opening stream: live (hls) [cli][error] Could not open stream: Unable to open URL: http://chromecast.afreeca.gscdn.com/livestream-21/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.sc7x-ehld5-KxkxkOiPP5OCRIPRQU5HWRfrXHd6uLVU-mYGaYlc5D4NVutwb_jYF2N4dS0jrP-oNkuNgRZXlM79qj_j6cJxFODyThaf-uQAmOiDY8TGf8J-Vr-0bQsiu (500 Server Error: Internal Server Error for url: http://chromecast.afreeca.gscdn.com/livestream-21/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.sc7x-ehld5-KxkxkOiPP5OCRIPRQU5HWRfrXHd6uLVU-mYGaYlc5D4NVutwb_jYF2N4dS0jrP-oNkuNgRZXlM79qj_j6cJxFODyThaf-uQAmOiDY8TGf8J-Vr-0bQsiu&aid=.A32.7bbT56vyHM9fKZk.sc7x-ehld5-KxkxkOiPP5OCRIPRQU5HWRfrXHd6uLVU-mYGaYlc5D4NVutwb_jYF2N4dS0jrP-oNkuNgRZXlM79qj_j6cJxFODyThaf-uQAmOiDY8TGf8J-Vr-0bQsiu

Edit:
So it seems to work a little better if i juggle between passing the "worst" and "best" options to streamlink. For example:
streamlink http://play.afreecatv.com/ch1716/191568626 worst

@tyu38 Changing the URL back works again... but then it goes back to what I originally posted. Most RTMP streams don't show up as available. The channel "ch1716" is one of the few that actually lists RTMP and HLS streams. The rest of the channels only load an HLS stream. I don't get why that is.

Here's what channel "ch1716" looks like for me, where "live_hls" is HLS and "live" is RTMP...

C:UsersUser>streamlink http://play.afreecatv.com/ch1716 best
[cli][info] Found matching plugin afreeca for URL http://play.afreecatv.com/ch1716
[cli][info] Available streams: live_hls, live (best, worst)
[cli][info] Opening stream: live (rtmp)
[cli][info] Starting player: "C:Program FilesVideoLANVLCvlc.exe"
[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

And here's what the rest of the channels look like, where "live" is HLS and no RTMP available...

C:UsersUser>streamlink play.afreeca.com/kimdhun best
[cli][info] Found matching plugin afreeca for URL play.afreeca.com/kimdhun
[cli][info] Available streams: live (best, worst)
[cli][info] Opening stream: live (hls)
[cli][info] Starting player: "C:Program FilesVideoLANVLCvlc.exe"
[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

C:UsersUser>streamlink play.afreeca.com/kimdhun worst
[cli][info] Found matching plugin afreeca for URL play.afreeca.com/kimdhun
[cli][info] Available streams: live (worst, best)
[cli][info] Opening stream: live (hls)
[cli][info] Starting player: "C:Program FilesVideoLANVLCvlc.exe"
[cli][info] Player closed
[cli][info] Stream ended
[cli][info] Closing currently open stream...

Service changed again, because there isn't any afreeca website use rtmp stream , this patch only support hls stream.
And ,there are 2 cdn can be used: amazon aws and gscdn, and, there are 3 kind of quality streams: original, hd, sd. This patch suport all of them, the best stream is aws_original by default.
Final, Please forgive me for my poor english...

import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_INFO_URL = "http://live.afreeca.com:8057/api/get_broad_state_list.php"
KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
STREAM_INFO_URLS = "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html"
HLS_KEY_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "gs_original": 1080,
    "gs_hd": 720,
    "gs_sd": 480,
    "aws_original": 1081,
    "aws_hd": 721,
    "aws_sd": 481,
}

CDN_SITES = {
    "gs": "gs_cdn",
    "aws": "aws_cf"
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BROAD_INFOS": [{
                "list": [{
                    "nBroadNo": validate.text
                }]
            }]
        }
    },
    validate.get("CHANNEL")
)
_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        headers = {
            "Referer": self.url
        }
        params = {
            "uid": username
        }
        res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)

        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "player_type": "html5",
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(HLS_KEY_URL, data=data, headers=headers)
        return http.json(res)

    def _get_stream_info(self, broadcast, quality, cdn):
        params={
            "return_type": cdn,
            "use_cors": "true",
            "cors_origin_url": "play.afreecatv.com",
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS, params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn):
        keyjson = self._get_hls_key(broadcast, username, quality)
        if keyjson["CHANNEL"]["RESULT"] != "1":
            return
        key = keyjson["CHANNEL"]["AID"]

        info = self._get_stream_info(broadcast, quality, cdn)
        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")
        tcdn = match.group("cdn")
        if tcdn=="aws.":
            cdn="aws_cf"
        else:
            cdn="gs_cdn"

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BROAD_INFOS"][0]["list"][0]["nBroadNo"]
        if not broadcast:
            return

        for skey in CDN_SITES:
            for qkey in QUALITYS:
                hls_stream = self._get_hls_stream(broadcast, username, qkey, CDN_SITES.get(skey))
                if hls_stream:
                    yield skey+"_"+qkey, hls_stream

__plugin__ = AfreecaTV

@trocknet Awesome... thanks, man.

Forgive my lack of knowledge or if I ever use the wrong terminology... I'm still learning to understand everything. I'd contribute if I could. Appreciate everyone's help so far...

@trocknet how can I choose which CDN to use?

Currently no HLS streams works for me :(

So I replaced the wrong script, afreecatv.py instead of afreeca,.py. Now its working perfectly. Thanks a lot @trocknet

我怎么感觉各位都是国人?
还有那个什么19+,没意思,都是抽烟喝酒骂人,没有你们想看的哈。。。

@trocknet In the future can you please use English when commenting? We're an international community that works on the project so we want to try and keep everything in the same language for ease of communication.

@trocknet That's a little stereotypical? Not that cool, man. Although I really do appreciate what you've done.
And @prech, doesn't help that you laughed at it.

Edit: I should not have taken a five minute break to check this... but my comment still stands @trocknet. Google translate might be wrong, but it's somewhat related.

Anyway.... have a good weekend everyone.

@tenminute Yeah I also threw that into Google translate earlier and I am hoping it's just a translation error. Let's keep everything in English and respectful of everyone please.

sorry, What I mean is the afreeca 19+ stream is not fun, only Smoking and drinking and curse, It's not what we want to see ;-) , so there is no need to crack and watch it.
I noticed that @tenminute wanted to watch 19+ stream, and i thought he was Chinese, bacause he watch chinese web site such as longzhu.com, so i said to him in Chinese.

@trocknet No worries... just a little misunderstanding.

Maybe it need afreecatv login(username,password) routine for 19+.

sorry.
afreeca rtmp: closed the address again.

http://127.0.0.1:4277/194139198/playlist.m3u8
I can broadcast it and watch it as another player. Is there any other way?

Is there another address?

It's broken again.

error: Unable to open URL: http://sessionmanager01.afreeca.tv:6060/broad_stream_assign.html (HTTPConnectionPool(host='sessionmanager01.afreeca.tv', port=6060): Max retries exceeded with url: /broad_stream_assign.html?cors_origin_url=m.afreeca.com&return_type=gs_cdn&broad_key=194429019-flash-hd-rtmp&use_cors=true (Caused by ConnectTimeoutError(, 'Connection to sessionmanager01.afreeca.tv timed out. (connect timeout=20.0)')))

Yeah, it's been broken for a day or two now. Well... depending on your plugin I guess. Everyone seems to have a different plugin.

error: Unable to open URL: http://sessionmanager01.afreeca.tv:6060/broad_stream_assign.html ((<requests.packages.urllib3.connectionpool.HTTPConnectionPool object at 0x028F0C10>, 'Connection to sessionmanager01.afreeca.tv timed out. (connect timeout=20.0)'))

If you try @trocknet's plugin in his comment above, it still works. I ended up not using it before because it wasn't working the greatest for me at times and I sometimes got errors. But I guess it's the only thing working at the moment. I'm also getting the timer problem sometimes like here: https://github.com/streamlink/streamlink/issues/1099

Working, thx man!

Ehh still not work;/
[stream.hls][error] Failed to open segment 3545: Unable to open URL: http://live-hls-korea-cf.afreecatv.com/livestream-24/1280x720/194542505-flash-original-hls_3544.TS (400 Client Error: Bad Request for url: http://live-hls-korea-cf.afreecatv.com/livestream-24/1280x720/194542505-flash-original-hls_3544.TS?aid=.A32.7bbT56vyHM9fKZk.QMhzJseV5fj27QaQDQ5eG0Z1Dxrq5Xc_G1YQccJmDQjtsouTinWIKSvqtCJUNDsNCg9QZL9Ju3iUEqeAESvHPQXY64-AABbBd2r3QNT8ePE)

@tyu38 Try again, and again. It takes a few tries sometimes. Sometimes you have to do it a couple times to work. That is also why I had trouble. I haven't tried it yet, but please go back to https://github.com/streamlink/streamlink/issues/1099. Maybe try MPC.

@tenminute I'm using streamlink to record video, not to watching. When stream is recording, this errors make broken files.

@tyu38 I'm not really big fan of people recording streams (because some people do it for the wrong reasons), but I have no problems recording.

@tenminute I do it form right reasons. I record streams Stacrfaft players from afreecca and upload to youtube.
Many persons watch this videos. You can chceck: http:/korhal.info.pl

So you have file afrecca.py from trocknet post (28 April) and all works fine?

@tyu38 Yes, the same one. Like I said... try, and try, and try. Or... try a different stream to test. I just tried one, and I had to do it like, 7 times to work. That's why I don't really like this plugin. Try to use MPC player? I've only tried VLC and PotPlayer.

See? It works.

[cli][info]_ Available streams: gs_sd (worst), aws_sd, gs_hd, aws_hd, gs_original, aws_original (best)
[cli][info] Opening stream: aws_original (hls)
[cli][info] Starting player: "C:Program FilesVideoLANVLCvlc.exe"

It seems that afreeca limit the api call speed. when build the 6 steams's url together, it often fails.
So, try to reduce the stream number , only use aws_original, now it works better...
Or, you can change variable QUALITYS and CDN_SITES,to set what stream you want...

import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_INFO_URL = "http://live.afreecatv.com:8057/api/get_broad_state_list.php"
KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
STREAM_INFO_URLS = "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html"
HLS_KEY_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

#QUALITYS=["original", "hd", "sd"]
QUALITYS=["original"]

QUALITY_WEIGHTS = {
    "gs_original": 1080,
    "gs_hd": 720,
    "gs_sd": 480,
    "aws_original": 1081,
    "aws_hd": 721,
    "aws_sd": 481,
}

CDN_SITES = {
#    "gs": "gs_cdn",
    "aws": "aws_cf"
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BROAD_INFOS": [{
                "list": [{
                    "nBroadNo": validate.text
                }]
            }]
        }
    },
    validate.get("CHANNEL")
)
_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        headers = {
            "Referer": self.url
        }
        params = {
            "uid": username
        }
        res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)

        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "player_type": "html5",
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(HLS_KEY_URL, data=data, headers=headers)
        return http.json(res)

    def _get_stream_info(self, broadcast, quality, cdn):
        params={
            "return_type": cdn,
#            "use_cors": "true",
#            "cors_origin_url": "play.afreecatv.com",
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS, params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn):
        keyjson = self._get_hls_key(broadcast, username, quality)
        if keyjson["CHANNEL"]["RESULT"] != "1":
            return
        key = keyjson["CHANNEL"]["AID"]

        info = self._get_stream_info(broadcast, quality, cdn)
        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")
        tcdn = match.group("cdn")
        if tcdn=="aws.":
            cdn="aws_cf"
        else:
            cdn="gs_cdn"

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BROAD_INFOS"][0]["list"][0]["nBroadNo"]
        if not broadcast:
            return

        for skey in CDN_SITES:
            for qkey in QUALITYS:
                hls_stream = self._get_hls_stream(broadcast, username, qkey, CDN_SITES.get(skey))
                if hls_stream:
                    yield skey+"_"+qkey, hls_stream

__plugin__ = AfreecaTV

streams i've tried with the trocknets modifications to the afreeca plugin is very choppy/laggy still.getting the below error

[stream.hls][error] Failed to open segment 1411: Unable to open URL: http://live-hls-korea-cf.afreecatv.com/livestream-02/1280x720/195161936-flash-original-hls_1410.TS (400 Client Error: Bad Request for url: http://live-hls-korea-cf.afreecatv.com/livestream-02/1280x720/195161936-flash-original-hls_1410.TSaid=.A32.7bbT56vyHM9fKZk.9Egw4pFMK2DPqLcUPTZ_OwGwZCxmDcpGQ__thbiCgmNKmjZoDJYh4P2c6ttPzRkAnq8Bf8Kt5AQpNxWgetE4aUERdmQFQnQARFK3YopcwM0)

any fix?

Someone will help?

Broken again?

[cli][info] Available streams: aws_original (worst, best) [cli][info] Opening stream: aws_original (hls) [cli][error] Try 1/1: Could not open stream <HLSStream('http://live-hls-korea-cf.afreecatv.com/livestream-03/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.cp7qx7EbPLmHQhgvb5BhOfYDuT-tGOMxTqxoGlMPXXszAE8bscrMNlrT-BaCQgnYowe7yv_wy_h7ghn3Xn7cHOI8elz9hiF6-oCDO107CpM')> (Could not open stream: Unable to open URL: http://live-hls-korea-cf.afreecatv.com/livestream-03/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.cp7qx7EbPLmHQhgvb5BhOfYDuT-tGOMxTqxoGlMPXXszAE8bscrMNlrT-BaCQgnYowe7yv_wy_h7ghn3Xn7cHOI8elz9hiF6-oCDO107CpM (HTTPConnectionPool(host='live-hls-korea-cf.afreecatv.com', port=80): Max retries exceeded with url: /livestream-03/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.cp7qx7EbPLmHQhgvb5BhOfYDuT-tGOMxTqxoGlMPXXszAE8bscrMNlrT-BaCQgnYowe7yv_wy_h7ghn3Xn7cHOI8elz9hiF6-oCDO107CpM&aid=.A32.7bbT56vyHM9fKZk.cp7qx7EbPLmHQhgvb5BhOfYDuT-tGOMxTqxoGlMPXXszAE8bscrMNlrT-BaCQgnYowe7yv_wy_h7ghn3Xn7cHOI8elz9hiF6-oCDO107CpM (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x03D5C790>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed',)))) error: Could not open stream <HLSStream('http://live-hls-korea-cf.afreecatv.com/livestream-03/auth_playlist.m3u8?aid=.A32.7bbT56vyHM9fKZk.cp7qx7EbPLmHQhgvb5BhOfYDuT-tGOMxTqxoGlMPXXszAE8bscrMNlrT-BaCQgnYowe7yv_wy_h7ghn3Xn7cHOI8elz9hiF6-oCDO107CpM')>, tried 1 times, exiting

Edit: Never mind, I can get gscdn to work but amazon aws doesn't seem to work at all now for me.

Can someone fix afreeca timer?
This problem was in livestreamer and fixed https://github.com/chrippa/livestreamer/issues/982#issuecomment-123748593

Dismiss my previous post below; I found a way to get the correct resourcemanager address by calling /payer_live_api.php. For now, it always ends up resourcemanager-aws-ko regardless from what IP I connect from. However, if that ever were to change and update to closer location, the following script should be able to handle that:

import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_API_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"
STREAM_INFO_URLS = "{rmd}/broad_stream_assign.html"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "aws_original": 1080,
    "aws_hd": 720,
    "aws_sd": 480,
}

CDN_SITES = {
    "aws": "aws_cf"
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BNO": validate.text,
            "RMD": validate.text
        }
    },
    validate.get("CHANNEL")
)

_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        data = {
            "bid": username,
            "mode": "landing"
        }

        res = http.post(CHANNEL_API_URL, data=data)
        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(CHANNEL_API_URL, data=data, headers=headers)
        return http.json(res)

    def _get_stream_info(self, broadcast, quality, cdn, rmd):
        params={
            "return_type": cdn,
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS.format(rmd=rmd), params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn, rmd):
        keyjson = self._get_hls_key(broadcast, username, quality)
        if keyjson["CHANNEL"]["RESULT"] != "1":
            return
        key = keyjson["CHANNEL"]["AID"]

        info = self._get_stream_info(broadcast, quality, cdn, rmd)
        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BNO"]
        if not broadcast:
            return

        rmd = channel["RMD"]
        if not rmd:
            return

        for skey in CDN_SITES:
            for qkey in QUALITYS:
                hls_stream = self._get_hls_stream(broadcast, username, qkey, CDN_SITES.get(skey), rmd)
                if hls_stream:
                    yield skey+"_"+qkey, hls_stream

__plugin__ = AfreecaTV

I removed almost all of the leftover RTMP stuff, as it's been proven not to work correctly. If tried a lot of streams and all of them ran over HLS. As for the timer, I've tried integrating @trocknet's solution, but it doesn't seem to be working anymore.


Here is a very, very slightly modified version of what @trocknet posted that works:

import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_INFO_URL = "http://live.afreecatv.com:8057/api/get_broad_state_list.php"
KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
STREAM_INFO_URLS = "http://resourcemanager-aws-ko.afreeca.tv:9090/broad_stream_assign.html"
HLS_KEY_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]
#QUALITYS=["original"]

QUALITY_WEIGHTS = {
    "gs_original": 1080,
    "gs_hd": 720,
    "gs_sd": 480,
    "aws_original": 1081,
    "aws_hd": 721,
    "aws_sd": 481,
}

CDN_SITES = {
#    "gs": "gs_cdn",
    "aws": "aws_cf"
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BROAD_INFOS": [{
                "list": [{
                    "nBroadNo": validate.text
                }]
            }]
        }
    },
    validate.get("CHANNEL")
)
_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        headers = {
            "Referer": self.url
        }
        params = {
            "uid": username
        }
        res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)

        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            #"player_type": "html5",
            "mode": "landing",
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(HLS_KEY_URL, data=data, headers=headers)
        return http.json(res)

    def _get_stream_info(self, broadcast, quality, cdn):
        params={
            "return_type": cdn,
#            "use_cors": "true",
#            "cors_origin_url": "play.afreecatv.com",
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS, params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn):
        keyjson = self._get_hls_key(broadcast, username, quality)
        if keyjson["CHANNEL"]["RESULT"] != "1":
            return
        key = keyjson["CHANNEL"]["AID"]

        info = self._get_stream_info(broadcast, quality, cdn)
        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")
        tcdn = match.group("cdn")
        if tcdn=="aws.":
            cdn="aws_cf"
        else:
            cdn="gs_cdn"

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BROAD_INFOS"][0]["list"][0]["nBroadNo"]
        if not broadcast:
            return

        for skey in CDN_SITES:
            for qkey in QUALITYS:
                hls_stream = self._get_hls_stream(broadcast, username, qkey, CDN_SITES.get(skey))
                if hls_stream:
                    yield skey+"_"+qkey, hls_stream

__plugin__ = AfreecaTV

For some reason using resourcemanager.afreeca.tv instead of resourcemanager-aws-ko.afreeca.tv returns a CDN address that doesn't work with all streams.

If we ignore the GS CDN and focus only on AWS (which is what I believe AfreecaTV is switching to anyway, so GS might eventually become obsolete) URLs for all qualities (there's always sd, hd and original, never more or fewer) can be returned without a timeout.

I've been testing a lot of streams of different categories and qualities with this fix, and I have not encountered significant issues.

The only annoying thing is that when you see the 15-seconds countdown, you need to wait for it to finish and restart the stream again with the same quality. It should work after that. I think this has to do with the stream having to be prepared if you are the first person to watch it in a particular configuration. So it's going to happen a lot more with smaller streams than bigger ones.

Thanks @schrobby! Do you prefer to use the first one?

Thanks @schrobby

Unfortunately, wonder if Afreeca changed things again. Tried both versions of your plugin script, with and without the RTMP remnants, but seeing "No playable streams" everywhere. Just tried random streamers from the Afreeca all page:

streamlink afreeca.com/tmsh401
[cli][info] Found matching plugin afreeca for URL afreeca.com/tmsh401
error: No playable streams found on this URL: afreeca.com/tmsh401

streamlink afreecatv.com/tmsh401
[cli][info] Found matching plugin afreeca for URL afreecatv.com/tmsh401
error: No playable streams found on this URL: afreecatv.com/tmsh401

streamlink afreecatv.com/yuambo
[cli][info] Found matching plugin afreeca for URL afreecatv.com/yuambo
error: No playable streams found on this URL: afreecatv.com/yuambo

streamlink afreecatv.com/gtv7
[cli][info] Found matching plugin afreeca for URL afreecatv.com/gtv7
error: No playable streams found on this URL: afreecatv.com/gtv7

streamlink afreecatv.com/four14
[cli][info] Found matching plugin afreeca for URL afreecatv.com/four14
error: No playable streams found on this URL: afreecatv.com/four14`

What the hell???

I'm getting the same exact thing as @prech now. It was just working 30 minutes ago. I tried four different versions. All the same error.

@tenminute It seems to me like afreeca puts a lot of work into ensuring their streams can only be viewed via their site. Either that or they are inadvertently breaking things with their updates, but somehow I doubt that is the case.

@schrobby
I'm recording streams, so I don't know when the 15-seconds countdown is running...
Anyway, I switched streamlink to livestreamer and works perfectly.

@tyu38
Is livestreamer really working for Afreeca, now?

Not working.

Fixed:

import re
import time

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_API_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"
STREAM_INFO_URLS = "{rmd}/broad_stream_assign.html"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "aws_original": 1080,
    "aws_hd": 720,
    "aws_sd": 480,
}

CDN_SITES = {
    "aws": "aws_cf"
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            validate.optional("BNO"): validate.text,
            validate.optional("RMD"): validate.text,
            validate.optional("AID"): validate.text
        }
    },
    validate.get("CHANNEL")
)

_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        ),
        "stream_status": validate.text
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        data = {
            "bid": username,
            "mode": "landing"
        }

        res = http.post(CHANNEL_API_URL, data=data)
        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(CHANNEL_API_URL, data=data, headers=headers)
        return http.json(res, schema=_channel_schema)

    def _get_stream_info(self, broadcast, quality, cdn, rmd):
        params={
            "return_type": cdn,
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS.format(rmd=rmd), params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn, rmd):
        keyjson = self._get_hls_key(broadcast, username, quality)

        if keyjson["RESULT"] != CHANNEL_RESULT_OK:
            return
        key = keyjson["AID"]

        info = self._get_stream_info(broadcast, quality, cdn, rmd)

        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        broadcast = channel["BNO"]
        if not broadcast:
            return

        rmd = channel["RMD"]
        if not rmd:
            return

        for skey in CDN_SITES:
            for qkey in QUALITYS:
                hls_stream = self._get_hls_stream(broadcast, username, qkey, CDN_SITES.get(skey), rmd)
                if hls_stream:
                    yield skey+"_"+qkey, hls_stream

__plugin__ = AfreecaTV

@gravyboat They changed something minuscule in their API. Could be intentional, but they should have known that this would be fixed within hours, if they are indeed keeping track of this discussion.

@schrobby You da man, bro!

Okay, I think this proves that someone is watching this thread and making changes to the API as we speak. The script still works when I connect from a Korean VPN, but any attempts to get data from "player_live_api.php" with my normal IP address returns in "RESULT" == -2.

This is now a cat-and-mouse game. I think we can still win this. They seem to be making only very small changes each time we update the script, so if we revert some of the recent changes I made and hardcode the URLs (resourcemanager, ...), there is nothing they can do without breaking the compatibility of their own clients. This removes some of the "tricks" such as getting the BNO from player_live_api.php. We'll have to stick to acting exactly like their own web client does - this might include having to scrape the website at the beginning to get the BNO.

I don't have time for this today, but I might post a big update tomorrow that'll hopefully work for more than a day.

@schrobby Lovely, works fine.

Do you (or anyone else) know what happened to the CDN alternatives? We used to have the option of either Amazon AWS or GSCDN, and lately it seems like only one has been working.

Edit: Great. Just read your post above. Good luck.

@CaneCraft I saw the GSCDN work for some streams when testing the older scripts that were posted here and in other issues. The problem is, I can't really build a working script around it myself as the AfreecaTV client does no longer seem to support that CDN. I think they might be switching to AWS entirely.

Did GSCDN work better for you?

@schrobby If someone is possibly monitoring this... I honestly understand why. It was even on Korean news stations. Because they're are hack sites. You may (or may not) be surprised as to how many people record afreeca streams. The "I honestly understand why" part is because people record streams for the wrong reasons and post them to adult/nude/porn sites. That is why I highly support @gravyboat's issue on recording options. I decided not to comment on it and I'm not going to reference the issue because it's not related to this specific topic... but that's another story. Thanks again though, @schrobby.

@schrobby Which CDN worked better seemed a bit random, or at least varied between users depending on their location. In Europe, GSCDN some times worked better, and Amazon AWS some times worked better in the US, but then other days it reversed for no obvious reason.

I used to recommend that people change CDNs before lowering quality options since that would often work. I'll be sad if there's only one CDN working now, but it makes it easier to maintain the launcher we're using.

@tenminute Not being able to use something like streamlink doesn't prevent people from recording the streams. It's much easier to open the network tab in your browser's developer console and look for .m3u8 file and just download that. Also, streamlink never worked with 19+ streams anyway, so nothing downloaded through it would have been uploaded to nude sites.

I understand them not wanting to lose (ad?) revenue by having people watch streams through different means, but this is an argument you could make for the streamlink project as a whole, not just the AfreecaTV plugin.

Do you have any links to the news articles you mentioned? I looked for it on Naver but couldn't really find anything substantial.

I have a link bookmarked somewhere, but I'm not going to link it. I also have the news video. "Streamlink" never worked for 19+, but it worked for Livestreamer at one point with RTMP streams. I've gotten into 19+/locked streams before. But 19+ streams isn't the point. It doesn't have to be a 19+ stream for horny perverted dudes to record a girl dancing and put it on the internet... only for some lonely bastard to come along and post perverted comments. Anyway.............................................. now i'm just rambling.

Edit: Not being able to use Streamlink doesn't prevent people from recording streams, but it makes it harder. The basic person or people who aren't programmers or know anything about coding know nothing about developer tools, etc, etc.... hence why we are here. Why we are in need for someone like you to contribute. Also they're seems to be other alternatives to Livestreamer/Streamlink that I've seen.

Wake up and discover I missed the party :), looks like we're back to "No playable streams" again?

Will try to give @schrobby's latest version another go with a VPN. Regardless, thanks for all the repeated attempts!

@prech It's good to go... no need for a vpn.

(I've been awake for too long... I need to go to sleep)

So... some "Aylear" guy is posting fixes on teamliquid (http://www.teamliquid.net/forum/brood-war/512913-guide-watch-afreeca-sc-bw-streams-with-vlc?page=20#396)

I simply asked him to post problems and issues on here instead of teamliquid and he went all HULK MODE on me, LOL. And I guess he frequents here also. So if you're reading this... you need a Xanax or somethig.

Agree with me or not... it's there to read.

@tenminute Let's try to keep it relatively respectful and drama free please.

@gravyboat Sorry... it just got to me a little bit. Just they way he acted immaturely. Had to stoop to his level. The posts seem to have been deleted... but I know he'll be in here.

@tenminute It's all good, let's just make sure we're being polite.

Here's another update (one that I hope @CaneCraft will be happy about 😄):

This new version can now auto-select between the AWS and GS CDNs based on what AfreecaTV's load balancer thinks is more appropriate for your location. Instead of aws_sd, aws_hd, etc. there's now only sd, hd and original. During tests, I have noticed that GS is mostly selected for connections from within Korea and AWS for my home location (Europe).

This does of course open up the possibility to have 6 quality options like in the older scripts, but I personally think that restricting the selection to only SD, HD and ORIGINAL is more in line with with the other streamlink plugins. Additionally, 6 quality options would require to hardcore the resourcemanager address which lends itself to breakage, like we've experienced these past few days. The current version however should be stable until AfreecaTV overhauls its internal API completely, which I hope is still a long way away.

@gravyboat If the script works for a few weeks without issue, I'd like to make a pull request and finally merge it into the official branch.


import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HLSStream

CHANNEL_API_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"
STREAM_INFO_URLS = "{rmd}/broad_stream_assign.html"

CHANNEL_RESULT_ERROR = 0
CHANNEL_RESULT_OK = 1

QUALITYS=["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "original": 1080,
    "hd": 720,
    "sd": 480
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            validate.optional("BNO"): validate.text,
            validate.optional("RMD"): validate.text,
            validate.optional("AID"): validate.text,
            validate.optional("CDN"): validate.text
        }
    },
    validate.get("CHANNEL")
)

_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        ),
        "stream_status": validate.text
    }
)

class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "afreeca"

        return Plugin.stream_weight(key)

    def _get_channel_info(self, username):
        data = {
            "bid": username,
            "mode": "landing",
            "player_type": "html5"
        }

        res = http.post(CHANNEL_API_URL, data=data)
        return http.json(res, schema=_channel_schema)

    def _get_hls_key(self, broadcast, username, quality):
        headers = {
            "Referer": self.url
        }

        data = {
            "bid": username,
            "bno": broadcast,
            "pwd": "",
            "quality": quality,
            "type": "pwd"
        }
        res = http.post(CHANNEL_API_URL, data=data, headers=headers)
        return http.json(res, schema=_channel_schema)

    def _get_stream_info(self, broadcast, quality, cdn, rmd):
        params={
            "return_type": cdn,
            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
        }
        res = http.get(STREAM_INFO_URLS.format(rmd=rmd), params=params)
        return http.json(res, schema=_stream_schema)

    def _get_hls_stream(self, broadcast, username, quality, cdn, rmd):
        keyjson = self._get_hls_key(broadcast, username, quality)

        if keyjson["RESULT"] != CHANNEL_RESULT_OK:
            return
        key = keyjson["AID"]

        info = self._get_stream_info(broadcast, quality, cdn, rmd)

        if "view_url" in info:
            return HLSStream(self.session, info["view_url"], params=dict(aid=key))


    def _get_streams(self):
        match = _url_re.match(self.url)
        username = match.group("username")

        channel = self._get_channel_info(username)
        if channel["RESULT"] != CHANNEL_RESULT_OK:
            return

        (broadcast, rmd, cdn) = (channel["BNO"], channel["RMD"], channel["CDN"])
        if not (broadcast and rmd and cdn):
            return

        for qkey in QUALITYS:
            hls_stream = self._get_hls_stream(broadcast, username, qkey, cdn, rmd)
            if hls_stream:
                yield qkey, hls_stream

__plugin__ = AfreecaTV

can now open streamns using above but get following error constantly making stream very stop start.[stream.hls][error] Failed to open segment 2652: Unable to open URL: http://live-hls-onebuild-cf.afreecatv.com/ko-livestream-01/1280x720/196240817-flash-original-hls_2651.TS (400 Client Error: Bad Request for url: http://live-hls-onebuild-cf.afreecatv.com/ko-livestream-01/1280x720/196240817-flash-original-hls_2651.TS?aid=.A32.7bbT56vyHM9fKZk.hAXgO0oFFcE84i5lzG9hpESr8QDySsVEKGgKi4UmRvZwoeCQV9_tUxuxAE4OCFg1moFqo5_mDUvRZG15ZwYleIfQU3O0EfHcQ7FYieEvZRk)

The script works very well and thanks.

Thanks @schrobby for update the script.
and i have no time and energy to do that anymore.Sorry for all afreecatv fans......

I can download other streaming in afreecatv with the latest script over here but not this one:
http://afreeca.com/feel0100
I don't know why, it is streaming fine just cannot download it and it is not 19+ know.

nvm

where can i get compiled file?

about the timer
change the code in hls.py
from

    def write(self, sequence, res, chunk_size=8192):
        if sequence.segment.key and sequence.segment.key.method != "NONE":

to

    def write(self, sequence, res, chunk_size=8192):
        if sequence.segment.uri.find("afreeca")!= -1 and (sequence.segment.uri.find("_0.TS")!= -1 or sequence.segment.uri.find("_1.TS")!= -1 or sequence.segment.uri.find("_2.TS")!= -1):
            return
        if sequence.segment.key and sequence.segment.key.method != "NONE":

@schrobby how to choose gs cdn only .aws link speed is not good for me.

@trocknet I can't find that code snippet in hls.py. Did they change it?

Found it, I was looking in the wrong folder, its the hls.py in the "stream" folder.

The code change is not working any more though :(

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cdrage picture cdrage  ·  43Comments

Junior1544 picture Junior1544  ·  55Comments

whitetop picture whitetop  ·  54Comments

bastimeyer picture bastimeyer  ·  36Comments

whady picture whady  ·  35Comments