SeaHOH / ykdl

a video downloader focus on China mainland video sites.
https://github.com/zhangn1985/ykdl
Other
1.42k stars 285 forks source link

有计划支持pp体育直播吗? #385

Open simonfire007 opened 5 years ago

simonfire007 commented 5 years ago

网页看还在使用flash,体验不好

SeaHOH commented 5 years ago

需要破解算法。

wnpllrzodiac commented 5 years ago

用 ppbox.so 下载了看吧。分段flv比较简单,主要是 下载地址要找到

wnpllrzodiac commented 5 years ago

http://www.huaren.tv/sports 可以关注下,英超应该是 pptv那里来的源

bigmangos commented 5 years ago

下载直播?

airdge commented 4 years ago

算法和pptv差不多 网页使用的是live2 (5s一个block) 但可以用M3U8拼接 http://{ip}/live/{interval}/{delay}/{rid}.m3u8?type={type}&k={k}

从数据里面可以匹配到interval delay rid sh等相关参数 直播api没有id数据(< id>xxx< /id>),可以忽略

<dt ft="3" rid="115c87c2c55abddc7ae50c28996b94f6">
<sh limit="0|2000">61.164.212.42:80</sh>
<st>Wed Dec 18 11:34:41 2019 UTC</st>
<bwt>0</bwt>
<key expire="Wed Dec 18 23:34:41 2019 UTC">
e25a8439c45f5f941fdb162f42c819e0-4b9a-1576712081%26bppcataid%3D38
</key>
<bh limit="0|2000">219.146.248.53:80</bh>
</dt>
<bc>
<bwt>0</bwt>
</bc>
<itemList>
<item id="4" intValue="0" itemValue=""/>
<item id="3" intValue="0" itemValue=""/>
<item id="9" intValue="0" itemValue=""/>
<item id="7" intValue="0" itemValue=""/>
<item id="8" intValue="0" itemValue=""/>
<item id="172" intValue="0" itemValue=""/>
<item id="173" intValue="0" itemValue=""/>
<item id="174" intValue="0" itemValue=""/>
<item id="175" intValue="0" itemValue=""/>
<item id="214" intValue="0" itemValue="delay,30;interval,5">delay,30;interval,5</item>
<item id="201" intValue="0" itemValue="3,4">3,4</item>
<item id="216" intValue="0" itemValue=""/>
</itemList>

kdecrypt算法(通用)

from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
import hashlib 

def sha256(string):
    return hashlib.sha256(string.encode("utf-8")).hexdigest()

def kdecrypt(key, sh, st, id, bh, iv):
    playAppKey = "V8oo0Or1f047NaiMTxK123LMFuINTNeI"
    hashkey =  sha256("{}{}{}{}{}{}".format(sh, st, id, bh, iv, playAppKey))
    cipher = AES.new(unhexlify(hashkey), AES.MODE_ECB)
    plaintext = cipher.decrypt(unhexlify(key[0:32]))
    return "{}{}".format(bytes.decode(hexlify(plaintext)[0:32]), key[32:])

key='5b06c0d53cbf4afa89d6aa8e76967ea5-4f0e-1576701296%26bppcataid%3D38'
sh='61.164.212.42:80'
st='Wed Dec 18 08:34:56 2019 UTC'
id=''
bh='117.21.186.148:80'
iv='0a604b7ae4d4b3f5af43c47aa00a027a'
k= kdecrypt(key, sh, st,id, bh, iv)
print(k)

图片

SeaHOH commented 4 years ago

年底比较忙,空闲都用在另一个项目上了。欢迎有人能挤出时间来 PR。

wnpllrzodiac commented 4 years ago

要当前有直播才能调试,可是都是晚上。有点尴尬。

