Recently, it's a hot rewind challenge - ReverseVoice (wechat applet version front and back source code) Ts Node Taro

Keywords: node.js npm MongoDB TypeScript sudo

Project address:

In fact, the whole project is very simple. It's only a few days after I saw the fire in tremolo and station B until the app finally went online. Since it went online on November 16, there are still a lot of users (mainly the fast apps made at that time are relatively few). Now there are a lot of simpler and better flip challenge apps. The open source of this project is only for you to learn~

Embrace TypeScript~

By the way, Taro's support for Ts is not enough. I hope you can go to Taro for dts's PR.


Applet QR code

Challenge sharing Poster (there is a problem with this poster for the time being, the fix code has not been submitted for review because of the qualification problem)

Function introduction / implementation principle

  • Function and realization principle
  1. Applet end users record and save local
  2. After recording, upload the recording file to the back end for playback, and return the processed audio url
  3. Download the url file on the applet side, prompt the user to reverse successfully, and make the data as a local map
  4. The user clicks share to generate a share link, and sends the video of the share in front and back to the back end to be saved in qiniu cloud
  5. At the same time, create a new sharing room to save the user information and return the roomId
  6. User sharing (poster sharing canvas dynamically generate sharing code poster)
  7. Other users take part in the challenge. The storage principle is the same as that of 4, but the logic of storing Challenger information into room is added
  • Audio upside down

Use ffmpeg for audio playback, core code:

// See. / server / SRC / Controllers / file. TS = > function reversevoice for details
import ffmpegPath from '@ffmpeg-installer/ffmpeg'
import ffprobePath from '@ffprobe-installer/ffprobe'
import ffmpeg from 'fluent-ffmpeg'

    // Reversal
      '-vf reverse',
      '-af areverse',
    .on('progress', (progress) => {
      // send upload progress
      console.log('upload-file-progress', progress.percent)
    .on('error', (err) => {
      console.log(`Ffmpeg has been killed${err.message}`)
    // Preservation
    .save(publicPath + saveFilePath)
    .on('end', () => {
      // Get audio information (duration, etc.)
      ffmpeg.ffprobe(publicPath + saveFilePath, (err, metadata) => {
  • Applet recording

The official api is used for small program recording. For details, see ./wechatapp/pages/index/index.tsx

Sound recording

  • Poster generation

Using canvas to dynamically synthesize and share posters /wechatapp/pages/sharePoster
Dynamic request page applet code is required, involving wechat AccessToken authentication, etc. for details, see /server/src/controllers/wechat.ts , some core codes are pasted below

// Drawing
const draw = async () => {
  // loading before drawing
    title: 'Poster generation in progress...',
    mask: true,
  // Get picture information
  const [productImgInfo, qrcodeImgInfo] = await Promise.all([
    this.getImageInfo(sharePoster), // Get master map
    this.getQrImgInfo(), // Get QR code picture

  // product image width and height
  const pW = CANVAS_WIDTH
  const pH = (pW / productImgInfo.width) * productImgInfo.height

  // canvas height
  let canvasHeight = pH

  const ctx = Taro.createCanvasContext('canvas', null)

  ctx.fillStyle = '#fff'
  ctx.fillRect(0, 0, CANVAS_WIDTH, canvasHeight)

  // Draw background picture
  ctx.drawImage(sharePoster, 0, 0, pW, pH)

  // Draw the QR code (because there is an angle, you need to rotate the canvas, and then rotate it back)
  ctx.rotate(-Math.PI / 32)
  ctx.translate(-25 * ratio, 10 * ratio)
  ctx.drawImage(qrcodeImgInfo.path, QR_LEFT, QR_TOP, QR_WIDTH, QR_WIDTH)
  ctx.rotate(Math.PI / 32)
    canvasStyle: {
      height: canvasHeight,
  setTimeout(() => {
  }, 500)
// Almost every page of wechat applet needs to be configured with shared parameters, and the shared parameters need to be changed dynamically
// Therefore, it is convenient to use the page by taking out the HOC component
import { ComponentClass } from 'react'

import Taro from '@tarojs/taro'
import { connect } from '@tarojs/redux';
import defaultShareImg from '@/assets/images/share.png'

type Options = {
  title?: string
  imageUrl?: string
  path?: string

const defalutOptions: Options = {
  title: 'Can you understand me? Recently, the hot reverse recording is coming~',
  imageUrl: defaultShareImg,
  path: 'pages/index/index',

function withShare() {
  return function demoComponent(Component: ComponentClass) {
    @connect(({ user }) => ({
      userInfo: user.userInfo
    class WithShare extends Component {
      $shareOptions?: Options
      async componentWillMount() {
          withShareTicket: true,

        if (super.componentWillMount) {

      // It will be called at the moment of clicking share
      onShareAppMessage() {
        // const sharePath = `${path}&shareFromUser=${userInfo.shareId}`
        let options = defalutOptions
        if (this.$shareOptions) {
          options = {
        return options

      render() {
        return super.render()

    return WithShare

export default withShare


class Room extends Component {
 * The type of the specified config is declared as: Taro.Config
 * Because typescript can only deduce the basic type of Key for object type derivation
 * For a derived type like navigationBarTextStyle: 'black', the type is string
 * Prompt and declaration navigationBarTextStyle: 'black' | 'white' type conflict, declaration type needs to be displayed
  config: Config = {
    navigationBarTitleText: 'home page',

  $shareOptions = {
    title: 'Rewind the challenge! You can understand my handstand Shampoo~',
    path: 'pages/index/index',
    imageUrl: '',

  • Wechat user login process

Wechat official document login process
See the source code for specific implementation

Project run - back end

Get ready

Need to be installed in advance:


  • Clone the project and go to the back-end directory
cd server
  • Installation dependency
npm install
  • Setting up mongoDB
# create the db directory
sudo mkdir -p /data/db
# give the db correct read/write permissions
sudo chmod 777 /data/db

# starting from macOS 10.15 even the admin cannot create directory at root
# so lets create the db diretory under the home directory.
mkdir -p ~/data/db
# user account has automatically read and write permissions for ~/data/db.
  • Start your mongodb server (you'll probably want another command prompt)

# on macOS 10.15 or above the db directory is under home directory
mongod --dbpath ~/data/db
  • Package and run the project
npm run build
npm start

Project running - applet side

Get ready

Need to be installed in advance:


  • Clone the project and enter the applet directory
cd wechatapp
  • Installation dependency
npm install
  • New. env file
In the wechatapp/src/utils directory, clone the env.example.ts file to the same directory as the. env.ts file
 The two parameters of this file represent the request address of local development and online deployment respectively
  • Operation item
npm run dev:weapp // development mode
//Or npm run build:weapp // production mode
  • Wechat developer tools
Select the import project and select the wechatapp/dist directory
 If you want to develop locally, you need to set "do not verify legal domain name" in the developer tool“



Posted by DeadDude on Wed, 27 Nov 2019 04:11:29 -0800