Skip to content
This repository was archived by the owner on Jan 9, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

详细信息和用法请见 [https://mooc.xoy.io/](https://mooc.xoy.io/)。

### m3u8下载脚本使用
依赖系统ffmpeg, 需要先安装ffmpeg,<https://www.ffmpeg.org/download.html> 在Ubuntu下已测试。

```sh
./download_m3u8.py -l 学术规范与论文写作/Videos/Videos.txt
```

### 声明

仅限个人学习和研究使用,切勿用于其他用途。强烈建议到 MOOC 网站进行学习,本程序只是提供一个备选方案。
Expand Down
25 changes: 25 additions & 0 deletions download_m3u8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import os
import sys
import argparse

if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Download m3u8 to mp4')
parser.add_argument('-l', default=r'', help='视频列表')
parser.add_argument('-d', default=r'', help='下载目录')
args = parser.parse_args()
video_list = os.path.abspath(args.l)

if args.d == '':
video_dir = os.path.dirname(video_list)
else:
video_dir = os.path.abspath(args.d)

with open(video_list) as f:
lines = f.readlines()
lines = [_.strip() for _ in lines if _ != '\n']
for line in lines:
name,url = line.split('\t')
print(name,url)
os.system('ffmpeg -i "{}" -c copy -bsf:a aac_adtstoasc "{}"'\
.format(url,os.path.join(video_dir,name)))
49 changes: 29 additions & 20 deletions mooc/icourse163.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import time
from .utils import *
import json

CANDY = Crawler()
CONFIG = {}
Expand All @@ -11,21 +12,21 @@

def get_summary(url):
"""从课程主页面获取信息"""
res = CANDY.get(url)
cookie_csrf = res.cookies.get('NTESSTUDYSI')
res_t = res.text

res = CANDY.get(url).text

term_id = re.search(r'termId : "(\d+)"', res).group(1)
names = re.findall(r'name:"(.+)"', res)
term_id = re.search(r'termId : "(\d+)"', res_t).group(1)
names = re.findall(r'name:"(.+)"', res_t)

dir_name = course_dir(*names[:2])

print(dir_name)
return term_id, dir_name
return term_id, dir_name, cookie_csrf


def parse_resource(resource):
"""解析资源地址和下载资源"""

post_data = {'callCount': '1', 'scriptSessionId': '${scriptSessionId}190',
'httpSessionId': '5531d06316b34b9486a6891710115ebc', 'c0-scriptName': 'CourseBean',
'c0-methodName': 'getLessonUnitLearnVo', 'c0-id': '0', 'c0-param0': 'number:' + resource.meta[0],
Expand All @@ -34,21 +35,28 @@ def parse_resource(resource):
res = CANDY.post('https://www.icourse163.org/dwr/call/plaincall/CourseBean.getLessonUnitLearnVo.dwr',
data=post_data).text


file_name = resource.file_name
if resource.type == 'Video':
resolutions = ['Shd', 'Hd', 'Sd']
for sp in resolutions[CONFIG['resolution']:]:
# TODO: 增加视频格式选择
# video_info = re.search(r'%sUrl="(?P<url>.*?(?P<ext>\.((m3u8)|(mp4)|(flv))).*?)"' % sp, res)
video_info = re.search(r'(?P<ext>mp4)%sUrl="(?P<url>.*?\.(?P=ext).*?)"' % sp, res)
if video_info:
url, ext = video_info.group('url', 'ext')
ext = '.' + ext
break
res_print(file_name + ext)
FILES['renamer'].write(re.search(r'(\w+\.((m3u8)|(mp4)|(flv)))', url).group(1), file_name, ext)
FILES['video'].write_string(url)
resource.ext = ext
post_data = {'bizId': resource.meta[2],
'bizType': '1',
'contentType': '1'}
url = 'https://www.icourse163.org/web/j/resourceRpcBean.getResourceToken.rpc?csrfKey={}'.format(CONFIG['_cookie_csrf'])
res = CANDY.post(url,data=post_data).text
res_json = json.loads(res)
if res_json.get('code', -1) != 0:
print('get signature error.')
return
sig = res_json['result']['videoSignDto']['signature']

url = 'https://vod.study.163.com/eds/api/v1/vod/video?videoId={}&signature={}&clientType=1'.format(resource.meta[0], sig)
res = CANDY.get(url).text
res_json = json.loads(res)
url = res_json['result']['videos'][-1]['videoUrl']
name = res_json['result']['name']
FILES['video'].write_string('{}\t{}'.format(name,url))
res_print('{}\t{}'.format(name, url))


if not CONFIG['sub']:
return
Expand Down Expand Up @@ -165,7 +173,8 @@ def start(url, config):
global WORK_DIR

CONFIG.update(config)
term_id, dir_name = get_summary(url)
term_id, dir_name, cookie_csrf = get_summary(url)
CONFIG['_cookie_csrf'] = cookie_csrf

WORK_DIR = WorkingDir(CONFIG['dir'], dir_name)
WORK_DIR.change('Videos')
Expand Down