airdge commented 4 years ago
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@File    : pptv.py
@Time    : 2019/12/18 17:14  
"""

from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
import importlib

def query(self, p):
    logger = self.logging.getLogger("query")
    common = self.common
    if p["parse"].startswith("http"):
        vid = common.match("sectionid=(\d+)", p["parse"])
    else:
        vid = p["parse"]

    if p["query"]:
        url = (
            "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
            % vid
        )
        html = common.curl(url)
        logger.debug("section: " + html + "\r\n")
        json = common.loads(html)
        title = json["data"]["sectionInfo"]["title"]

    return self.compact("query")

def parse(self, p):
    logger = self.logging.getLogger("parse")
    common = self.common
    assert p["vid"], self.message.output("vid")
    vid = p["vid"]
    url = (
        "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
        % vid
    )
    html = common.curl(url)
    logger.debug("section: " + html + "\r\n")
    json = common.loads(html)
    title = json["data"]["sectionInfo"]["title"]
    # assert json["data"]["liveFlag"] == "1", self.message.output("close")
    if json["data"]["liveFlag"] == "1":
        cid = json["data"]["sectionInfo"]["lives"][0]["cid"]
        webplayParams = {
            "zone": "8",
            "vvid": "5199ebda-7ce8-6463-97e9-18254ef22e2b",
            "username": "",
            "param": "type=web&ahl_ver=1&ahl_random=5c6c25342d512d295f726b3838355827&ahl_signa=b2c418ee1293af26e29e732c1853317813b2b19cc481b993e517ca16d5ff5d41&userType=0&o=m.ppsport.com",
            "isSports": "1",
            "vts": "0",
            "pageUrl": "https://sports.pptv.com/sportslive?sectionid=171471",
            "o": "m.ppsport.com",
            "type": "web",
            "salt": "pv",
            "sl": "1",
            "referrer": "https://m.ppsport.com/live/z5D1c9tBse9S0Dg",
            "version": "6",
            "appplt": "flp",
            "appid": "pptv.flashplayer.live",
            "appver": "2.12.59",
            "nddp": "1",
        }
        webplayUrl = "https://web-play.pptv.com/webplay3-0-{}.xml?{}".format(
            cid, common.urlencode(webplayParams)
        )
        webplayHtml = common.curl(webplayUrl)
        logger.debug("xml: " + webplayHtml + "\r\n")
        dtList = common.matchall("<dt(.*?)<\/dt>", webplayHtml.replace("\n", ""))
        assert dtList, self.message.output("lists")

        # common.data函数,列表按hd取一组数据
        dt = common.data(dtList, p["hd"])
        rid = common.match('rid="(\w+)', dt)

        ary = ["流畅", "标清", "高清", "超清", "蓝光"]
        quality = ary[0 : len(dt)]
        show = common.data(quality, p["hd"])

        match = common.matchall(
            "<(\w+).*?>([^\<]+)<\/\w+>", dt.replace("<ip_serviceid/>", "")
        )
        array = {i[0]: i[1] for i in match}

        key = kdecrypt(
            self, array["key"], array["sh"], array["st"], "", array["bh"], array["iv"]
        )
        delay, interval = common.match("delay,(\d+);interval,(\d+)", webplayHtml)
        m3u8 = "http://%s/live/%s/%s/%s.m3u8?k=%s&type=web" % (
            array["sh"],
            interval,
            delay,
            rid,
            key,
        )
        ext = playback = "m3u8"
    elif json["data"]["liveFlag"] == "2":

        self.params["vtype"] = "video"

        afterCid = json["data"]["sectionInfo"]["lives"][0]["afterCid"]
        image = "http://s1.pplive.cn/v/cap/%s/w640.jpg" % afterCid
        imp = importlib.import_module("parse.video.pptv")
        data = imp.parse(self, {"vid": afterCid, "hd": p["hd"], "from": "pptv"})

        for i in data:
            locals()[i] = data[i]
    return self.compact("parse")

def kdecrypt(self, key, sh, st, id, bh, iv):
    playAppKey = "V8oo0Or1f047NaiMTxK123LMFuINTNeI"
    hashkey = self.common.sha256("{}{}{}{}{}{}".format(sh, st, id, bh, iv, playAppKey))
    cipher = AES.new(unhexlify(hashkey), AES.MODE_ECB)
    plaintext = cipher.decrypt(unhexlify(key[0:32]))
    return "{}{}".format(bytes.decode(hexlify(plaintext)[0:32]), key[32:])

算法差不多是这样,可以当下参考

{
    "cid": "505779",
    "ext": "m3u8",
    "hd": 5,
    "multirates": 5,
    "parse": "http://sports.pptv.com/sportslive/pg_h5live?sectionid=172160&matchid=",
    "playback": "m3u8",
    "quality": [
        "流畅",
        "标清",
        "高清",
        "超清",
        "蓝光"
    ],
    "show": "蓝光",
    "site": "PPTV直播(PPTV)",
    "streams": {
        "m3u8": "http://27.159.123.226:80/live/5/30/557ce94254c89ce64edeb8d38530d64d.m3u8?k=03b935baf58f785f8adba83e96917eca-5592-1578687462%26bppcataid%3D38&type=web"
    },
    "title": "走进CBA全明星",
    "type": "pptv",
    "vid": "172160",
    "vtype": "live"
}
wnpllrzodiac commented 4 years ago

very good!

wnpllrzodiac commented 4 years ago

@airdge 好像失效了。可惜

airdge commented 4 years ago

@airdge 好像失效了。可惜

type=mhpptv

图片

wnpllrzodiac commented 3 years ago

@airdge 好像失效了。可惜

type=mhpptv

图片

I will try it! THX!!!

die9o commented 3 years ago
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@File    : pptv.py
@Time    : 2019/12/18 17:14  
"""

