关键词:
郑重声明: 本人已购买该付费课程, 必须提前购买课程才可以下载。不是广告!
写这篇文章的目的是为了方便在本地播放和保存视频.
流程
- 获取播放列表
- 通过播放列表获取视频地址并生成Aria2c批量下载URL(指定文件名)
- 使用Aria2c进行下载视频
开始分析
打开DevTools,进入已购买的课程页面,点击开始学习
按钮,在DevTools筛选器上搜索all_lesson_list
此时获取到了一个URL,这个连接是所有课程数据列表,https://www.cctalk.com/webapi/content/v1.2/series/all_lesson_list?seriesId=1648563754438470&limit=&showStudyTime=false&_timestamp=1663807475982
seriesId=1648563754438470是课程ID在下面URL中使用。
继续通过分析从该链接https://www.cctalk.com/webapi/content/v1.1/series/1648563754438470/get_content_unit_struts
获取json数据,该数据中contentIdList
就是所有课程视频ID => videoId
"data":
"seriesId": 1648563754438470,
"showType": 0,
"totalCount": 83,
"unitList": [
"contentIdList": [
16486908151117,
......
],
"unitId": 0
]
,
"message": "success",
"status": 0,
"time": "2022-09-22T09:24:25+0800"
通过上面的数据获取到了必要参数后,现在需要获取单个视频的json数据,经过分析发现通过该URLhttps://www.cctalk.com/webapi/content/v1.1/video/detail?videoId=16486908151117&seriesId=1648563754438470
可以获取到视频信息。
- videoId=16486908151117 视频ID
- seriesId=1648563754438470 课程ID
这2个参数是从上面步骤获取到的.
返回数据如下,在这里核心数据为videoUrl
这个是视频URL,通过访问该URL可以在线播放,也可以下载视频.同时也需要videoName
它是视频标题,使用它在下面下载视频保存文件时指定文件名.
"data":
"adEnable": true,
"collectInfo":
"collectCount": 2,
"collectStatus": 0,
"collectionContentId": 5096,
"contentId": 16486908151117,
"contentType": 1
,
"companyId": 0,
"coursewareInfo":
"coursewareId": "1509184337896175193",
"ocsStatus": 0,
"tenantId": 10002,
"userSign": "K0JPWgQeSLvrlK551YiXHo3Xuq/TnKdqQ3sTnwo4LVUXcIN4HGEBPvvXtvGKsjPN1LVV+290fyfn2MGBdIu3jrlJxZ9f5JZeTmFbAPRSQ62rIgTK0hqizoUr+jyx+ne77tLjdTNQQd4ISF7XpJ+lsspshn6gu9dIB0T634KPiBEPMheGNHXq6xkEv3dE3aqtXTbPvzWGk27ezVG5qHpAlu8eTdIsAhepVtc5uF4fsPYopKlbXujRYRlg1NVUmdPXHOX3/gg8yFzpxDEb2u/pC5XFkYYJ5ztdBGtxA7r1OoSmMg/b2JaOenmkrd/Cze2PxBEWKt7PM2AUphUMwm0re6EeAf2aw5TlFksl6+WkCcxkSoUtVtw6ValjGo5AxWieNc5itJpA8OQ8TenBiBhikvrQiToJBh5Aw1QvfhgAVht7LcOUDOhFd2wAEJRoZMuPkGOAbixXVhV/5i9+80lg/3WXrRuCi8lDkn1W+NPsCVvDh0P8WKX3teCgfBXYR9Gl"
,
"coverUrl": "https://cc.hjfile.cn/cc/img/20220330/2022033011024002972564.jpg",
"createUserId": xxxxxx,
"enableCache": 1,
"feedbackInfo":
"haveFeedback": 0
,
"groupId": 90268531,
"groupIsActive": 1,
"groupName": "vol框架开发实战讲解.NetCore vue uni-app",
"hideRating": false,
"hideReserveButton": false,
"intro":
"section": [
"contentStruct": ,
"contents": []
]
,
"isCharge": 1,
"isGroupMember": true,
"isOpenProtection": false,
"isReserve": true,
"isTrial": false,
"knowledgeList": [],
"likeCount": 2,
"likeStatus": 0,
"liveNum": 0,
"liveStartDate": "2022-03-31T09:40:15+0800",
"liveStatus": 11,
"logo": "vol",
"logoShowStatus": 0,
"mediaTotalTime": 411000,
"openType": 1,
"pagePermission": 1,
"playCount": 1881,
"playPermission": 1,
"ratingCount": 6,
"reserveCount": 0,
"reviewStatus": 21,
"seriesInfo":
"coverUrl": "https://cc.hjfile.cn/cc/img/20220329/2022032910204179033032.jpg",
"hasSubscribe": true,
"seriesId": 1648563754438470,
"seriesName": "vol框架视频",
"subscribeCount": 336,
"videoCount": 83
,
"showRecommend": false,
"starAvg": 5.0,
"studyPosition": 0,
"taskInfo": ,
"totalSize": 40894464,
"trialDuration": -1,
"userCard":
"avatar": "https://i2n.hjfile.cn/u/default/ext/200/5.jpg",
"followedMeCount": 366,
"nickName": "vol",
"relationStatus": 1,
"summary": "多年.net、vue前后端分离开发经验.",
"userId": xxxxxx,
"userName": "QQ用户xxxx"
,
"videoCut": 0,
"videoEndDate": "2022-03-31T09:47:06+0800",
"videoId": 16486908151117,
"videoName": "框架介绍",
"videoStartDate": "2022-03-31T09:40:15+0800",
"videoType": 0,
"videoUrl": "https://record-manual.cctalk.com/5285d79daf63dd321eb89d912a9236d5.mp4?auth_key=1663823958-58b24996728341e69d9ec573d356f669-0-0606fbe337ddd193046cff6ffc121ee9"
,
"message": "success",
"status": 0
到目前为止已经完成了基本的分析,下面开始写代码
c#代码
在下面代码中需要填写2个参数,第一个是填写自己的COOKIE,使用自己的账号登录后,从浏览器中复制cookie,第二个是课程ID,await Getcourse("1648563754438470");
这里传递的参数是课程ID
使用nuget
添加以下2个扩展,同时本地需要安装redis数据库,如果没有可以改代码,使用SortedList
代替redis
- StackExchange.Redis
- Newtonsoft.Json
Program.cs
// See https://aka.ms/new-console-template for more information
using StackExchange.Redis;
using System.Net.Http.Headers;
using System.Net;
using Newtonsoft.Json;
using System;
using Newtonsoft.Json.Converters;
using System.Globalization;
using System;
using System.Collections.Generic;
using static CCtalk.Program;
namespace CCtalk
class Program
public static string global_cookies = "填写自己的COOKIE";//使用自己的账号登录后,从浏览器中复制cookie
//连接redis服务器
public static ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
//获取redis数据库
public static IDatabase db = redis.GetDatabase();
static async Task Main(string[] args)
//获取课程所有视频ID
await Getcourse("1648563754438470");
//获取最终视频播放地址并把地址写入txt文件,最后使用aria2c工具进行批量下载
await Getcontent();
/// <summary>
/// 获取最终视频播放地址并把地址写入txt文件,最后使用aria2c工具进行批量下载
/// </summary>
/// <returns></returns>
public static async Task Getcontent()
//获取桌面路径
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
//在桌面生成cctalk.txt文件
using (FileStream fs = new FileStream(desktopPath + @"\\cctalk.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
//获取redis数据
var full_redis_json_data = await db.HashGetAllAsync("cctalk_contentIdLists");
//遍历数据
foreach (var item in full_redis_json_data)
//获取单个视频JSON数据,提取VideoName和videoUrl
var result = await Browser(item.Value.ToString());
//解析json数据
var contents = Contents.FromJson(result);
//把数据保存到redis数据库,进行备份操作,方便后续使用,也可以使用自己编写代码进行下载视频.
// key为1. 框架介绍.mp4,value为最终视频URL地址
await db.HashSetAsync("cctalk_VideoLists", item.Name.ToString() + "." + contents.Data.VideoName.ToString() + ".mp4", contents.Data.VideoUrl.ToString());
//把数据写入txt文件,输入格式为Aria2c下载格式并指定文件名
Task task = sw.WriteLineAsync(contents.Data.VideoUrl.ToString() + "\\r\\n" + " out=" + item.Name.ToString() + "." + contents.Data.VideoName.ToString() + ".mp4");
/// <summary>
/// 获取课程所有视频ID
/// </summary>
/// <param name="seriesId">课程ID</param>
/// <returns></returns>
public static async Task Getcourse(string seriesId)
var kechengURL = "https://www.cctalk.com/webapi/content/v1.1/series/" + seriesId.ToString() + "/get_content_unit_struts";
string url_begin = "https://www.cctalk.com/webapi/content/v1.1/video/detail?videoId=";
string url_end = "&seriesId="+ seriesId.ToString()+ "&_timestamp=1663738877186";
//获取JSON数据
var result = await Browser(kechengURL);
//解析JSON数据
var course = Course.FromJson(result);
var i = 1;
//遍历course,获取所有视频ID
foreach (var item in course.Data.UnitList[0].ContentIdList)
//拼接完整单个视频访问URL地址
var final_url = url_begin + item.ToString() + url_end;
//将视频ID和完整URL存入redis数据库, key为i,value为完整URL地址
var redis_hastset = await db.HashSetAsync("cctalk_contentIdLists", i.ToString(), final_url.ToString());
i += 1;
public partial class Course
[JsonProperty("data")]
public Data Data get; set;
[JsonProperty("message")]
public string Message get; set;
[JsonProperty("status")]
public long Status get; set;
[JsonProperty("time")]
public string Time get; set;
public partial class Data
[JsonProperty("seriesId")]
public long SeriesId get; set;
[JsonProperty("showType")]
public long ShowType get; set;
[JsonProperty("totalCount")]
public long TotalCount get; set;
[JsonProperty("unitList")]
public UnitList[] UnitList get; set;
public partial class UnitList
[JsonProperty("contentIdList")]
public long[] ContentIdList get; set;
[JsonProperty("unitId")]
public long UnitId get; set;
public partial class Course
public static Course FromJson(string json) => JsonConvert.DeserializeObject<Course>(json, Converter.Settings);
public partial class Contents
[JsonProperty("data")]
public Data Data get; set;
[JsonProperty("message")]
public string Message get; set;
[JsonProperty("status")]
public long Status get; set;
public partial class Data
[JsonProperty("adEnable")]
public bool AdEnable get; set;
[JsonProperty("collectInfo")]
public CollectInfo CollectInfo get; set;
[JsonProperty("companyId")]
public long CompanyId get; set;
[JsonProperty("coursewareInfo")]
public CoursewareInfo CoursewareInfo get; set;
[JsonProperty("coverUrl")]
public Uri CoverUrl get; set;
[JsonProperty("createUserId")]
public long CreateUserId get; set;
[JsonProperty("enableCache")]
public long EnableCache get; set;
[JsonProperty("feedbackInfo")]
public FeedbackInfo FeedbackInfo get; set;
[JsonProperty("groupId")]
public long GroupId get; set;
[JsonProperty("groupIsActive")]
public long GroupIsActive get; set;
[JsonProperty("groupName")]
public string GroupName get; set;
[JsonProperty("hideRating")]
public bool HideRating get; set;
[JsonProperty("hideReserveButton")]
public bool HideReserveButton get; set;
[JsonProperty("intro")]
public Intro Intro get; set;
[JsonProperty("isCharge")]
public long IsCharge get; set;
[c#中利用npoi包操作excel文件(代码片段)
...供ISheet、IRow、ICell等接口usingNPOI.XSSF.UserModel;//提供相关类操作扩展名为xlsx的2007之后版本E 查看详情
你不一定知晓的c#取消异步操作(代码片段)
原文:【你不一定知晓的】C#取消异步操作在.Net和C#中运行异步代码相当简单,因为我们有时候需要取消正在进行的异步操作,通过本文,可以掌握通过CancellationToken取消任务(包括non-cancellable任务)。 早期早期.Net使用Backgroun... 查看详情
c#操作svg矢量图-nuget库svg(代码片段)
文章目录NuGet测试文档NuGet新建C#应用配置包源工具->NuGet包管理器->程序包管理器设置新建包源https://www.nuget.org/api/v2/安装svg测试测试数据博客不支持SVG格式,只能放入网盘,下载后放在D盘目录下。(使用其他svg图... 查看详情
c#中简单操作mysql数据库(代码片段)
以C#访问MySQL数据库,执行简单的CRUD。MySql.Data.dll是C#操作MySQL的驱动文件,是C#连接MySQL必要插件,使C#语言更简洁的操作MySQL数据库。可以用NuGet命令安装 PM>Install-PackageMySql.Data-Version8.0.16项目中添加该组件的引用,在代码页... 查看详情
c#中的原子操作(代码片段)
某天,我同学发给我这样一道题。看着还是挺简单的,我当时就想到了线程等待。使用AutoResetEvent类来两个线程互相等待,互相通知1classProgram23staticAutoResetEventautoResetEvent1=newAutoResetEvent(false);4staticAutoResetEventautoResetEvent2=newAutoResetEv... 查看详情
c#操作excel总结(代码片段)
1、导入命名空间: usingMicrosoft.Office.Interop.Excel; 2、如何打开已有excel文档,或者创建一个新的excel文档 Applicationapp=newApplication();Workbookswbks=app.Workbooks;_Workbook_wbk=wbks.Add(excelTempPath+"qu 查看详情
c#操作高低位(代码片段)
1.int32占4个字节intdata=2147483647;byte[]byte4=newbyte[4];byte4=BitConverter.GetBytes(data);//结果逆序2.int16占2个字节:intdata=32767;bytehigh=Convert.ToByte((data>>8)&0x00ff);//位运算:右移8位bytelow=Convert.T 查看详情
关于c#中async/await的用法(代码片段)
...,async/await成对出现才有意义。其意义在于可以等待异步操作完成后继续顺序执行,而不是异步操作还没处理完成主线程就进行了下一步。 假设,我们现在要模拟简单的下载场景,首先用户点击下载,那么就调用DownloadHa... 查看详情
c#操作mongodb(代码片段)
1安装MongoDB.Driver2建立数据库连接#mongodb://127.0.0.1:27017MongoClientclient=newMongoClient(“连接字符串”);3获取数据库vardatabase=client.GetDatabase("数据库");4获取数据集Collection#如果数据是预先定义好的可以在<输入数据的类型>,如果是没有... 查看详情
c#字符串的操作(代码片段)
vartStr="0|1:开门|2:关门|3:门检失败|4:开门|5:开门|6:关门";varresult=string.Format("[0]",string.Join(",",tStr.Substring(2).Split(‘|‘).Select(s=>string.Format("doorid:0,status:1",s.Split(‘:‘)[0],s.Split( 查看详情
c#学习笔记(19)操作sqlserver下(代码片段)
C#学习笔记(19)操作SQLServer下ADO.net操作数据库这应该是比较老的技术了,以后有空的话学学Linq///<summary>///数据库使用类///</summary>publicstaticclassMySqlHelper///<summary>///1.数据库连接字符串///</summary 查看详情
c#学习笔记(18)操作sqlserver中(代码片段)
C#学习笔记(18)操作SQLServer中数据库基础操作SQL语法可以参考菜鸟教程或者微软官方的SQL示例注意SQL不区分大小写查1.基础查询--最基础的查询语句,select*fromtable_nameselect*fromstudentTable2.条件查询--条件查询select*fromtable... 查看详情
c#一个简单办法判断操作系统版本(代码片段)
做个记录,最近写个小软件,涉及到判断操作系统版本的,查看了微软的相关资料,感觉以及网上的一些Demo,感觉不全,而且有些系统也不好区分。因为之前写过批处理版本的操作系统判断使用了WMIC的命令,所以一想放到C#试... 查看详情
c#中利用npoi包操作excel文件(代码片段)
...供ISheet、IRow、ICell等接口usingNPOI.XSSF.UserModel;//提供相关类操作扩展名为xlsx的2007之后版本Excel文件usingNPOI.HSSF.UserModel;//提供相关类操作扩展名为xls的2007之前版本Excel文件完整代码如下usingSystem;usingSystem.IO;//提供文件流文件和文件流... 查看详情
c#中利用npoi包操作excel文件(代码片段)
...供ISheet、IRow、ICell等接口usingNPOI.XSSF.UserModel;//提供相关类操作扩展名为xlsx的2007之后版本Excel文件usingNPOI.HSSF.UserModel;//提供相关类操作扩展名为xls的2007之前版本Excel文件完整代码如下usingSystem;usingSystem.IO;//提供文件流文件和文件流... 查看详情
c#下载文件(代码片段)
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Net;WebClientwebClient=newWebClient();stringurl="http://192.168.1.171: 查看详情
c#基础之ado.net(代码片段)
...DO.NET事务 定义:所谓事务,可以看做是一个操作单元,是最小的操作单位,在这个事务中的操作,要么全部执行成功,要么全部失败。 在一个事务性操作的环境下,操作有着以下的4种特性,被称为ACID特性... 查看详情
c#学习笔记(17)操作sqlserver上(代码片段)
C#学习笔记(17)操作SQLServer上安装SQLServer微软官网https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads如果不使用局域网访问数据库,安装默认功能即可安装完毕后需要重启电脑启动SQLServer如果该服务没有启动,将其... 查看详情