+Backend commands are inputted via STDIN or if using the Utility Script by using `./musare.sh attach backend`.
+
+## Commands
+| Command | Parameters | Description |
+| --- | --- | --- |
+| `rs` | | Restart backend. |
+| `status` | | Returns all modules and a sample of information including, state, jobs queued, running and paused, concurrency (amount of jobs that can run simultaneously), and (startup) stage. |
+| `queued` | `module` | Returns all jobs queued for specified module. |
+| `running` | `module` | Returns all jobs running for specified module. |
+| `paused` | `module` | Returns all jobs paused for specified module. |
+| `jobinfo` | `UUID` | Returns a detailed overview of a specified job. |
+| `runjob` | `module job_name json_encoded_payload` | Run a specified job in a specified module including a JSON encoded payload, and return response. |
+| `eval` | `some_javascript` | Execute JavaScript within the index.js context and return response. |
+| `lockdown` | | Lockdown backend. |
+| `stats` | `module` | Returns job statistics for a specified module. |
+
+## Modules
+When specifying a module please use all lowercase. The available modules are as follows:
+| `mode` | Should be either `development` or `production`. |
+| `migration` | Should be set to `true` if you need to update MongoDB documents to a newer version after an update. Should be false at all other times. |
+| `secret` | Set to something unique and secure - used by express's session module. |
+| `domain` | Should be the url where the site will be accessible from, usually `http://localhost` for non-Docker. |
+| `serverDomain` | Should be the url where the backend will be accessible from, usually `http://localhost/backend` for docker or `http://localhost:8080` for non-Docker. |
+| `serverPort` | Should be the port where the backend will listen on, should always be `8080` for Docker, and is recommended for non-Docker. |
+| `registrationDisabled` | If set to true, users can't register accounts. |
+| `hideAutomaticallyRequestedSongs` | If `true` any automatically requested songs will be hidden. |
+| `hideAnonymousSongs` | If `true` any anonymously requested songs will be hidden. |
+| `sendDataRequestEmails` | If `true` all admin users will be sent an email if a data request is received. |
+| `apis.youtube.key` | YouTube Data API v3 key, obtained from [here](https://developers.google.com/youtube/v3/getting-started). |
+| `apis.youtube.rateLimit` | Minimum interval between YouTube API requests in milliseconds. |
+| `apis.youtube.requestTimeout` | YouTube API requests timeout in milliseconds. |
+| `apis.youtube.retryAmount` | The amount of retries to perform of a failed YouTube API request. |
+| `apis.recaptcha.secret` | ReCaptcha Site v3 secret, obtained from [here](https://www.google.com/recaptcha/admin). |
+| `apis.recaptcha.enabled` | Whether to enable ReCaptcha at email registration. |
+| `apis.github.redirect_uri` | The authorization callback url is the backend url with `/auth/github/authorize/callback` appended, for example `http://localhost/backend/auth/github/authorize/callback`. |
+| `apis.discogs.client` | Discogs Application client, obtained from [here](https://www.discogs.com/settings/developers). |
+| `apis.discogs.secret` | Discogs Application secret, obtained with client. |
+| `apis.discogs.enabled` | Whether to enable Discogs API usage. |
+| `cors.origin` | Array of allowed request origin urls, for example `http://localhost`. |
+| `smtp.host` | SMTP Host |
+| `smtp.port` | SMTP Port |
+| `smtp.auth.user` | SMTP Username |
+| `smtp.auth.pass` | SMTP Password |
+| `smtp.secure` | Whether SMTP is secured. |
+| `smtp.enabled` | Whether SMTP and sending emails is enabled. |
+| `redis.url` | Should be left as default for Docker installations, else changed to `redis://localhost:6379/0`. |
+| `redis.password` | Redis password. |
+| `mongo.url` | For Docker replace temporary MongoDB musare user password with one specified in `.env`, and for non-Docker replace `@musare:27017` with `@localhost:27017`. |
+| `cookie.domain` | The ip or address you use to access the site, without protocols (http/https), so for example `localhost`. |
+| `cookie.secure` | Should be `true` for SSL connections, and `false` for normal http connections. |
+| `cookie.SIDname` | Name of the cookie stored for sessions. |
+| `skipConfigVersionCheck` | Skips checking if the config version is outdated or not. Should almost always be set to false. |
+| `skipDbDocumentsVersionCheck` | Skips checking if there are any DB documents outdated or not. Should almost always be set to false. |
+| `debug.stationIssue` | If set to `true` it will enable the `/debug_station` API endpoint on the backend, which provides information useful to debugging stations not skipping, as well as capure all jobs specified in `debug.captureJobs`.
+| `debug.traceUnhandledPromises` | Enables the trace-unhandled package, which provides detailed information when a promise is unhandled. |
+| `debug.captureJobs` | Array of jobs to capture for `debug.stationIssue`. |
+| `defaultLogging.hideType` | Filters out specified message types from log, for example `INFO`, `SUCCESS`, `ERROR` and `STATION_ISSUE`. |
+| `defaultLogging.blacklistedTerms` | Filters out messages containing specified terms from log, for example `success`. |
+| `customLoggingPerModule.[module].hideType` | Where `[module]` is a module name specify hideType as you would `defaultLogging.hideType` to overwrite default. |
+| `customLoggingPerModule.[module].blacklistedTerms` | Where `[module]` is a module name specify blacklistedTerms as you would `defaultLogging.blacklistedTerms` to overwrite default. |
+| `configVersion` | Version of the config. Every time the template changes, you should change your config accordingly and update the configVersion. |
+
+## Frontend
+Location: `frontend/dist/config/default.json`
+
+| Property | Description |
+| --- | --- |
+| `mode` | Should be either `development` or `production`. |
+| `backend.apiDomain` | Should be the url where the backend will be accessible from, usually `http://localhost/backend` for docker or `http://localhost:8080` for non-Docker. |
+| `backend.websocketsDomain` | Should be the same as the `apiDomain`, except using the `ws://` protocol instead of `http://` and with `/ws` at the end. |
+| `devServer.webSocketURL` | Should be the webpack-dev-server websocket URL, usually `ws://localhost/ws`. |
+| `devServer.port` | Should be the port where webpack-dev-server will be accessible from, should always be port `81` for Docker since nginx listens on port 80, and is recommended to be port `80` for non-Docker. |
+| `frontendDomain` | Should be the url where the frontend will be accessible from, usually `http://localhost` for docker or `http://localhost:80` for non-Docker. |
+| `recaptcha.key` | ReCaptcha Site v3 key, obtained from [here](https://www.google.com/recaptcha/admin). |
+| `recaptcha.enabled` | Whether to enable ReCaptcha at email registration. |
+| `cookie.domain` | Should be the ip or address you use to access the site, without protocols (http/https), so for example `localhost`. |
+| `cookie.secure` | Should be `true` for SSL connections, and `false` for normal http connections. |
+| `cookie.SIDname` | Name of the cookie stored for sessions. |
+| `siteSettings.logo_white` | Path to the white logo image, by default it is `/assets/white_wordmark.png`. |
+| `siteSettings.logo_blue` | Path to the blue logo image, by default it is `/assets/blue_wordmark.png`. |
+| `siteSettings.sitename` | Should be the name of the site. |
+| `siteSettings.github` | URL of GitHub repository, defaults to `https://github.com/Musare/MusareNode`. |
+| `messages.accountRemoval` | Message to return to users on account removal. |
+| `skipConfigVersionCheck` | Skips checking if the config version is outdated or not. Should almost always be set to false. |
+| `configVersion` | Version of the config. Every time the template changes, you should change your config accordingly and update the configVersion. |
+
+## Docker Environment
+Location: `.env`
+
+In the table below the container host refers to the IP address that the docker container listens on, setting this to `127.0.0.1` for example will only expose the configured port to localhost, whereas setting to `0.0.0.0` will expose the port on all interfaces.
+
+The container port refers to the external docker container port, used to access services within the container. Changing this does not require any changes to configuration within container. For example setting the `MONGO_PORT` to `21018` will allow you to access the mongo service through that port, even though the application within the container is listening on `21017`.
+
+| Property | Description |
+| --- | --- |
+| `COMPOSE_PROJECT_NAME` | Should be a unique name for this installation, especially if you have multiple instances of Musare on the same machine. |
+| `BACKEND_HOST` | Backend container host. |
+| `BACKEND_PORT` | Backend container port. |
+| `FRONTEND_HOST` | Frontend container host. |
+| `FRONTEND_PORT` | Frontend container port. |
+| `FRONTEND_MODE` | Should be either `dev` or `prod`. |
+| `MONGO_HOST` | Mongo container host. |
+| `MONGO_PORT` | Mongo container port. |
+| `MONGO_ROOT_PASSWORD` | Password of the root/admin user for MongoDB. |
+| `MONGO_USER_USERNAME` | Application username for MongoDB. |
+| `MONGO_USER_PASSWORD` | Application password for MongoDB. |
+| `REDIS_HOST` | Redis container host. |
+| `REDIS_PORT` | Redis container port. |
+| `REDIS_PASSWORD` | Redis password. |
+| `BACKUP_LOCATION` | Directory to store musare.sh backups. Defaults to `/backups` in script location. |
+| `BACKUP_NAME` | Name of musare.sh backup files. Defaults to `musare-$(date +"%Y-%m-%d-%s").dump`. |
+The frontend is a [vue-cli](https://github.com/vuejs/vue-cli) generated, [vue-loader](https://github.com/vuejs/vue-loader) single page app, that's served over Nginx or Express. The Nginx server not only serves the frontend, but can also serve as a load balancer for requests going to the backend.
+
+### Backend
+
+The backend is a scalable NodeJS / Redis / MongoDB app. User sessions are stored in a central Redis server. All data is stored in a central MongoDB server. The Redis and MongoDB servers are replicated to several secondary nodes, which can become the primary node if the current primary node goes down.
+
+We currently only utilize 1 backend, 1 MongoDB server and 1 Redis server running for production, though it is relatively easy to expand.
+| `update` | `[auto]` | Update Musare. When auto is specified the update will be cancelled if there are any changes requiring manual intervention, allowing you to run this unattended. |
+| `attach` | `<backend,mongo>` | Attach to backend server or mongodb shell. |
+| `eslint` | `[frontend backend] [fix]` | Run eslint on frontend and/or backend. Specify fix to auto fix issues where possible. |
+| `backup` | | Backup database data to file. Configured in .env file. |
+| `restore` | `[file]` | Restore database from file. If file is not specified you will be prompted. |
+| `reset` | `[frontend backend redis mongo]` | Reset all data for service(s). |
+| `admin` | `<add,remove> [username]` | Assign/unassign admin role to/from user. If the username is not specified you will be prompted. |
+
+### Services
+There are currently 4 services; frontend, backend, redis and mongo. Where services is a parameter you can specify any of these, or multiple seperated by spaces, for example `./musare.sh restart frontend backend` to restart the frontend and backend. If no services are specified all will be selected.
+Every input needs validation, below is the required formatting of each value.
+
+- **User**
+ - Username
+ - Description: Any letter from a-z in any case, numbers, underscores and dashes. Must contain at least 1 letter or number.
+ - Length: From 2 to 32 characters.
+ - Regex: ```/^[A-Za-z0-9_]+$/```
+ - Name
+ - Description: Any letter from any language in any case, numbers, underscores, dashes, periods, apostrophes and spaces. Must contain at least 1 letter or number.
-Based off of the original [Musare](https://github.com/Musare/MusareMeteor), which utilized Meteor.
-
-MusareNode now uses NodeJS, Express, SocketIO and VueJS - among other technologies. We have also implemented the ability to host Musare in [Docker Containers](https://www.docker.com/).
-
-The master branch is available at [musare.com](https://musare.com)
-You can also find the staging branch at [musare.dev](https://musare.dev)
-
-<br />
-
-## Our Stack
-
-- NodeJS
-- MongoDB
-- Redis
-- Nginx (not required)
-- VueJS
-
-### **Frontend**
-
-The frontend is a [vue-cli](https://github.com/vuejs/vue-cli) generated, [vue-loader](https://github.com/vuejs/vue-loader) single page app, that's served over Nginx or Express. The Nginx server not only serves the frontend, but can also serve as a load balancer for requests going to the backend.
-
-### **Backend**
-
-The backend is a scalable NodeJS / Redis / MongoDB app. User sessions are stored in a central Redis server. All data is stored in a central MongoDB server. The Redis and MongoDB servers are replicated to several secondary nodes, which can become the primary node if the current primary node goes down.
-
-We currently only utilize 1 backend, 1 MongoDB server and 1 Redis server running for production, though it is relatively easy to expand.
- | `mode` | Should be either `development` or `production`. No more explanation needed. |
- | `secret` | Whatever you want - used by express's session module. |
- | `domain` | Should be the url where the site will be accessible from,usually `http://localhost` for non-Docker. |
- | `serverDomain` | Should be the url where the backend will be accessible from, usually `http://localhost:8080` for non-Docker. |
- | `serverPort` | Should be the port where the backend will listen on, should always be `8080` for Docker, and is recommended for non-Docker. |
- | `isDocker` | Self-explanatory. Are you using Docker? |
- | `serverPort` | Should be the port where the backend will listen on, should always be `8080` for Docker, and is recommended for non-Docker. |
- | `apis.youtube.key` | Can be obtained by setting up a [YouTube API Key](https://developers.google.com/youtube/v3/getting-started). You need to use the YouTube Data API v3, and create an API key. |
- | `apis.recaptcha.secret` | Can be obtained by setting up a [ReCaptcha Site (v3)](https://www.google.com/recaptcha/admin). |
- | `apis.github` | Can be obtained by setting up a [GitHub OAuth Application](https://github.com/settings/developers). You need to fill in some values to create the OAuth application. The homepage is the homepage of frontend. The authorization callback url is the backend url with `/auth/github/authorize/callback` added at the end. For example `http://localhost:8080/auth/github/authorize/callback`. |
- | `apis.discord.token` | Token for the Discord bot. |
- | `apis.discord.loggingServer` | Server ID of the Discord logging server. |
- | `apis.discord.loggingChannel` | ID of the channel to be used in the Discord logging server. |
- | `apis.mailgun` | Can be obtained by setting up a [Mailgun account](http://www.mailgun.com/), or you can disable it. |
- | `apis.spotify` | Can be obtained by setting up a [Spotify client id](https://developer.spotify.com/dashboard/applications), or you can disable it. |
- | `apis.discogs` | Can be obtained by setting up a [Discogs application](https://www.discogs.com/settings/developers), or you can disable it. |
- | `redis.url` | Should be left alone for Docker, and changed to `redis://localhost:6379/0` for non-Docker. |
- | `redis.password` | Should be the Redis password you either put in your `startRedis.cmd` file for Windows, or `.env` for docker. |
- | `mongo.url` | Needs to have the proper password for the MongoDB musare user, and for non-Docker you need to replace `@musare:27017` with `@localhost:27017`. |
- | `cookie.domain` | Should be the ip or address you use to access the site, without protocols (http/https), so for example `localhost`. |
- | `cookie.secure` | Should be `true` for SSL connections, and `false` for normal http connections. |
- | `serverDomain` | Should be the url where the backend will be accessible from, usually `http://localhost:8080` for non-Docker. |
- | `frontendDomain` | Should be the url where the frontend will be accessible from, usually `http://localhost` for docker or `http://localhost:80` for non-Docker. |
- | `frontendPort` | Should be the port where the frontend will be accessible from, should always be port `81` for Docker, and is recommended to be port `80` for non-Docker. |
- | `recaptcha.key` | Can be obtained by setting up a [ReCaptcha Site (v3)](https://www.google.com/recaptcha/admin). |
- | `cookie.domain` | Should be the ip or address you use to access the site, without protocols (http/https), so for example `localhost`. |
- | `cookie.secure` | Should be `true` for SSL connections, and `false` for normal http connections. |
- | `siteSettings.logo` | Path to the logo image, by default it is `/assets/wordmark.png`. |
- | `siteSettings.siteName` | Should be the name of the site. |
- | `siteSettings.socialLinks` | `github`, `twitter` and `facebook` are set to the official Musare accounts by default, but can be changed. |
-
-5. Simply `cp .env.example .env` to setup your environment variables.
-
-6. To setup [snyk](https://snyk.io/) (which is what we use for our precommit git-hooks), you will need to:
-
- - Setup an account
- - Go to [settings](https://app.snyk.io/account)
- - Copy the API token and set it as your `SNYK_TOKEN` environment variable.
-
- We use snyk to test our dependencies / dev-dependencies for vulnerabilities.
-
-<br />
-
-## Installation
-
-After initial configuration, there are two different options to use for your local development environment.
-
-1) [**Docker**](#docker)
-2) [Standard Setup](#standard-setup)
-
-We **highly recommend using Docker** - both for stability and speed of setup. We also use Docker on our production servers.
-
-<br />
-
-### **Docker**
-
-___
-
-1. Configure the `.env` file to match your settings in `backend/config/default.json`.
-
- | Property | Description |
- | - | - |
- | Ports | Will be how you access the services on your machine, or what ports you will need to specify in your nginx files when using proxy_pass. |
- | `COMPOSE_PROJECT_NAME` | Should be a unique name for this installation, especially if you have multiple instances of Musare on the same machine. |
- | `FRONTEND_MODE` | Should be either `dev` or `prod` (self-explanatory). |
- | `MONGO_ROOT_PASSWORD` | Password of the root/admin user of MongoDB |
- | `MONGO_USER_USERNAME` | Password for the "musare" user (what the backend uses) of MongoDB |
-
-
-2. Install [Docker for Desktop](https://www.docker.com/products/docker-desktop)
-
-3. Build the backend and frontend Docker images (from the root folder)
-
- `docker-compose build`
-
-4. Start the MongoDB database (in detached mode), which will generate the correct MongoDB users based on the `.env` file.
-
- `docker-compose up -d mongo`
-
-5. If you want to use linting extensions in IDEs, then you must attach the IDE to the docker containers. This is entirely [possible with VS Code](https://code.visualstudio.com/docs/remote/containers).
-
-<br />
-
-### **Standard Setup**
-
-___
-
-#### Installation
-
-1. Install [Redis](http://redis.io/download) and [MongoDB](https://www.mongodb.com/download-center#community)
- And again, make sure that the paths lead to the proper config and executable. Replace `PASSWORD` with your Redis password.
-
-<br />
-
-## Everyday usage
-
-<br />
-
-### **Docker**
-
-___
-
-1. Start the MongoDB database in the background.
-
- `docker-compose up -d mongo`
-
-2. Start redis and the mongo client in the background, as we usually don't need to monitor these for errors.
-
- `docker-compose up -d mongoclient redis`
-
-3. Start the backend and frontend in the foreground, so we can watch for errors during development.
-
- `docker-compose up backend frontend`
-
-4. You should now be able to begin development!
-
- The backend is auto reloaded when you make changes and the frontend is auto compiled and live reloaded by webpack when you make changes.
-
- You should be able to access Musare in your local browser at `http://localhost:8080/`.
-
-<br />
-
-### **Standard Setup**
-
-___
-
-##### Automatic
-
-1. If you are on Windows you can run `windows-start.cmd` or just double click the `windows-start.cmd` file and all servers will automatically start up.
-
-##### Manual
-
-1. Run `startRedis.cmd` and `startMongo.cmd` to start Redis and Mongo.
+ this.uniqueId = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
+ const r = (Math.random() * 16) | 0;
+ const v = c == "x" ? r : (r & 0x3) | 0x8;
+ return v.toString(16);
+ });
+ this.status = "INITIALIZED";
+ this.task = null;
+ }
+
+ /**
+ * Adds a child job to this job
+ *
+ * @param {object} childJob - the child job
+ */
+ addChildJob(childJob) {
+ this.childJobs.push(childJob);
+ }
+
+ /**
+ * Sets the job status
+ *
+ * @param {string} status - the new status
+ */
+ setStatus(status) {
+ this.status = status;
+ }
+
+ /**
+ * Sets the task for a job
+ *
+ * @param {string} task - the job task
+ */
+ setTask(task) {
+ this.task = task;
+ }
+
+ /**
+ * Returns the UUID of the job, allowing you to compare jobs with toString
+ *
+ * @returns {string} - the job's UUID/uniqueId
+ */
+ toString() {
+ return this.uniqueId;
+ }
+
+ /**
+ * Sets the response that will be provided to the onFinish DeferredPromise resolve/reject function, as soon as the job is done if it has no parent, or when the parent job is resumed
+ *
+ * @param {object} response - the response
+ */
+ setResponse(response) {
+ this.response = response;
+ }
+
+ /**
+ * Sets the response type that is paired with the response. If it is RESOLVE/REJECT, then it will resolve/reject with the response. If it is RESOLVED/REJECTED, then it has already resolved/rejected with the response.
+ *
+ * @param {string} responseType - the response type, so RESOLVE/REJECT/RESOLVED/REJECTED
-// if (this.modulesLeft.length === 0) this.allModulesInitialized();
-// }
-
-// allModulesInitialized() {
-// this.logger.success("MODULE_MANAGER", "All modules have started!");
-// this.modules["discord"].sendAdminAlertMessage("The backend server started successfully.", "#00AA00", "Startup", false, []);
-// }
-
-// aModuleFailed(failedModule) {
-// this.logger.error("MODULE_MANAGER", `A module has failed, locking down. Module: ${failedModule.name}`);
-// this.modules["discord"].sendAdminAlertMessage(`The backend server failed to start due to a failing module: ${failedModule.name}.`, "#AA0000", "Startup", false, []);
- message: "The backend server started successfully.",
- color: "#00AA00",
- type: "Startup",
- critical: false,
- extraFields: [],
- });
- }
+ // eslint-disable-next-line require-jsdoc
+ constructor() {
+ this.modules = {};
+ this.modulesNotInitialized = [];
+ this.jobManager = new JobManager();
+ this.i = 0;
+ this.lockdown = false;
+ this.debugLogs = {
+ stationIssue: []
+ };
+ this.debugJobs = {
+ all: [],
+ completed: []
+ };
+ this.name = "MODULE_MANAGER";
+ }
+
+ /**
+ * Adds a new module to the backend server/module manager
+ *
+ * @param {string} moduleName - the name of the module (also needs to be the same as the filename of a module located in the logic folder or "logic/moduleName/index.js")
+ )}${module.getStatus()}. Jobs in queue: ${module.jobQueue.lengthQueue()}. Jobs in progress: ${module.jobQueue.lengthRunning()}. Jobs paused: ${module.jobQueue.lengthPaused()} Concurrency: ${
- `Importing a YouTube playlist to private playlist "${playlistId}" failed for user "${session.userId}". "${err}"`
- );
- return cb({ status: "failure", message: err });
- } else {
- activities.runJob("ADD_ACTIVITY", {
- userId: session.userId,
- activityType: "added_songs_to_playlist",
- payload: addedSongs,
- });
- console.log(
- "SUCCESS",
- "PLAYLIST_IMPORT",
- `Successfully imported a YouTube playlist to private playlist "${playlistId}" for user "${session.userId}". Videos in playlist: ${videosInPlaylistTotal}, songs in playlist: ${songsInPlaylistTotal}, songs successfully added: ${songsSuccess}, songs failed: ${songsFail}.`
- );
- cb({
- status: "success",
- message: "Playlist has been successfully imported.",
- data: playlist.songs,
- stats: {
- videosInPlaylistTotal,
- songsInPlaylistTotal,
- songsAddedSuccessfully: songsSuccess,
- songsFailedToAdd: songsFail,
- },
- });
- }
- }
- );
- }
- ),
-
- /**
- * Removes a song from a private playlist
- *
- * @param {Object} session - the session object automatically added by socket.io
- * @param {String} songId - the id of the song we are removing from the private playlist
- * @param {String} playlistId - the id of the playlist we are removing the song from
- * @param {Function} cb - gets called with the result
+ `Importing a YouTube playlist to private playlist "${playlistId}" failed for user "${session.userId}". "${err}"`
+ );
+ return cb({ status: "error", message: err });
+ }
+
+ if (playlist.privacy === "public")
+ ActivitiesModule.runJob("ADD_ACTIVITY", {
+ userId: session.userId,
+ type: "playlist__import_playlist",
+ payload: {
+ message: `Imported ${addSongsStats.successful} songs to playlist <playlistId>${playlist.displayName}</playlistId>`,
+ playlistId
+ }
+ });
+
+ this.log(
+ "SUCCESS",
+ "PLAYLIST_IMPORT",
+ `Successfully imported a YouTube playlist to private playlist "${playlistId}" for user "${session.userId}". Videos in playlist: ${videosInPlaylistTotal}, songs in playlist: ${songsInPlaylistTotal}, songs successfully added: ${addSongsStats.successful}, songs failed: ${addSongsStats.failed}, already in playlist: ${addSongsStats.alreadyInPlaylist}.`
+ );
+
+ return cb({
+ status: "success",
+ message: `Playlist has been imported. ${addSongsStats.successful} were added successfully, ${addSongsStats.failed} failed (${addSongsStats.alreadyInPlaylist} were already in the playlist)`,
+ data: {
+ songs: playlist.songs,
+ stats: {
+ videosInPlaylistTotal,
+ songsInPlaylistTotal
+ }
+ }
+ });
+ }
+ );
+ }),
+
+ /**
+ * Removes a song from a private playlist
+ *
+ * @param {object} session - the session object automatically added by the websocket
+ * @param {string} youtubeId - the youtube id of the song we are removing from the private playlist
+ * @param {string} playlistId - the id of the playlist we are removing the song from
+ * @param {Function} cb - gets called with the result
+ */
+ removeSongFromPlaylist: isLoginRequired(async function removeSongFromPlaylist(session, youtubeId, playlistId, cb) {
+ `Importing a YouTube playlist to be requested failed for user "${session.userId}". "${err}"`
+ );
+ return cb({ status: "error", message: err });
+ }
+ this.log(
+ "SUCCESS",
+ "REQUEST_SET",
+ `Successfully imported a YouTube playlist to be requested for user "${session.userId}".`
+ );
+ return cb({
+ status: "success",
+ message: `Playlist is done importing. ${response.successful} were added succesfully, ${response.failed} failed (${response.alreadyInDatabase} were already in database)`,
+ songs: returnSongs ? response.songs : null
+ });
+ }
+ );
+ }),
+
+ /**
+ * Likes a song
+ *
+ * @param session
+ * @param youtubeId - the youtube id
+ * @param cb
+ */
+ like: isLoginRequired(async function like(session, youtubeId, cb) {
- // TODO: Check every 30s/60s, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
+ * This migration is used to set the documentVersion to 1 for all documents that don't have a documentVersion yet, meaning they were created before the migration system
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+ * Updates the document version 1 stations to add the includedPlaylists and excludedPlaylists properties, and to create a station playlist and link that playlist with the playlist2 property.
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+ * Clean up station object from playlist2 property (replacing old playlist property with playlist2 property), adding a playMode property and removing genres/blacklisted genres
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+ // TODO: Check every 30s/, for all sockets, if they are still allowed to be in the rooms they are in, and on socket at all (permission changing/banning)
+ const server = await AppModule.runJob("SERVER");
+
+ this._io = new WebSocketServer({ server, path: "/ws" });