from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
import importlib

def query(self, p):
    logger = self.logging.getLogger("query")
    common = self.common
    if p["parse"].startswith("http"):
        vid = common.match("sectionid=(\d+)", p["parse"])
    else:
        vid = p["parse"]

    if p["query"]:
        url = (
            "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
            % vid
        )
        html = common.curl(url)
        logger.debug("section: " + html + "\r\n")
        json = common.loads(html)
        title = json["data"]["sectionInfo"]["title"]

    return self.compact("query")

def parse(self, p):
    logger = self.logging.getLogger("parse")
    common = self.common
    assert p["vid"], self.message.output("vid")
    vid = p["vid"]
    url = (
        "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
        % vid
    )
    html = common.curl(url)
    logger.debug("section: " + html + "\r\n")
    json = common.loads(html)
    title = json["data"]["sectionInfo"]["title"]
    # assert json["data"]["liveFlag"] == "1", self.message.output("close")
    if json["data"]["liveFlag"] == "1":
        cid = json["data"]["sectionInfo"]["lives"][0]["cid"]
        webplayParams = {
            "zone": "8",
            "vvid": "5199ebda-7ce8-6463-97e9-18254ef22e2b",
            "username": "",
            "param": "type=web&ahl_ver=1&ahl_random=5c6c25342d512d295f726b3838355827&ahl_signa=b2c418ee1293af26e29e732c1853317813b2b19cc481b993e517ca16d5ff5d41&userType=0&o=m.ppsport.com",
            "isSports": "1",
            "vts": "0",
            "pageUrl": "https://sports.pptv.com/sportslive?sectionid=171471",
            "o": "m.ppsport.com",
            "type": "web",
            "salt": "pv",
            "sl": "1",
            "referrer": "https://m.ppsport.com/live/z5D1c9tBse9S0Dg",
            "version": "6",
            "appplt": "flp",
            "appid": "pptv.flashplayer.live",
            "appver": "2.12.59",
            "nddp": "1",
        }
        webplayUrl = "https://web-play.pptv.com/webplay3-0-{}.xml?{}".format(
            cid, common.urlencode(webplayParams)
        )
        webplayHtml = common.curl(webplayUrl)
        logger.debug("xml: " + webplayHtml + "\r\n")
        dtList = common.matchall("<dt(.*?)<\/dt>", webplayHtml.replace("\n", ""))
        assert dtList, self.message.output("lists")

        # common.data函数,列表按hd取一组数据
        dt = common.data(dtList, p["hd"])
        rid = common.match('rid="(\w+)', dt)

        ary = ["流畅", "标清", "高清", "超清", "蓝光"]
        quality = ary[0 : len(dt)]
        show = common.data(quality, p["hd"])

        match = common.matchall(
            "<(\w+).*?>([^\<]+)<\/\w+>", dt.replace("<ip_serviceid/>", "")
        )
        array = {i[0]: i[1] for i in match}

        key = kdecrypt(
            self, array["key"], array["sh"], array["st"], "", array["bh"], array["iv"]
        )
        delay, interval = common.match("delay,(\d+);interval,(\d+)", webplayHtml)
        m3u8 = "http://%s/live/%s/%s/%s.m3u8?k=%s&type=web" % (
            array["sh"],
            interval,
            delay,
            rid,
            key,
        )
        ext = playback = "m3u8"
    elif json["data"]["liveFlag"] == "2":

        self.params["vtype"] = "video"

        afterCid = json["data"]["sectionInfo"]["lives"][0]["afterCid"]
        image = "http://s1.pplive.cn/v/cap/%s/w640.jpg" % afterCid
        imp = importlib.import_module("parse.video.pptv")
        data = imp.parse(self, {"vid": afterCid, "hd": p["hd"], "from": "pptv"})

        for i in data:
            locals()[i] = data[i]
    return self.compact("parse")

