{ "cells": [ { "cell_type": "code", "execution_count": 2, "id": "5ab50d0f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2023-10-21 13:58:26\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Downloading Meetings: 50%|█████████████▌ | 1/2 [00:17<00:17, 17.92s/ meeting]Exception in thread Thread-38 (download_range):\n", "Traceback (most recent call last):\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 467, in _make_request\n", " six.raise_from(e, None)\n", " File \"\", line 3, in raise_from\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 462, in _make_request\n", " httplib_response = conn.getresponse()\n", " ^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3.11/http/client.py\", line 1378, in getresponse\n", " response.begin()\n", " File \"/usr/lib/python3.11/http/client.py\", line 318, in begin\n", " version, status, reason = self._read_status()\n", " ^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3.11/http/client.py\", line 279, in _read_status\n", " line = str(self.fp.readline(_MAXLINE + 1), \"iso-8859-1\")\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3.11/socket.py\", line 706, in readinto\n", " return self._sock.recv_into(b)\n", " ^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3.11/ssl.py\", line 1311, in recv_into\n", " return self.read(nbytes, buffer)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3.11/ssl.py\", line 1167, in read\n", " return self._sslobj.read(len, buffer)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", "TimeoutError: The read operation timed out\n", "\n", "During handling of the above exception, another exception occurred:\n", "\n", "Traceback (most recent call last):\n", " File \"/usr/lib/python3/dist-packages/requests/adapters.py\", line 486, in send\n", " resp = conn.urlopen(\n", " ^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 799, in urlopen\n", " retries = retries.increment(\n", " ^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/urllib3/util/retry.py\", line 550, in increment\n", " raise six.reraise(type(error), error, _stacktrace)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/six.py\", line 719, in reraise\n", " raise value\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 715, in urlopen\n", " httplib_response = self._make_request(\n", " ^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 469, in _make_request\n", " self._raise_timeout(err=e, url=url, timeout_value=read_timeout)\n", " File \"/usr/lib/python3/dist-packages/urllib3/connectionpool.py\", line 358, in _raise_timeout\n", " raise ReadTimeoutError(\n", "urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='internal-api-drive-stream.feishu.cn', port=443): Read timed out. (read timeout=30)\n", "\n", "During handling of the above exception, another exception occurred:\n", "\n", "Traceback (most recent call last):\n", " File \"/usr/lib/python3.11/threading.py\", line 1045, in _bootstrap_inner\n", " self.run()\n", " File \"/usr/lib/python3.11/threading.py\", line 982, in run\n", " self._target(*self._args, **self._kwargs)\n", " File \"/tmp/ipykernel_25915/2918984942.py\", line 45, in download_range\n", " File \"/usr/lib/python3/dist-packages/requests/api.py\", line 73, in get\n", " return request(\"get\", url, params=params, **kwargs)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/requests/api.py\", line 59, in request\n", " return session.request(method=method, url=url, **kwargs)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/requests/sessions.py\", line 589, in request\n", " resp = self.send(prep, **send_kwargs)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/requests/sessions.py\", line 703, in send\n", " r = adapter.send(request, **kwargs)\n", " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " File \"/usr/lib/python3/dist-packages/requests/adapters.py\", line 532, in send\n", " raise ReadTimeout(e, request=request)\n", "requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='internal-api-drive-stream.feishu.cn', port=443): Read timed out. (read timeout=30)\n", "Downloading Meetings: 100%|███████████████████████████| 2/2 [00:50<00:00, 25.42s/ meeting]\n", "Deleting Meetings: 100%|██████████████████████████████| 1/1 [00:00<00:00, 1.18 meeting/s]\n" ] }, { "ename": "KeyboardInterrupt", "evalue": "", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[2], line 227\u001b[0m\n\u001b[1;32m 225\u001b[0m downloader\u001b[38;5;241m.\u001b[39mcheck_meetings()\n\u001b[1;32m 226\u001b[0m downloader\u001b[38;5;241m.\u001b[39mdelete_minutes(\u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m--> 227\u001b[0m \u001b[43mtime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msleep\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3600\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 229\u001b[0m \u001b[38;5;66;03m# 如果填写了管理参数,则定时查询妙记空间使用情况,超出指定额度则删除最早的指定数量的会议\u001b[39;00m\n\u001b[1;32m 230\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m :\n\u001b[1;32m 231\u001b[0m \u001b[38;5;66;03m# 从manager_cookie中获取X-Csrf-Token\u001b[39;00m\n\u001b[1;32m 232\u001b[0m x_csrf_token \u001b[38;5;241m=\u001b[39m manager_cookie[manager_cookie\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m csrf_token=\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;241m+\u001b[39m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m csrf_token=\u001b[39m\u001b[38;5;124m'\u001b[39m):manager_cookie\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m;\u001b[39m\u001b[38;5;124m'\u001b[39m, manager_cookie\u001b[38;5;241m.\u001b[39mfind(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m csrf_token=\u001b[39m\u001b[38;5;124m'\u001b[39m))]\n", "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } ], "source": [ "import os, re, shutil, time, threading\n", "import requests\n", "from tqdm import tqdm\n", "\n", "\n", "# 不使用系统代理\n", "proxies = {\"http\": None, \"https\": None}\n", "\n", "# 多线程下载器\n", "class MultiDownloader:\n", " def __init__(self, headers, url, file_name, thread_count=20):\n", " self.headers = headers\n", " self.url = url\n", " self.file_name = file_name\n", " self.thread_count = thread_count\n", " self.chunk_size = 1024 * 1024\n", " self.total_range = self.get_file_size()\n", " self.file_lock = threading.Lock()\n", "\n", " def get_file_size(self):\n", " res = requests.head(self.url, headers=self.headers, proxies=proxies)\n", " if res.status_code == 200:\n", " return int(res.headers.get('Content-Length'))\n", " return None\n", "\n", " def page_dispatcher(self, content_size):\n", " page_size = content_size // self.thread_count\n", " start_pos = 0\n", " while start_pos + page_size < content_size:\n", " yield {\n", " 'start_pos': start_pos,\n", " 'end_pos': start_pos + page_size\n", " }\n", " start_pos += page_size + 1\n", " yield {\n", " 'start_pos': start_pos,\n", " 'end_pos': content_size - 1\n", " }\n", "\n", " def download_range(self, thread_name, page, file_handler):\n", " range_headers = {\"Range\": f\"bytes={page['start_pos']}-{page['end_pos']}\"}\n", " range_headers |= self.headers\n", " try_times = 3\n", " for _ in range(try_times):\n", " with requests.get(url=self.url, headers=range_headers, stream=True, timeout=30, proxies=proxies) as res:\n", " if res.status_code == 206:\n", " for data in res.iter_content(chunk_size=self.chunk_size):\n", " with self.file_lock:\n", " file_handler.seek(page[\"start_pos\"])\n", " file_handler.write(data)\n", " page[\"start_pos\"] += len(data)\n", " break\n", "\n", " def run(self):\n", " if not self.total_range or self.total_range < 1024:\n", " raise Exception(\"get file total size failed\")\n", " if os.path.exists(self.file_name.split('/')[0]) and os.path.exists(self.file_name) and os.path.getsize(self.file_name) != 0:\n", " while True:\n", " choice = input(f\" {self.file_name.split('/')[0]}已存在,是否覆盖(Y/n)?\")\n", " if choice == 'y' or choice == 'Y' or choice == '':\n", " break\n", " elif choice == 'n' or choice == 'N':\n", " return\n", " else:\n", " os.mkdir(self.file_name.split('/')[0])\n", " thread_list = []\n", " with open(self.file_name, \"wb+\") as f:\n", " for i, page in enumerate(self.page_dispatcher(self.total_range)):\n", " thread_list.append(threading.Thread(target=self.download_range, args=(i, page, f)))\n", " for thread in thread_list:\n", " thread.start()\n", " for thread in thread_list:\n", " thread.join()\n", "\n", "\n", "# 会议下载器\n", "class MeetingDownloader:\n", " def __init__(self, cookie):\n", " self.headers = {\n", " 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',\n", " 'cookie': cookie,\n", " 'bv-csrf-token': cookie[cookie.find('bv_csrf_token=') + len('bv_csrf_token='):cookie.find(';', cookie.find('bv_csrf_token='))],\n", " 'referer': f'https://meetings.feishu.cn/minutes/me',\n", " 'content-type': 'application/x-www-form-urlencoded'\n", " }\n", " if len(self.headers.get('bv-csrf-token')) != 36:\n", " raise Exception(\"cookie中不包含bv_csrf_token,请确保从请求`list?size=20&`中获取!\")\n", " \n", " def get_meeting_info(self):\n", " \"\"\"\n", " 批量获取妙记信息\n", "\n", " size的取值:获取的妙记数量\n", "\n", " space_name的取值:\n", " 1:主页(包含企业内部妙记与外部妙记)\n", " 2:我的内容(只包含归属人为自己的妙记)\n", " \"\"\"\n", " get_rec_url = f\"https://meetings.feishu.cn/minutes/api/space/list?&size=1000&space_name=2\"\n", " resp = requests.get(url=get_rec_url, headers=self.headers, proxies=proxies)\n", " return list(reversed(resp.json()['data']['list'])) # 返回按时间正序排列的妙记信息(从旧到新)\n", "\n", " def download_video(self, minutes_info):\n", " \"\"\"\n", " 下载单个妙记视频\n", " \"\"\"\n", " # 获取妙记视频的下载链接\n", " video_url_url = f\"https://meetings.feishu.cn/minutes/api/status?object_token={minutes_info['object_token']}&language=zh_cn&_t={int(time.time() * 1000)}\"\n", " resp = requests.get(url=video_url_url, headers=self.headers, proxies=proxies)\n", " video_url = resp.json()['data']['video_info']['video_download_url']\n", "\n", " # 根据会议的起止时间和会议标题来设置文件名\n", " start_time = time.strftime(\"%Y年%m月%d日%H时%M分\", time.localtime(minutes_info['start_time'] / 1000))\n", " stop_time = time.strftime(\"%Y年%m月%d日%H时%M分\", time.localtime(minutes_info['stop_time'] / 1000))\n", " file_name = start_time+\"至\"+stop_time+minutes_info['topic']\n", "\n", " # 将文件名中的特殊字符替换为下划线\n", " rstr = r\"[\\/\\\\\\:\\*\\?\\\"\\<\\>\\|]\" # '/ \\ : * ? \" < > |'\n", " file_name = re.sub(rstr, \"_\", file_name)\n", "\n", " # 多线程下载\n", " run_params = {'headers': self.headers,\n", " 'url': video_url,\n", " 'file_name': f'{file_name}/{file_name}.mp4',\n", " 'thread_count': 20\n", " }\n", " downloader = MultiDownloader(**run_params)\n", " downloader.run()\n", "\n", " return file_name\n", "\n", " def download_subtitle(self, object_token, file_name, file_mtime):\n", " \"\"\"\n", " 下载单个妙记字幕\n", " \"\"\"\n", " srt_url = f\"https://meetings.feishu.cn/minutes/api/export\"\n", " params = {'add_speaker': 'true', # 包含说话人\n", " 'add_timestamp': 'true', # 包含时间戳\n", " 'format': '3', # SRT格式\n", " 'object_token': object_token, # 妙记id\n", " }\n", " resp = requests.post(url=srt_url, params=params, headers=self.headers, proxies=proxies)\n", "\n", " # 如果cookie选择的不对,可能会出现能下载视频但无法下载字幕的情况\n", " if resp.status_code != 200:\n", " raise Exception(f\"下载字幕失败,请检查你的cookie!\\nStatus code: {resp.status_code}\")\n", " \n", " # 写入对应视频的文件夹\n", " resp.encoding = \"utf-8\"\n", " with open(f\"{file_name}/{file_name}.srt\", \"w+\") as f:\n", " f.write(resp.text)\n", "\n", " # 将文件最后修改时间改为会议结束时间\n", " os.utime(f\"{file_name}/{file_name}.srt\", (file_mtime, file_mtime))\n", " os.utime(f\"{file_name}/{file_name}.mp4\", (file_mtime, file_mtime))\n", " os.utime(f\"{file_name}\", (file_mtime, file_mtime))\n", "\n", " def check_meetings(self):\n", " \"\"\"\n", " 检查需要下载的会议\n", " \"\"\"\n", " all_meetings = self.get_meeting_info()\n", " need_download_meetings = []\n", "\n", " # 检查记录中不存在的会议id进行下载\n", " if os.path.exists('meetings.txt'):\n", " with open('meetings.txt', 'r') as f:\n", " downloaded_meetings = f.readlines()\n", " need_download_meetings = [index for index in all_meetings if index['meeting_id']+'\\n' not in downloaded_meetings]\n", " else:\n", " need_download_meetings = all_meetings\n", " # 如果有需要下载的会议则进行下载\n", " if need_download_meetings:\n", " for index in tqdm(need_download_meetings, desc='Downloading Meetings', unit=' meeting'):\n", " # 下载妙记视频\n", " file_name = self.download_video(index)\n", " # 下载妙记字幕\n", " self.download_subtitle(index['object_token'], file_name, index['stop_time']/1000)\n", " # 将已下载的妙记所对应的会议id记录到文件中\n", " with open('meetings.txt', 'a+') as f:\n", " f.write(index['meeting_id'] + '\\n')\n", "\n", " def delete_minutes(self, num):\n", " \"\"\"\n", " 删除指定数量的最早几个妙记\n", " \"\"\"\n", " all_meetings = self.get_meeting_info()\n", " num = num if num <= len(all_meetings) else 1\n", " need_delete_meetings = all_meetings[:num]\n", "\n", " for index in tqdm(need_delete_meetings, desc='Deleting Meetings', unit=' meeting'):\n", " \n", " # 将该妙记放入回收站\n", " delete_url = f\"https://meetings.feishu.cn/minutes/api/space/delete\"\n", " params = {'object_tokens': index['object_token'],\n", " 'is_destroyed': 'false',\n", " 'language': 'zh_cn'}\n", " resp = requests.post(url=delete_url, params=params, headers=self.headers, proxies=proxies)\n", " if resp.status_code != 200:\n", " raise Exception(f\"删除会议{index['meeting_id']}失败!{resp.json()}\")\n", " \n", " # 将该妙记彻底删除\n", " params['is_destroyed'] = 'true'\n", " resp = requests.post(url=delete_url, params=params, headers=self.headers, proxies=proxies)\n", " if resp.status_code != 200:\n", " raise Exception(f\"删除会议{index['meeting_id']}失败!{resp.json()}\")\n", "\n", "\n", "if __name__ == '__main__':\n", "\n", " # 在飞书妙记主页 https://meetings.feishu.cn/minutes/home 获取cookie\n", " minutes_cookie = \"minutes_csrf_token=ea0b6372-d541-49fd-9fca-e6e579ed6c56; m_ce8f16=65613062363337322d643534312d343966642d396663612d653665353739656436633536b37b91f4efa89b27b410d5626626f1b2ebf3ed82d6b510030362344fb5776178; __tea__ug__uid=2399591691287585247; Hm_lvt_e78c0cb1b97ef970304b53d2097845fd=1691287586; Hm_lpvt_e78c0cb1b97ef970304b53d2097845fd=1691287586; _gcl_au=1.1.1631485248.1691287586; passport_web_did=7264024950279913500; QXV0aHpDb250ZXh0=825bfa61b8ce47d6a89b987cdb581ff9; locale=zh-CN; trust_browser_id=48711549-c255-44b4-820f-442ea9608738; fid=80a777ac-f638-404f-8e7c-0902ac3cf140; lang=zh; _csrf_token=d0a421e1a93d873197fbf7134ab4c8f6a76769dd-1697598422; landing_url=https://login.feishu.cn/accounts/page/login?redirect_uri=https%3A%2F%2Fmeetings.feishu.cn%2Fminutes%2Fhome&app_id=16&should_pass_through=1&from=byteview_meeting_object; _ga=GA1.2.1302241623.1691287588; _gid=GA1.2.2068635412.1697867029; session=XN0YXJ0-31cg8369-b829-495d-ab17-8d31a56fc100-WVuZA; session_list=XN0YXJ0-31cg8369-b829-495d-ab17-8d31a56fc100-WVuZA; bv_csrf_token=25758c71-aa97-4174-b6e0-92625ce7ea3d; m_e09b70=32353735386337312d616139372d343137342d623665302d393236323563653765613364b37b91f4efa89b27b410d5626626f1b2ebf3ed82d6b510030362344fb5776178; _ga_VPYRHN104D=GS1.1.1697867029.2.1.1697867180.0.0.0; MM_U_ID=006c90d75f171009579f3ec6e755529eb47d86a0; sl_session=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTc5MTAzODIsInVuaXQiOiJldV9uYyIsInJhdyI6eyJtZXRhIjoiQVYrN0Z3OUxBSUFEWDdzWEQwSEJBQVJrendBMHNRSkFIR1RQQURTeEFrQWNaVE5scXN6QVFCd0NLZ0VBUVVGQlFVRkJRVUZCUVVKc1RUSlhjVEkwVWtGQlVUMDkiLCJpZGMiOlsxLDJdLCJzdW0iOiJlNDhlNDZjMTIzZWM1ZTk3MGIxYjY1OWU1MmUxOTUwNmEwNmQ2ZDAwYmNjMjY1MjllYzYzMzQwY2QxOThiMTNmIiwibG9jIjoiemhfY24iLCJhcGMiOiJSZWxlYXNlIiwiaWF0IjoxNjk3ODY3MTgyLCJzYWMiOnsiVXNlclN0YWZmU3RhdHVzIjoiMSIsIlVzZXJUeXBlIjoiNDIifSwibG9kIjpudWxsLCJucyI6ImxhcmsiLCJuc191aWQiOiI2ODk4MTMyNjA4Njk4MzE4ODUxIiwibnNfdGlkIjoiNjg5ODEzMjYwODU0MzE2MjM3MiIsIm90IjowfX0.JfoJxXHUlKLPJGpM3Td-Qg2dAYG3ntlBqOgbCKO5XU33lHGELeZhq7dYZnp8tgKnQ3QhlPO9NlAz_fU8zEeb7Q; home8e9bfaeded6957ef07dfd71b5753f855065e9207={%22filterOption%22:{%22rankType%22:1%2C%22order%22:%22desc%22}%2C%22objectOwnerType%22:1%2C%22recentOpenTab%22:1%2C%22timeColumnKey%22:%22time%22}; passport_app_access_token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTc5MTAzOTgsInVuaXQiOiJldV9uYyIsInJhdyI6eyJtX2FjY2Vzc19pbmZvIjp7IjE2Ijp7ImlhdCI6MTY5Nzg2NzE4NSwiYWNjZXNzIjp0cnVlfSwiMiI6eyJpYXQiOjE2OTc4NjcxOTgsImFjY2VzcyI6dHJ1ZX19LCJzdW0iOiJlNDhlNDZjMTIzZWM1ZTk3MGIxYjY1OWU1MmUxOTUwNmEwNmQ2ZDAwYmNjMjY1MjllYzYzMzQwY2QxOThiMTNmIn19.9kJ3WnK16ZwRTfiXw5536Wcmf2zPzVKFMTVrW3ajrOF1Xs_6ewG8q_t_jPrpC3KmV5HzQZkX5WpHoYiBt16RXA; is_anonymous_session=; _tea_utm_cache_1229=undefined; shortscc=3; swp_csrf_token=041eb6e5-1ad7-4d53-b0b2-819a3f66a214; t_beda37=978c60ee89273d7dea2a9898b4a964a5cb2330836f28d8a5726141475f44ad4d\"\n", "\n", " # (可选,需身份为企业创建人、超级管理员或普通管理员)在飞书管理后台获取cookie\n", " manager_cookie = \"\"\n", "\n", " if not minutes_cookie:\n", " raise Exception(\"cookie不能为空!\")\n", " \n", " # 如果未填写管理参数,则定时下载会议\n", " elif not manager_cookie:\n", " while True:\n", " print(time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime()))\n", " downloader = MeetingDownloader(minutes_cookie)\n", " downloader.check_meetings()\n", " downloader.delete_minutes(1)\n", " time.sleep(3600)\n", "\n", " # 如果填写了管理参数,则定时查询妙记空间使用情况,超出指定额度则删除最早的指定数量的会议\n", " else :\n", " # 从manager_cookie中获取X-Csrf-Token\n", " x_csrf_token = manager_cookie[manager_cookie.find(' csrf_token=') + len(' csrf_token='):manager_cookie.find(';', manager_cookie.find(' csrf_token='))]\n", " if len(x_csrf_token) != 36:\n", " raise Exception(\"manager_cookie中不包含csrf_token,请确保从请求`count?_t=`中获取!\")\n", "\n", " usage_bytes_old = 0 # 上次记录的已经使用的字节数\n", "\n", " # 定期查询已使用的妙记空间字节数\n", " while True:\n", " print(time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime()))\n", "\n", " # 查询妙记空间已用字节数\n", " query_url = f\"https://www.feishu.cn/suite/admin/api/gaea/usages\"\n", " manager_headers = {'cookie': manager_cookie, 'X-Csrf-Token':x_csrf_token}\n", " res = requests.get(url=query_url, headers=manager_headers, proxies=proxies)\n", " usage_bytes = int(res.json()['data']['items'][6]['usage']) # 查询到的目前已用字节数\n", " print(f\"已用空间:{usage_bytes / 2 ** 30:.2f}GB\")\n", "\n", " # 如果已用字节数有变化则下载会议\n", " if usage_bytes != usage_bytes_old:\n", " downloader = MeetingDownloader(minutes_cookie)\n", " downloader.check_meetings()\n", " # 如果已用超过9.65G则删除最早的两个会议\n", " if usage_bytes > 2 ** 30 * 9.65:\n", " downloader.delete_minutes(2)\n", " usage_bytes_old = usage_bytes # 更新已用字节数\n", " time.sleep(3600)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "9712c75e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.6" } }, "nbformat": 4, "nbformat_minor": 5 }