{"id":620,"date":"2025-04-08T01:25:51","date_gmt":"2025-04-07T17:25:51","guid":{"rendered":"https:\/\/www.gladguang.cn\/?p=620"},"modified":"2025-04-19T10:33:13","modified_gmt":"2025-04-19T02:33:13","slug":"08134e1f36a5c21342e89c0eb1b91484","status":"publish","type":"post","link":"https:\/\/www.gladguang.cn\/index.php\/2025\/04\/08\/08134e1f36a5c21342e89c0eb1b91484\/","title":{"rendered":"m3u8 python\u4e0b\u8f7d"},"content":{"rendered":"<div data-mode=\"python\">\n<pre><code>%pip list<\/code><\/pre>\n<\/div>\n<pre><code>Package                   Version\n------------------------- --------------\nanyio                     4.9.0\nargon2-cffi               23.1.0\nargon2-cffi-bindings      21.2.0\narrow                     1.3.0\nasttokens                 3.0.0\nasync-lru                 2.0.5\nattrs                     25.3.0\nbabel                     2.17.0\nbeautifulsoup4            4.13.3\nbleach                    6.2.0\ncertifi                   2025.1.31\ncffi                      1.17.1\ncharset-normalizer        3.4.1\ncomm                      0.2.2\ndebugpy                   1.8.13\ndecorator                 5.2.1\ndefusedxml                0.7.1\nexecuting                 2.2.0\nfastjsonschema            2.21.1\nfqdn                      1.5.1\nh11                       0.14.0\nhttpcore                  1.0.7\nhttpx                     0.28.1\nidna                      3.10\nipykernel                 6.29.5\nipython                   9.0.2\nipython_pygments_lexers   1.1.1\nipywidgets                8.1.5\nisoduration               20.11.0\njedi                      0.19.2\nJinja2                    3.1.6\njson5                     0.10.0\njsonpointer               3.0.0\njsonschema                4.23.0\njsonschema-specifications 2024.10.1\njupyter                   1.1.1\njupyter_client            8.6.3\njupyter-console           6.6.3\njupyter_core              5.7.2\njupyter-events            0.12.0\njupyter-lsp               2.2.5\njupyter_server            2.15.0\njupyter_server_terminals  0.5.3\njupyterlab                4.3.6\njupyterlab_pygments       0.3.0\njupyterlab_server         2.27.3\njupyterlab_widgets        3.0.13\nm3u8                      6.0.0\nMarkupSafe                3.0.2\nmatplotlib-inline         0.1.7\nmistune                   3.1.3\nnbclient                  0.10.2\nnbconvert                 7.16.6\nnbformat                  5.10.4\nnest-asyncio              1.6.0\nnotebook                  7.3.3\nnotebook_shim             0.2.4\noverrides                 7.7.0\npackaging                 24.2\npandocfilters             1.5.1\nparso                     0.8.4\npexpect                   4.9.0\npip                       23.0.1\nplatformdirs              4.3.7\nprometheus_client         0.21.1\nprompt_toolkit            3.0.50\npsutil                    7.0.0\nptyprocess                0.7.0\npure_eval                 0.2.3\npycparser                 2.22\nPygments                  2.19.1\npython-dateutil           2.9.0.post0\npython-json-logger        3.3.0\nPyYAML                    6.0.2\npyzmq                     26.3.0\nreferencing               0.36.2\nrequests                  2.32.3\nrfc3339-validator         0.1.4\nrfc3986-validator         0.1.1\nrpds-py                   0.24.0\nSend2Trash                1.8.3\nsetuptools                66.1.1\nsix                       1.17.0\nsniffio                   1.3.1\nsoupsieve                 2.6\nstack-data                0.6.3\nterminado                 0.18.1\ntinycss2                  1.4.0\ntornado                   6.4.2\ntqdm                      4.67.1\ntraitlets                 5.14.3\ntypes-python-dateutil     2.9.0.20241206\ntyping_extensions         4.13.0\nuri-template              1.3.0\nurllib3                   2.3.0\nwcwidth                   0.2.13\nwebcolors                 24.11.1\nwebencodings              0.5.1\nwebsocket-client          1.8.0\nwidgetsnbextension        4.0.13\nNote: you may need to restart the kernel to use updated packages.\n<\/code><\/pre>\n<p><!--more--><\/p>\n<div data-mode=\"python\">\n<pre><code>import os\nimport requests\nfrom m3u8 import M3U8\nfrom urllib.parse import urljoin\nimport tempfile\nimport subprocess\nfrom concurrent.futures import ThreadPoolExecutor\nfrom tqdm import tqdm\nimport threading\n\ndef download_ts_video(m3u8_url, output_mp4, headers=None, max_workers=4):\n    temp_dir = tempfile.mkdtemp()\n    lock = threading.Lock()\n    errors = []\n\n    try:\n        if headers is None:\n            headers = {\n                'User-Agent': 'Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/58.0.3029.110 Safari\/537.3',\n                'Referer': urljoin(m3u8_url, '\/')\n            }\n\n        # \u83b7\u53d6M3U8\u76ee\u5f55\u8def\u5f84\n        m3u8_dir = m3u8_url[:m3u8_url.rfind('\/') + 1]\n\n        # \u4e0b\u8f7d\u5e76\u89e3\u6790M3U8\n        response = requests.get(m3u8_url, headers=headers)\n        response.raise_for_status()\n        m3u8 = M3U8(response.text, base_uri=m3u8_dir)\n        ts_urls = [urljoin(m3u8_dir, seg.uri) for seg in m3u8.segments]\n\n        if not ts_urls:\n            raise ValueError(\"\u672a\u627e\u5230TS\u7247\u6bb5\")\n\n        # \u591a\u7ebf\u7a0b\u4e0b\u8f7d\n        print(f\"\u542f\u52a8 {max_workers} \u4e2a\u7ebf\u7a0b\u4e0b\u8f7d...\")\n        progress = tqdm(total=len(ts_urls), desc=\"\u4e0b\u8f7d\u8fdb\u5ea6\")\n\n        def download_segment(url, index):\n            try:\n                ts_file = os.path.join(temp_dir, f'segment_{index:04d}.ts')\n                if not os.path.exists(ts_file):  # \u907f\u514d\u91cd\u590d\u4e0b\u8f7d\n                    resp = requests.get(url, headers=headers, timeout=10)\n                    resp.raise_for_status()\n                    with open(ts_file, 'wb') as f:\n                        f.write(resp.content)\n                with lock:\n                    progress.update(1)\n            except Exception as e:\n                errors.append(f\"\u7247\u6bb5{index}\u4e0b\u8f7d\u5931\u8d25: {str(e)}\")\n                with lock:\n                    progress.set_description(f\"\u9519\u8bef\u6570: {len(errors)}\")\n\n        with ThreadPoolExecutor(max_workers=max_workers) as executor:\n            futures = []\n            for i, url in enumerate(ts_urls):\n                futures.append(executor.submit(download_segment, url, i))\n\n            # \u7b49\u5f85\u6240\u6709\u4efb\u52a1\u5b8c\u6210\n            for future in futures:\n                future.result()\n\n        progress.close()\n\n        # \u68c0\u67e5\u9519\u8bef\n        if errors:\n            print(\"\\n\u53d1\u751f\u4ee5\u4e0b\u9519\u8bef\uff1a\")\n            for error in errors[:3]:  # \u53ea\u663e\u793a\u524d3\u4e2a\u9519\u8bef\n                print(error)\n            if len(errors) &gt; 3:\n                print(f\"\uff08\u5171 {len(errors)} \u4e2a\u9519\u8bef\uff09\")\n            if input(\"\u662f\u5426\u7ee7\u7eed\u5408\u5e76\uff1f(y\/n)\").lower() != 'y':\n                return\n\n        # \u751f\u6210\u6587\u4ef6\u5217\u8868\n        ts_files = sorted([os.path.join(temp_dir, f) \n                         for f in os.listdir(temp_dir) if f.endswith('.ts')],\n                         key=lambda x: int(x.split('_')[-1].split('.')[0]))\n\n        file_list = os.path.join(temp_dir, 'file_list.txt')\n        with open(file_list, 'w', encoding='utf-8') as f:\n            for ts_file in ts_files:\n                f.write(f\"file '{os.path.abspath(ts_file)}'\\n\")\n\n        # \u5408\u5e76\u8f6c\u7801\n        print(\"\u5408\u5e76\u8f6c\u7801\u4e2d...\")\n        subprocess.run([\n            'ffmpeg',\n            '-f', 'concat',\n            '-safe', '0',\n            '-i', file_list,\n            '-c', 'copy',\n            '-y',\n            output_mp4\n        ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\n        print(f\"\\n\u89c6\u9891\u5df2\u4fdd\u5b58\u81f3\uff1a{os.path.abspath(output_mp4)}\")\n\n    finally:\n        import shutil\n        shutil.rmtree(temp_dir)\n\n# \u4f7f\u7528\u793a\u4f8b\n#'https:\/\/t26.cdn2020.com\/video\/m3u8\/index.m3u8',\nif __name__ == '__main__':\n\n    url = input(\"\u8bf7\u8f93\u5165m3u8\u94fe\u63a5\uff1a\")\n    print(\"\u60a8\u8f93\u5165\u7684\u94fe\u63a5\u540d\u4e3a\" + url)\n    name = '.\/move\/{movie_name}.mp4'.format(movie_name=input(\"\u8bf7\u8f93\u5165\u5f71\u7247\u540d\uff1a\"))\n    print(\"\u60a8\u5b58\u50a8\u5f71\u7247\u8def\u5f84\u4e3a\" +name)\n\n    download_ts_video(\n        m3u8_url= url,    \n        output_mp4= name,\n        max_workers=16\n    )\n<\/code><\/pre>\n<\/div>\n<pre><code>\u8bf7\u8f93\u5165m3u8\u94fe\u63a5\uff1a https:\/\/t26.cdn2020.com\/video\/m3u8\/index.m3u8\n\n\n\u60a8\u8f93\u5165\u7684\u94fe\u63a5\u540d\u4e3ahttps:\/\/t26.cdn2020.com\/video\/m3u8\/index.m3u8\n\n\n\u8bf7\u8f93\u5165\u5f71\u7247\u540d\uff1a 0fa806c8\n\n\n\u60a8\u5b58\u50a8\u5f71\u7247\u8def\u5f84\u4e3a.\/move\/0fa806c8.mp4\n\u542f\u52a8 16 \u4e2a\u7ebf\u7a0b\u4e0b\u8f7d...\n\n\n\u9519\u8bef\u6570: 2: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a| 1141\/1143 [17:43&lt;00:01,  1.07it\/s]\n\n\n\n\u53d1\u751f\u4ee5\u4e0b\u9519\u8bef\uff1a\n\u7247\u6bb5552\u4e0b\u8f7d\u5931\u8d25: HTTPSConnectionPool(host='t26.cdn2020.com', port=443): Read timed out.\n\u7247\u6bb5608\u4e0b\u8f7d\u5931\u8d25: HTTPSConnectionPool(host='t26.cdn2020.com', port=443): Read timed out. (read timeout=10)\n\n\n\u662f\u5426\u7ee7\u7eed\u5408\u5e76\uff1f(y\/n) n\n<\/code><\/pre>\n<div data-mode=\"python\">\n<pre><code><\/code><\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>%pip list Package Version &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- &#8212; [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[114,115],"tags":[],"class_list":["post-620","post","type-post","status-publish","format-standard","hentry","category-114","category-115"],"_links":{"self":[{"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/posts\/620","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/comments?post=620"}],"version-history":[{"count":2,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/posts\/620\/revisions"}],"predecessor-version":[{"id":647,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/posts\/620\/revisions\/647"}],"wp:attachment":[{"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/media?parent=620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/categories?post=620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gladguang.cn\/index.php\/wp-json\/wp\/v2\/tags?post=620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}