def kdecrypt(self, key, sh, st, id, bh, iv):
    playAppKey = "V8oo0Or1f047NaiMTxK123LMFuINTNeI"
    hashkey = self.common.sha256("{}{}{}{}{}{}".format(sh, st, id, bh, iv, playAppKey))
    cipher = AES.new(unhexlify(hashkey), AES.MODE_ECB)
    plaintext = cipher.decrypt(unhexlify(key[0:32]))
    return "{}{}".format(bytes.decode(hexlify(plaintext)[0:32]), key[32:])

算法差不多是这样,可以当下参考

{
    "cid": "505779",
    "ext": "m3u8",
    "hd": 5,
    "multirates": 5,
    "parse": "http://sports.pptv.com/sportslive/pg_h5live?sectionid=172160&matchid=",
    "playback": "m3u8",
    "quality": [
        "流畅",
        "标清",
        "高清",
        "超清",
        "蓝光"
    ],
    "show": "蓝光",
    "site": "PPTV直播(PPTV)",
    "streams": {
        "m3u8": "http://27.159.123.226:80/live/5/30/557ce94254c89ce64edeb8d38530d64d.m3u8?k=03b935baf58f785f8adba83e96917eca-5592-1578687462%26bppcataid%3D38&type=web"
    },
    "title": "走进CBA全明星",
    "type": "pptv",
    "vid": "172160",
    "vtype": "live"
}

你好,照着你的代码我也读到了m3u8,http://live2.vcdn.pplive.cn/live/5/30/b2b336baa56db209f24028cdf5248752.m3u8?k=1b4068f4ffa97049072945b5779f5010-e9ac-1607201005%26bppcataid%3D38&type=web,但是读取里面一个分片ts信息返回了403,播放不了,有解决的思路吗

