Before I do this thing, a few years ago to play several years of CMP4, also do not know how many domestic programmers remember CMP Morning wind music player, if there is, that is my peer, in the long ago to play CMP player to play a few years, do music, do video. I’ve seen nodeJS blogging a lot in the community before, and nodeJS slicing as well. I’ve been looking around for video systems, but I can’t find them, and I have a thing for players. Write a basic NodeJS video system.

<<< nodeJS +mongodb developed a set of film and television site CMS, think it can send a star >>>

<<< a Flutter fijkPlayer skin >>>

Thought 1: Whether it is a film and television system or a music system, the premise is to have enough data. What about the data?

Playing so many years of player, we all know the claw ba bai, search words, can search a lot of Zi Aid wang, almost all have open API for you claw ba. You can connect to an interface like an Apple CMS, like xxx.com/macapi/?ac=videolist&pg=1,

Content planning (background part) is divided by functions, video data, message module, user module, configuration module, other modules (navigation, classification), template module, timing module, script management module.

The foreground part

By page, home page () show all kinds of information, details page (show a single data information), on page (+) display a single data information, personal center (change passwords, modify the nickname), category pages (all classification, s, area), the search page (search data), navigation page (all categories under a certain navigation data)

First, we need to determine the classification field. On the classification page of the movie website, there will be a separate description of the classification and keywords (referred to as SEO information). There may be secondary categories under the navigation (which may require the query subcategories of the table), and the navigation can be set to show and hide. What if navigation needs to adjust the output order? Name (used for navigation name), display (hidden), parent_id (if there is a parent navigation, save the _id of the parent, if there is no parent, save false to check whether it is true or false, whether it is a level 1 navigation or level 2 classification), seo information is used for storage

{"name" : "action ", "parent_id" : ObjectId(" 5e819A8619f4d790bC5d0C76 "), "display" : true, "seo" : {"title" : "Action", "keywords" : "action", "description" : "action"}, "type" : "nav_type", "index" : 0}Copy the code

Video data (background)

First determine the data fields, which fields? What type of field is appropriate? Here I refer to the resource network to think and do

Title, director, star, update date, description, update status, cover, language, region, release date. These fields are mandatory, but it is important to note that if you are searching for Chinese medicine and doing the actor index search, it is best to set the actor table here as an array. Example: [” Jin Dong “,” Jiang Xin “]; In order to find actors in mongodb, use the $IN operator, I do not do actor index, index directly string to save. If you don’t save arrays, save strings to find actors, then the free version of Mango, can only use re, is a waste of time

What about classification?

When a movie is displayed, it needs to find the corresponding classification. If the field is dead, then if you want to modify the classification name later, you need to find all the video data and replace the old classification name in batches, which is not good. The best way to do this is by using the pipe Aggregate operator. Mongo provides the operation for a join table query

