|
1 |
| -# bilibili-live-video-noty (Bilibili直播下载) |
| 1 | +# json-typescript-mapper |
2 | 2 |
|
3 |
| -## 来源 |
4 |
| -由于我家妹纸需要学习B站直播画画(需求),她跟我说无法下载, 只能录屏. 可是我是拯救世界的程序员, 怎么可能手动做这玩意呢. 于是开始研究/分析, 发现首先视频木有加密(不像能力天空), |
5 |
| -其次可以通过一个动态生成出来的链接进行下载, 这样需要手动使用例如迅雷的方式下载. 但是万恶的主播时不时早上6点直播, 要么就下班的路上一言不合又直播, 我靠!!! 哎, 于是就开始挨喷. 宝宝心里苦:( 所以就花了一下午冲冲忙忙做了这么一个玩意) |
6 |
| -测试了一周没问题, 后来发现B站动态回收直播服务器, 换句话说地址必须动态获取. |
| 3 | +## Introduction |
7 | 4 |
|
8 |
| -现在挂在在自己的VPS服务器上(http://45.78.48.209:8080), 稳定测试了一个月, 再也没有被家暴了~ |
| 5 | +For single page application, data sources are obtained from API server. Instead of directly using api data, we |
| 6 | +definitely require an adapter layer to transform data as needed. Furthermore, |
| 7 | +the adapter inverse the the data dependency from API server(API Server is considered uncontrollable and |
| 8 | +highly unreliable as data structure may be edit by backend coder for some specific purposes)to our adapter |
| 9 | +which becomes reliable. Thus, this library is created as the adapter make use of es7 reflect decorator. |
9 | 10 |
|
10 |
| -## 原理 |
11 |
| -整个应用挂在在VPS上, 使用PM2维护应用 |
12 |
| - |
13 |
| -1. 创建一个Schedule |
14 |
| - |
15 |
| -2. 请求确定Package.json配置的下载直播列表的状态是<直播中> |
| 11 | +### Get Started |
| 12 | +```bash |
| 13 | +npm install typescript-json-mapper --save |
| 14 | +``` |
| 15 | +## Environment |
| 16 | +* NodeJS |
| 17 | +* Browser |
16 | 18 |
|
17 |
| -3. 动态获取下载地址, 返回了XML直播服务器列表与备用直播服务器列表 |
| 19 | +## Language |
| 20 | +* Typescript |
| 21 | +* Javascript (Typescript will eventually compile to js.) |
18 | 22 |
|
19 |
| -4. 创建文件流并下载 |
| 23 | +### Typescript & ES6 |
20 | 24 |
|
21 |
| -## 要求 |
22 |
| -* NodeJS |
| 25 | +```bash |
| 26 | +import {deserialize} from 'typescript-json-mapper'; |
23 | 27 |
|
24 |
| -## 语言 |
25 |
| -* Typescript |
26 |
| -* Javascript (TS会编译成JS, 所以想用JS也可以直接用, 代码我也编译成JS了) |
| 28 | +deserialize(<Class Type>, <JSON Object>); |
| 29 | +``` |
27 | 30 |
|
28 |
| -### 快速开始 |
29 |
| -在Package.json中配置需要下载的用户列表, |
30 |
| -其中VideoId在直播URL中获得 (e.g. http://live.bilibili.com/20375), 20375为VideoId, 该VideoId是绝对唯一不变的. |
| 31 | +## Example |
| 32 | +Here is a complex example, hopefully could give you an idea of how to use it: |
31 | 33 |
|
32 |
| -配置案例例如: (Name用于创建文件名) |
| 34 | +```bash |
| 35 | +class Student { |
| 36 | + @JsonProperty('name') |
| 37 | + fullName:string; |
| 38 | + |
| 39 | + constructor() { |
| 40 | + this.fullName = undefined; |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +class Address { |
| 45 | + @JsonProperty('first-line') |
| 46 | + firstLine:string; |
| 47 | + @JsonProperty('second-line') |
| 48 | + secondLine:string; |
| 49 | + @JsonProperty({clazz: Student}) |
| 50 | + student:Student; |
| 51 | + city:string; |
| 52 | + |
| 53 | + constructor() { |
| 54 | + this.firstLine = undefined; |
| 55 | + this.secondLine = undefined; |
| 56 | + this.city = undefined; |
| 57 | + this.student = undefined |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +class Person { |
| 62 | + @JsonProperty('Name') |
| 63 | + name:string; |
| 64 | + @JsonProperty('xing') |
| 65 | + surname:string; |
| 66 | + age:number; |
| 67 | + @JsonProperty({clazz: Address, name: 'AddressArr'}) |
| 68 | + addressArr:Address[]; |
| 69 | + @JsonProperty({clazz: Address, name: 'Address'}) |
| 70 | + address:Address; |
| 71 | + |
| 72 | + constructor() { |
| 73 | + this.name = undefined; |
| 74 | + this.surname = undefined; |
| 75 | + this.age = undefined; |
| 76 | + this.addressArr = undefined; |
| 77 | + this.address = undefined; |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
33 | 81 |
|
| 82 | +Now here is what API server return, assume it is already parsed to JSON object. |
34 | 83 | ```bash
|
35 |
| - "users": [ |
| 84 | +let json = { |
| 85 | + "Name": "Mark", |
| 86 | + "xing": "Galea", |
| 87 | + "age": 30, |
| 88 | + "AddressArr": [ |
36 | 89 | {
|
37 |
| - "name": "雷涟漪の日常", |
38 |
| - "videoId": 20375 |
| 90 | + "first-line": "Some where", |
| 91 | + "second-line": "Over Here", |
| 92 | + "city": "In This City", |
| 93 | + "student": { |
| 94 | + name1: "Ailun" |
| 95 | + } |
39 | 96 | },
|
40 | 97 | {
|
41 |
| - "name": "离城的直播间", |
42 |
| - "videoId": 61132 |
43 |
| - }, |
44 |
| - { |
45 |
| - "name": "艾伦测试", |
46 |
| - "videoId": 288546 |
| 98 | + "first-line": "Some where", |
| 99 | + "second-line": "Over Here", |
| 100 | + "city": "In This City", |
| 101 | + "student": { |
| 102 | + name1: "Ailun" |
| 103 | + } |
47 | 104 | }
|
48 |
| - ] |
49 |
| -``` |
50 |
| -下载目标文件夹: (默认ftp) |
51 |
| - |
52 |
| -```bash |
53 |
| - "downloadFolder": "ftp", |
| 105 | + ], |
| 106 | + "Address": { |
| 107 | + "first-line": "Some where", |
| 108 | + "second-line": "Over Here", |
| 109 | + "city": "In This City", |
| 110 | + "student": { |
| 111 | + name: "Ailun" |
| 112 | + } |
| 113 | + } |
54 | 114 | ```
|
55 | 115 |
|
56 |
| - |
57 |
| -#### 下载NPM依赖关系, 启动Server |
| 116 | +Simply, just map it use following code. The mapping is based on <@JsonProperty> decorator meta data. |
58 | 117 |
|
59 | 118 | ```bash
|
60 |
| -$ npm install |
61 |
| -$ npm start |
| 119 | +const person = deserialize(Person, json); |
62 | 120 | ```
|
63 | 121 |
|
64 |
| -##注意事项 |
65 |
| -1) 执行开始之后, 只有时间例如13:01 00:00才执行检测, 设定是每分钟执行一次检查 (妹纸说直播的每分钟都很重要) |
66 |
| - |
67 |
| -2) 下载的FLV文件由于主播可能网络环境不好, 出现断断续续, 导致下载多个小文件是正常的. |
68 |
| -还有一种情况出现多个几百B的文件是由于主播网络问题导致直播API返回是直播进行中, 但实际上文件流已经关闭. 已解决, 待测试. |
69 |
| - |
70 |
| -3) 由于视频文件是直播形式, 所以下载的文件自身没有结束符, 这导致在部分视频工具无法调节进度条. 但大多数视频工具可以调整. |
71 |
| -这个问题的解决方式需要理解视频文件的构造(我研究了一下, 有点麻烦需要检测最后几位二进制的特征做修复) |
72 |
| - |
73 |
| -## TODO |
74 |
| -如果有需求, 可以加个Electron.js的壳, 做成桌面APP |
75 |
| - |
76 |
| -## Demo |
77 |
| - |
| 122 | +## Test Report |
| 123 | + |
78 | 124 |
|
79 |
| - |
| 125 | +## Roadmap: |
| 126 | +1) Fully json mapping to the modal class convention should be provided. |
| 127 | +If any unmapped variable discover, throw exception! |
| 128 | +This could be useful to detect if API data has change it data structure in the unit testing phrase. |
80 | 129 |
|
| 130 | +2) Runtime data type validation might be a good idea. Alternatively, if this feacture is not covered in the future, I will make use of json schema concept instead. |
0 commit comments