airdge commented 3 years ago
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@File    : pptv.py
@Time    : 2019/12/18 17:14  
"""

from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
import importlib

def query(self, p):
    logger = self.logging.getLogger("query")
    common = self.common
    if p["parse"].startswith("http"):
        vid = common.match("sectionid=(\d+)", p["parse"])
    else:
        vid = p["parse"]

    if p["query"]:
        url = (
            "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
            % vid
        )
        html = common.curl(url)
        logger.debug("section: " + html + "\r\n")
        json = common.loads(html)
        title = json["data"]["sectionInfo"]["title"]

    return self.compact("query")

def parse(self, p):
    logger = self.logging.getLogger("parse")
    common = self.common
    assert p["vid"], self.message.output("vid")
    vid = p["vid"]
    url = (
        "https://sportlive.suning.com/slsp-web/cms/competitionschedule/v1/detail/section.do?sectionid=%s&liveFlag=0&app=web&longzhu=0"
        % vid
    )
    html = common.curl(url)
    logger.debug("section: " + html + "\r\n")
    json = common.loads(html)
    title = json["data"]["sectionInfo"]["title"]
    # assert json["data"]["liveFlag"] == "1", self.message.output("close")
    if json["data"]["liveFlag"] == "1":
        cid = json["data"]["sectionInfo"]["lives"][0]["cid"]
        webplayParams = {
            "zone": "8",
            "vvid": "5199ebda-7ce8-6463-97e9-18254ef22e2b",
            "username": "",
            "param": "type=web&ahl_ver=1&ahl_random=5c6c25342d512d295f726b3838355827&ahl_signa=b2c418ee1293af26e29e732c1853317813b2b19cc481b993e517ca16d5ff5d41&userType=0&o=m.ppsport.com",
            "isSports": "1",
            "vts": "0",
            "pageUrl": "https://sports.pptv.com/sportslive?sectionid=171471",
            "o": "m.ppsport.com",
            "type": "web",
            "salt": "pv",
            "sl": "1",
            "referrer": "https://m.ppsport.com/live/z5D1c9tBse9S0Dg",
            "version": "6",
            "appplt": "flp",
            "appid": "pptv.flashplayer.live",
            "appver": "2.12.59",
            "nddp": "1",
        }
        webplayUrl = "https://web-play.pptv.com/webplay3-0-{}.xml?{}".format(
            cid, common.urlencode(webplayParams)
        )
        webplayHtml = common.curl(webplayUrl)
        logger.debug("xml: " + webplayHtml + "\r\n")
        dtList = common.matchall("<dt(.*?)<\/dt>", webplayHtml.replace("\n", ""))
        assert dtList, self.message.output("lists")

        # common.data函数,列表按hd取一组数据
        dt = common.data(dtList, p["hd"])
        rid = common.match('rid="(\w+)', dt)

        ary = ["流畅", "标清", "高清", "超清", "蓝光"]
        quality = ary[0 : len(dt)]
        show = common.data(quality, p["hd"])

        match = common.matchall(
            "<(\w+).*?>([^\<]+)<\/\w+>", dt.replace("<ip_serviceid/>", "")
        )
        array = {i[0]: i[1] for i in match}

        key = kdecrypt(
            self, array["key"], array["sh"], array["st"], "", array["bh"], array["iv"]
        )
        delay, interval = common.match("delay,(\d+);interval,(\d+)", webplayHtml)
        m3u8 = "http://%s/live/%s/%s/%s.m3u8?k=%s&type=web" % (
            array["sh"],
            interval,
            delay,
            rid,
            key,
        )
        ext = playback = "m3u8"
    elif json["data"]["liveFlag"] == "2":

        self.params["vtype"] = "video"

        afterCid = json["data"]["sectionInfo"]["lives"][0]["afterCid"]
        image = "http://s1.pplive.cn/v/cap/%s/w640.jpg" % afterCid
        imp = importlib.import_module("parse.video.pptv")
        data = imp.parse(self, {"vid": afterCid, "hd": p["hd"], "from": "pptv"})

        for i in data:
            locals()[i] = data[i]
    return self.compact("parse")

def kdecrypt(self, key, sh, st, id, bh, iv):
    playAppKey = "V8oo0Or1f047NaiMTxK123LMFuINTNeI"
    hashkey = self.common.sha256("{}{}{}{}{}{}".format(sh, st, id, bh, iv, playAppKey))
    cipher = AES.new(unhexlify(hashkey), AES.MODE_ECB)
    plaintext = cipher.decrypt(unhexlify(key[0:32]))
    return "{}{}".format(bytes.decode(hexlify(plaintext)[0:32]), key[32:])

算法差不多是这样,可以当下参考

{
    "cid": "505779",
    "ext": "m3u8",
    "hd": 5,
    "multirates": 5,
    "parse": "http://sports.pptv.com/sportslive/pg_h5live?sectionid=172160&matchid=",
    "playback": "m3u8",
    "quality": [
        "流畅",
        "标清",
        "高清",
        "超清",
        "蓝光"
    ],
    "show": "蓝光",
    "site": "PPTV直播(PPTV)",
    "streams": {
        "m3u8": "http://27.159.123.226:80/live/5/30/557ce94254c89ce64edeb8d38530d64d.m3u8?k=03b935baf58f785f8adba83e96917eca-5592-1578687462%26bppcataid%3D38&type=web"
    },
    "title": "走进CBA全明星",
    "type": "pptv",
    "vid": "172160",
    "vtype": "live"
}

你好,照着你的代码我也读到了m3u8,http://live2.vcdn.pplive.cn/live/5/30/b2b336baa56db209f24028cdf5248752.m3u8?k=1b4068f4ffa97049072945b5779f5010-e9ac-1607201005%26bppcataid%3D38&type=web,但是读取里面一个分片ts信息返回了403,播放不了,有解决的思路吗。

type=mhpptv