手把手教你做短视频去水印微信小程序(5-服务端代码)

701 阅读4分钟

前言

前边几篇文章我们介绍了小程序端关键代码,这一篇我们来说一下服务端代码。这里主要借助一个开源项目 smalls/video-tools 来实现视频解析的核心部分,在此基础上,我们实现api接口以供调用。


一、框架

这里我选用我最为熟悉的php语言开发框架:Laravel

目前laravel版本已经到了 8.x,这里我使用的是长期支持版 6.x

快速开始请见github的 readme.md 文档。

有关框架的使用在此不做介绍,有不熟悉的同学可以查阅官方文档,laravel是值得phper掌握的优雅框架。这里我们的实际代码量相对较小。

二、登陆/退出接口

微信登陆我们使用overtrue大神封装的composer包:overtrue/laravel-wechat

2.1 路由

routes/api.php 文件中添加路由:

Route::group(['prefix' => 'auth', 'namespace' => 'Auth'], function () {
    Route::post('login', 'LoginController@login')->name('login');
    Route::post('logout', 'LoginController@logout')->name('logout');
});

2.2 控制器

重写 App\Http\Controllers\Auth\LoginController 文件的login方法,使用 overtrue/laravel-wechat

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function login(Request $request)
    {
        $miniProgram = \EasyWeChat::miniProgram(); // 小程序
        $sessionInfo = $miniProgram->auth->session($request->input('code'));
        $decryptedData = $miniProgram->encryptor->decryptData($sessionInfo['session_key'], $request->input('iv'), $request->input('data'));
        $user = User::firstOrCreate([
            'openid' => $decryptedData['openId'],
        ],[
            'name' => $decryptedData['nickName'],
            'avatar' => $decryptedData['avatarUrl'],
            'gender' => $decryptedData['gender'],
            'unionid' => $decryptedData['unionId'],
            'country' => $decryptedData['country'],
            'province' => $decryptedData['province'],
            'city' => $decryptedData['city'],
        ]);

        $user->api_token = Str::random(80);
        $user->save();

        return response()->json(['token' => $user->api_token]);
    }


}

退出接口没有进行重写,logout接口实际调用的是sessionGuard的退出逻辑,实际在小程序中也没有提供退出功能,大家知道就好。

三、视频解析、解析记录接口

这边就不拆开细讲了,整段代码贴出来,详细请看注释。

3.1 路由

routes/api.php 文件中添加路由:

代码如下(示例):

// 以下接口需要识别用户登陆态,因此添加api认证中间件
Route::group(['middleware' => 'auth:api'], function () {
	// 视频解析接口
    Route::post('video-parse', 'VideoParseController@parse')->name('video.parse');

	// 当前用户解析记录总数
    Route::get('records/total', 'RecordController@getTotalNum');
    // 当前用户解析记录列表接口
    Route::resource('records', 'RecordController');
});

3.2 控制器

3.2.1 视频地址解析

我们新建一个视频地址解析controller:

class VideoParseController extends Controller
{
    public function parse(Request $request)
    {
    	// 参数验证器
        $request->validate([
            'url' => 'required|string|url', //url参数格式验证
        ]);

        $user = $request->user();
        $url = $request->input('url');
        Log::info("video-parse|user_id:{$user->id}|{$url}");
        $urlInfo = parse_url($url);
        $host = $urlInfo['host'];

        $domain = implode('.', array_slice(explode('.', $host), -2));
		// 优先尝试从缓存获取数据(缓存有效期3600秒)
        [$noWatermarkUrl, $imageUrl] = Cache::remember(md5($url), 3600, function () use($domain, $url) {
        	// 根据域名判断短视频来源
            switch ($domain) {
                //抖音
                case 'douyin.com':
                    $data = VideoManager::DouYin()->start($url);
                    break;
                //微视
                case 'qq.com':
                    $data = VideoManager::WeiShi()->start($url);
                    break;
                //快手
                case 'kuaishou.com':
                case 'chenzhongtech.com':
                    $data = VideoManager::KuaiShou()->start($url);
                    break;
                //最右
                case 'izuiyou.com':
                    $data = VideoManager::ZuiYou()->start($url);
                    break;
                //皮皮虾
                case 'hulushequ.com':
                case 'pipix.com':
                    $data = VideoManager::PiPiXia()->start($url);
                    break;
                //皮皮搞笑
                case 'ippzone.com':
                    $data = VideoManager::PiPiGaoXiao()->start($url);
                    break;
                default:
                    abort(Response::HTTP_BAD_REQUEST, '解析失败,请检查地址');

            }
            // 视频地址
            $videoUrl = Arr::get($data, 'video_url');
            // 缩略图地址
            $imageUrl = Arr::get($data, 'img_url');
            // 视频地址为空时直接返回500状态码
            abort_if(empty($videoUrl), Response::HTTP_INTERNAL_SERVER_ERROR, '解析失败,请稍后再试');
            return [preg_replace('/^http:/', 'https:', $videoUrl), $imageUrl];
        });
		
		// 当得到无水印地址后,存入数据库,用于查阅解析历史
        if ($noWatermarkUrl) {
            $record = Record::firstOrNew(['url' => $url], [
                'host' => $host,
            ]);
            $record->no_water_mark_url = $noWatermarkUrl;
            $record->image_url = $imageUrl;
            $user->records()->save($record);
            return response()->json([
                'url' => $noWatermarkUrl,
                'image' => $imageUrl,
                'preview' => config('app.preview'), // 这里注意,这个参数是用来控制视频是否展示的!由于微信小程序个人主体不能涉及视频,所以需要服务端控制是否展示,需要审核通过后再显示视频,以达到目的!
            ]);
        } else {
            Cache::forget(md5($url));
            return response()->json(['message' => '转换失败,请检查链接是否有效,或联系客服'], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}

这里注意,返回参数中有一个preview参数,这个参数是用来控制视频是否展示的!由于微信小程序个人主体不能涉及视频,所以我们耍个小聪明,通过服务端控制视频是否展示,这样我们在小程序审核通过后再显示视频,以达到目的!(请勿滥用)

3.2.2 解析记录controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;

class RecordController extends Controller
{
	// 获取某用户历史解析列表
    public function index(Request $request)
    {
        $data = $request->user()->records()->orderBy('updated_at', 'desc')->paginate($request->input('size', 10))->toArray();
        $data['preview'] = config('app.preview');
        return response()->json($data);
    }

	// 获取某用户解析总数
    public function getTotalNum(Request $request)
    {
        $totalNum = $request->user()->records()->count();
        return response()->json([
            'total_num' => $totalNum,
        ]);
    }

	// 删除某条记录
    public function destroy($id, Request $request)
    {
        $result = $request->user()->records()->where('id', $id)->delete();
        if ($result) {
            return response()->json([
                'message' => '删除成功'
            ]);
        } else {
            return response()->json([
                'message' => '删除失败'
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}


总结

得益于我们核心代码使用了开源项目,我们只做了针对具体业务的封装,所以整体编码量相对较小,后续只需共同维护解析部分即可,这便是开源的力量,再次鸣谢:


系列文章

github源码地址

欢迎浏览,欢迎star~