{$lookup: {from: "other", // Associate table name localField: "video_type", // The fields of the current table need to be associated with the target table foreignField: "_id", / / the target table and table field current corresponding fields as: "the type" / / output field}}, {$unwind: "$type}"Copy the code

User modules

Username (username), password (password), nickname (nickname), display hide display, permission location high or low, grade_id, default or not, grade_id or not, grade_id or not, grade_id or not, grade_id or not, grade_id or not, grade_id or not Here is the simplest example: the password can be MD5 or MD5 +hash, or with salt? _ID is the unique index field that comes with mongodb insertion. It can be used directly to solve the problem of id duplication in distributed mode. Save the trouble of generating their own ID, with a unique index

{
    "_id" : ObjectId("5e7e35cf4345c47a1c8c15f6"),
    "userName" : "abcdxxxx",
    "passWord" : "ba0a086c8a7b0ca4232406b5efff3a95",
    "nickName" : "阿打算",
    "admin" : false,
    "display" : true,
    "default" : false,
    "grade_id" : 0
}
Copy the code

Message module

Possible new students (here refers to CXK playing basketball that kind of front end to do message) do message trouble, how to plan? What fields are there? Here in the form of building messages for the model.

The data fields are as follows:

  • _ID Specifies the unique ID of the data provided by the system.
  • Vid is the unique ID of the corresponding video in the associated video table.
  • Uid is used to associate the unique id of the corresponding user in the user table.
  • Pid is used to find the parent level of this message (here refers to the id of the message from the building kid)
  • Wid building inside the message must be on so-and-so people, you replied to which turtle sun? It’s the unique id of the turtle son
{ "_id" : ObjectId("5e7f93a60eefb36e54fe8f72"), "vid" : ObjectId("5e7e15b04a285358100e3d6f"), "uid" : ObjectId("5e7e35cf4345c47a1c8c15f6"), "pid" : false, "wid" : false, "agree" : true, "display" : true, "date" : 1585419174015.0, "sub_date" : 1585419174014.0, "text" : "da"}Copy the code

And video data associated classification data, we can not leave a message on the user name and who reply to write dead, in case the other party changed its name? Are you looking for crazy old data replacement tables? So we’re going to have to use syntables here.

When looking for comments, the rules are as follows: First find a rating, that is, the building of the turtle sun

Then loop these two data and use these two data _id to find the data of the secondary response below the building. Find them and add them to the children field of this data (the data of the building)

At this point, the message and user section ends.

Video source input (background)

The video data entry format was covered in the previous chapter. How to input the playback source? It is the same as the message association video table and user association table. The video source data also needs to be separate, because if it is not separate, we just query the movie list, there is no need to show the source list, there is no need to bring out so much data

It is also easy to associate source data using the mongodb pipeline join table operation.

Initialize data (background)

For example, the blog must have a default root user for the background system, default top level classification, some default configuration, from where? It can’t fall out of the sky.

Nodejs pakcage. Json’s Script fields are used to define commands. NPM run dev, NPM run build

You can define your initialization data commands in script.

"scripts": {
    "build": "node ./build/initDataBase.js",
    "restore": "mongorestore -d movie ./backup/movie",
    "backup": "mongodump -d movie -o ./backup/movie"
}
Copy the code

Build is used to connect to the database, delete the original database, create a new database, create field indexes, and insert default data into the database, such as the default administrator information for the movie CMS system. Initialized data

const mongodb = require('mongodb'), MongoClient = mongodb.MongoClient, config = require('.. /utils/config.js'), dbConfig = config.project; Let connectURL = 'mongo: / / 127.0.0.1:27017'. let client = MongoClient.connect(connectURL, { useNewUrlParser: true, useUnifiedTopology: true }); client.then(async (db) => { let DB = db.db(dbConfig.dbName); Await new Promise(async (resolve, reject) => {console.log(' clearing raw data table '); await DB.dropCollection('session1').catch(err => {}); await DB.dropCollection('session2').catch(err => {}); await DB.dropCollection('config').catch(err => {}); await DB.dropCollection('logs').catch(err => {}); await DB.dropCollection('message').catch(err => {}); await DB.dropCollection('other').catch(err => {}); await DB.dropCollection('user').catch(err => {}); await DB.dropCollection('video_info').catch(err => {}); await DB.dropCollection('video_list').catch(err => {}); The resolve ()}). Then (() = > {the console. The log (' original data table cleared on '); }) .catch(err => { console.log('+++', err); }) await new Promise(async (resolve, reject) => {console.log(' start rebuilding table '); await DB.createCollection('session1'); await DB.createCollection('session2'); await DB.createCollection('config'); await DB.createCollection('logs'); await DB.createCollection('message'); await DB.createCollection('other'); await DB.createCollection('user'); await DB.createCollection('video_info'); await DB.createCollection('video_list'); The resolve ()}). Then (() = > {the console. The log (' data table reconstruction completed '); }) await new Promise(async (resolve, reject) => {console.log(' start creating user data '); Let userColl = db.collection ('user'); // Index field await userColl.createIndexes([{key:{userName: 1}}, {key:{passWord: 1}}, {key:{display: 1}}, {key:{admin: 1}}, {key:{grade_id: 1}}, {key:{default: 1}} ]); await userColl.insertOne({ userName: 'root', passWord: 'e10adc3949ba59abbe56e057f20f883e', // 123456 md5 nickName: 'site owner ', admin: true, display: true, default: true, grade_id: 2, // 0 User 1 Administrator 2root user}); resolve(); }). Then (res=>{console.log(' user data created successfully '); })});Copy the code

Deployment problem Consideration: PM2 deployment cannot close tasks (background)

Pm2 deployment cannot close the task because when the user (administrator) starts the scheduled task, pM2 may distribute the task to THE B thread (suppose the computer has 6 cores and 12 threads). You: My computer has two cores, where can I get six cores?

Sorry, my 12 threads.

Let’s get out of here. Let’s get back. When the user (administrator) wants to shut down, pM2 may send a task to the G thread, but the G thread does not execute the task, and an error will be reported.

Is a scheduled task executed simultaneously by multiple threads?

This is because the scheduled task is set when the project is deployed and the service is started. When the time is up, pM2 copies multiple tasks (exactly the same as pM2) and of course repeats the task.

How to avoid fit?

Imagine if our project was deployed with PM2 and had 12 copies of the Node project. However, we can use other process daemon tools, such as Forever (singleton, single-threaded) to daemon timed tasks. Speaking human: is divided into two project deployment, scheduled tasks using a single thread daemon tool, daemon. In this way, the scheduled task is a single thread running after the time, not multiple threads called at the same time. Then there’s the question, how do you communicate? How does pM2 tell forever’s daemon process to open and close the task? (HTTP service)

Solution 1: If the pM2 daemon listens on port 8888, the HTTP service enabled by js at Forever can listen on port 9999. The communication between the two sends a POST request. Because of this machine. So soon, received in seconds) this way, the communication problem is solved, and scheduled tasks can be created/deleted dynamically at any time. There is no problem with multi-threading at the same time.

Solution 2: Use the scheduled task interface provided by the underlying system, for example, cronTab in Linux. In Windows, I do not know, to avoid repeated pM2 execution

Deploy the NodeJS project using the Pagoda panel lazybones

Go to pagoda Panel software store, install PM2 manager, mongodb, nginx

Once the installation is complete, click File to upload the site zip file

CD/WWW /wwwroot // Install dependency NPM install // initialize data NPM run buildCopy the code

Go to PM2 Manager and select the site

This is equivalent to going into the project directory and executing (the operation above ↑ is equivalent to executing this command)

pm2 start app.js --name app
Copy the code

This step only starts the project, but there is no mapping (i.e., setting up the forwarding request), because nginx accepts all the requests at the beginning. Selecting the mapping means that Nginx forwards a web site request to the pM2 manager enabled service

After the mapping is completed, you can find the website in the website column.

Deployment Completion Effect