React看美剧

想法

2016年在西安出差,周末起了沙尘暴,只能在酒店呆着,一时无聊至极。
想起之前在家里通过Raspberry Pi观看美剧的软件,心想为什么不自己做一个在线看美剧的网站呢?

尝试

在前些时间,在家里用Raspberry Pi鼓捣过一个家庭影音,其主要的功能是依托OSMC这款开源软件。热心的”jindaxia”制作了一个美剧插件,使得我们能够在电视机大屏幕上有非常好的美剧观影体验。
基于这款美剧插件的基本原理, 只花了半天时间,用js代码拼凑了一个非常简单的chrome插件。当天我把它Po到了v站上,收到了不少网友的反馈。尽管简单,但非常实用,甚至有人说这种极简风格非常喜欢。
但是,插件的上手曲线还是比较大,从而诱发了我做一个网站的设想。

设计

直到今年6月了解到《权利的游戏》第七季马上回归了,我才开始着手实现一直以来的想法。
设计上有几点考虑

  • 利用nodejs的全栈来提高开发效率
  • 利用Github和Heroku来做Devops
  • 基于Bootstrap实现简洁的网站风格
  • 前后端分离RESTFul接口访问

后端

后端使用Express + Redis,由于网站逻辑简单,RESTFul的接口定的很简单。

1
2
3
4
5
6
7
8
9
10
app.get('/api/search/:title', function(req, res) {
var api = SERVER + '/v3plus/video/search'
var body = {'title': req.params.title };
console.log('/api/search/');
// Check Redis and get from remote
cacheAndGet(api, body, getJSON, function(json){
res.send(json);
})
});

前端

前端使用React + Boostrap,基本上是第一次用React,没想到用起来这么方便易学。
使用React-Router做前端路由,再基于Velocity来做一些简单的动画效果。
有了架子,逐个逐个页面去开发,效率也非常高,开发的过程中去发现复用的代码来重构,降低复杂度。
基于组件式的开发,使页面更加模块化,可维护性非常高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SeasonList extends Component {
constructor(props) {
super(props);
}
render() {
let seasonList = []
let objList = this.props.objList;
for(var i = 0; i < objList.length; i++) {
let obj = objList[i];
seasonList.push(<Season key={i}
id={obj.id}
cover={obj.cover}
title={obj.title}
intro={obj.intro} /> );
}
return (
<div>
{ seasonList }
</div>
)
}
}

Hack

整个网站的核心其实是在于视频源的获取,实际上网站是不存储任何的视频,所有源都来自于“人人视频”。而人人视频是没有网页版的,我觉得是处于当前大环境打击盗版的原因。
后端通过Mock移动端的接口,获取到视频的播放地址。不过要获取这个地址并不容易,首先要能够分析出接口的地址,再者要截取到接口的参数。
一般会有以下的方法:

  • 通过Fiddler来抓手机通信数据包
  • 通过Wireshark仿真Android的数据包
  • 逆向移动应用APK来破解

“人人视频”的接口是RESTFul形式,我使用Fiddler抓包,已经能够把基本功能的接口都逆向出来了,比如“搜索”,“分类”,“剧集信息”等等。
最关键的视频源接口,在抓包发现了一个签名字段,而且字段每次都不一样,猜测和时间戳有关系,应该是通过摘要算法把时间戳和其他信息包含在内,在服务端会进行校验。这时就只能通过APK逆向来找到对应的算法。

  • dex2jar + jd-gui
  • cfr

使用cfr逆向出来的代码,可读性更高,很快就能找到了该签名的摘要算法

1
2
3
4
5
6
7
key = 'episodeSid=' + req.params.episodeSid;
key += 'quality=' + 'super';
key += 'clientType=' + FAKE_HEADERS['clientType'];
key += 'clientVersion=' + FAKE_HEADERS['clientVersion'];
key += 't=' + FAKE_HEADERS['t'];
key += SECRET_KEY;
headers['signature'] = md5(key);

优化

网站上线以后发现初次加载性能太慢,调查过是因为React将所有组件打包到一个js文件,初次加载时间非常长。
优化方法是使用react-router的getComponent来实现按需加载的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function asyncComponent(getComponent) {
return class AsyncComponent extends React.Component {
static Component = null;
state = { Component: AsyncComponent.Component };
componentWillMount() {
if (!this.state.Component) {
getComponent().then(({default: Component}) => {
AsyncComponent.Component = Component
this.setState({ Component })
})
}
}
render() {
const { Component } = this.state
if (Component) {
return <Component {...this.props} />
}
return null
}
}
}

与此同时,在后端Express加一个压缩中间件,压缩率可达40%,初次加载时间缩短70%以上。

Devops

由于这个网站是业余项目,个人也不打算投入太多精力时间,使用Heroku虽然慢了点,不过免费这一点完胜。另外Heroku支持github的hook,完美的实现了我的Devops要求,每次写完一个feature,只要直接push到Github,什么都不用管,方便!直接!
前端代码里加了google-analytics,可以实时对网站的运行情况进行监控。

Growth Hack

尝试在一些论坛、社交媒体上做过推广,效果不佳。后来在页面也加了社交分享功能,使用的人也不多。目前日均ip在1500左右,顺其自然好了。

总结

现在的应用开发对于开发人员来说,简化了很多流程。脚手架很多,工具很多,Paas又减少了很多部署的难度,一个网站开发到上线只需2天。
将来编程开发上手的难度只会越来越低,一个产品重要的不在是程序员的能力,更多是产品经理的,运营的能力,程序员随时可替代。
当然只是初级的,想要不可替代,成为高级是唯一选择。
附上项目地址: Github 网站
image