From 5a90be7703fe2a4b500e66139d4d6d71daa611c8 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 8 Aug 2021 14:54:24 -0600 Subject: [PATCH] Logger is now separated into its own module Added eslint and fixed many logic errors based on its recommendations --- .eslintrc.json | 20 + backend/.eslintrc.json | 18 + backend/app.js | 160 ++---- backend/authentication/auth.js | 19 +- backend/categories.js | 45 +- backend/config.js | 10 +- backend/consts.js | 13 +- backend/db.js | 18 +- backend/logger.js | 23 + backend/subscriptions.js | 74 +-- backend/utils.js | 32 +- package-lock.json | 813 +++++++++++++++++++++++++++++ package.json | 3 + src/app/player/player.component.ts | 1 - 14 files changed, 1005 insertions(+), 244 deletions(-) create mode 100644 .eslintrc.json create mode 100644 backend/.eslintrc.json create mode 100644 backend/logger.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..c5624c4 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + } +} diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json new file mode 100644 index 0000000..172a2e4 --- /dev/null +++ b/backend/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "env": { + "node": true, + "es2021": true + }, + "extends": [ + "eslint:recommended" + ], + "parser": "esprima", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": [], + "rules": { + }, + "root": true +} diff --git a/backend/app.js b/backend/app.js index a9025e9..3154f05 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,35 +1,35 @@ const { uuid } = require('uuidv4'); -var fs = require('fs-extra'); -var { promisify } = require('util'); -var auth_api = require('./authentication/auth'); -var winston = require('winston'); -var path = require('path'); -var ffmpeg = require('fluent-ffmpeg'); -var compression = require('compression'); -var glob = require("glob") -var multer = require('multer'); -var express = require("express"); -var bodyParser = require("body-parser"); -var archiver = require('archiver'); -var unzipper = require('unzipper'); -var db_api = require('./db'); -var utils = require('./utils') -var mergeFiles = require('merge-files'); +const fs = require('fs-extra'); +const { promisify } = require('util'); +const auth_api = require('./authentication/auth'); +const winston = require('winston'); +const path = require('path'); +const compression = require('compression'); +const glob = require("glob") +const multer = require('multer'); +const express = require("express"); +const bodyParser = require("body-parser"); +const archiver = require('archiver'); +const unzipper = require('unzipper'); +const db_api = require('./db'); +const utils = require('./utils') +const mergeFiles = require('merge-files'); const low = require('lowdb') -var ProgressBar = require('progress'); +const ProgressBar = require('progress'); const NodeID3 = require('node-id3') const fetch = require('node-fetch'); -var URL = require('url').URL; +const URL = require('url').URL; const url_api = require('url'); const CONSTS = require('./consts') const read_last_lines = require('read-last-lines'); -var ps = require('ps-node'); +const ps = require('ps-node'); // needed if bin/details somehow gets deleted if (!fs.existsSync(CONSTS.DETAILS_BIN_PATH)) fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"}) -var youtubedl = require('youtube-dl'); +const youtubedl = require('youtube-dl'); +const logger = require('./logger'); var config_api = require('./config.js'); var subscriptions_api = require('./subscriptions') var categories_api = require('./categories'); @@ -61,30 +61,11 @@ const admin_token = '4241b401-7236-493e-92b5-b72696b9d853'; // logging setup -// console format -const defaultFormat = winston.format.printf(({ level, message, label, timestamp }) => { - return `${timestamp} ${level.toUpperCase()}: ${message}`; -}); -const logger = winston.createLogger({ - level: 'info', - format: winston.format.combine(winston.format.timestamp(), defaultFormat), - defaultMeta: {}, - transports: [ - // - // - Write to all logs with level `info` and below to `combined.log` - // - Write all logs error (and below) to `error.log`. - // - new winston.transports.File({ filename: 'appdata/logs/error.log', level: 'error' }), - new winston.transports.File({ filename: 'appdata/logs/combined.log' }), - new winston.transports.Console({level: !debugMode ? 'info' : 'debug', name: 'console'}) - ] -}); - -config_api.initialize(logger); -db_api.initialize(db, users_db, logger); -auth_api.initialize(db_api, logger); -subscriptions_api.initialize(db_api, logger); -categories_api.initialize(db, users_db, logger, db_api); +config_api.initialize(); +db_api.initialize(db, users_db); +auth_api.initialize(db_api); +subscriptions_api.initialize(db_api); +categories_api.initialize(db_api); // Set some defaults db.defaults( @@ -122,13 +103,9 @@ users_db.defaults( ).write(); // config values -var frontendUrl = null; -var backendUrl = null; var backendPort = null; -var basePath = null; var audioFolderPath = null; var videoFolderPath = null; -var downloadOnlyMode = null; var useDefaultDownloadingAgent = null; var customDownloadingAgent = null; var allowSubscriptions = null; @@ -138,8 +115,6 @@ var archivePath = path.join(__dirname, 'appdata', 'archives'); var url_domain = null; var updaterStatus = null; -var timestamp_server_start = Date.now(); - const concurrentStreams = {}; if (debugMode) logger.info('YTDL-Material in debug mode!'); @@ -368,6 +343,7 @@ async function updateServer(tag) { } restartServer(true); }, err => { + logger.error(err); updaterStatus = { updating: false, error: true, @@ -398,12 +374,10 @@ async function downloadReleaseFiles(tag) { fs.createReadStream(path.join(__dirname, `youtubedl-material-release-${tag}.zip`)).pipe(unzipper.Parse()) .on('entry', function (entry) { var fileName = entry.path; - var type = entry.type; // 'Directory' or 'File' - var size = entry.size; var is_dir = fileName.substring(fileName.length-1, fileName.length) === '/' if (!is_dir && fileName.includes('youtubedl-material/public/')) { // get public folder files - var actualFileName = fileName.replace('youtubedl-material/public/', ''); + const actualFileName = fileName.replace('youtubedl-material/public/', ''); if (actualFileName.length !== 0 && actualFileName.substring(actualFileName.length-1, actualFileName.length) !== '/') { fs.ensureDirSync(path.join(__dirname, 'public', path.dirname(actualFileName))); entry.pipe(fs.createWriteStream(path.join(__dirname, 'public', actualFileName))); @@ -412,7 +386,7 @@ async function downloadReleaseFiles(tag) { } } else if (!is_dir && !replace_ignore_list.includes(fileName)) { // get package.json - var actualFileName = fileName.replace('youtubedl-material/', ''); + const actualFileName = fileName.replace('youtubedl-material/', ''); logger.verbose('Downloading file ' + actualFileName); entry.pipe(fs.createWriteStream(path.join(__dirname, actualFileName))); } else { @@ -750,17 +724,6 @@ function generateEnvVarConfigItem(key) { return {key: key, value: process['env'][key]}; } -function getVideoFormatID(name) -{ - var jsonPath = videoFolderPath+name+".info.json"; - if (fs.existsSync(jsonPath)) - { - var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - var format = obj.format.substring(0,3); - return format; - } -} - /** * @param {'audio' | 'video'} type * @param {string[]} fileNames @@ -808,16 +771,11 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) { let category = null; // prepend with user if needed - let multiUserMode = null; if (options.user) { let usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); const user_path = path.join(usersFileFolder, options.user, type); fs.ensureDirSync(user_path); fileFolderPath = user_path + path.sep; - multiUserMode = { - user: options.user, - file_path: fileFolderPath - } options.customFileFolderPath = fileFolderPath; } @@ -947,11 +905,8 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) { if (!success) logger.error('Failed to apply ID3 tag to audio file ' + output_json['_filename']); } - const file_path = options.noRelativePath ? path.basename(full_file_path) : full_file_path.substring(fileFolderPath.length, full_file_path.length); - const customPath = options.noRelativePath ? path.dirname(full_file_path).split(path.sep).pop() : null; - if (options.cropFileSettings) { - await cropFile(full_file_path, options.cropFileSettings.cropFileStart, options.cropFileSettings.cropFileEnd, ext); + await utils.cropFile(full_file_path, options.cropFileSettings.cropFileStart, options.cropFileSettings.cropFileEnd, ext); } // registers file in DB @@ -1022,6 +977,8 @@ async function generateArgs(url, type, options) { var youtubePassword = options.youtubePassword; let downloadConfig = null; + + // TODO: fix let qualityPath = (is_audio && !options.skip_audio_args) ? ['-f', 'bestaudio'] : ['-f', 'bestvideo+bestaudio', '--merge-output-format', 'mp4']; const is_youtube = url.includes('youtu'); if (!is_audio && !is_youtube) { @@ -1197,33 +1154,6 @@ async function getUrlInfos(urls) { }); } -// ffmpeg helper functions - -async function cropFile(file_path, start, end, ext) { - return new Promise(resolve => { - const temp_file_path = `${file_path}.cropped${ext}`; - let base_ffmpeg_call = ffmpeg(file_path); - if (start) { - base_ffmpeg_call = base_ffmpeg_call.seekOutput(start); - } - if (end) { - base_ffmpeg_call = base_ffmpeg_call.duration(end - start); - } - base_ffmpeg_call - .on('end', () => { - logger.verbose(`Cropping for '${file_path}' complete.`); - fs.unlinkSync(file_path); - fs.moveSync(temp_file_path, file_path); - resolve(true); - }) - .on('error', (err, test, test2) => { - logger.error(`Failed to crop ${file_path}.`); - logger.error(err); - resolve(false); - }).save(temp_file_path); - }); -} - // download management functions async function updateDownloads() { @@ -1951,14 +1881,12 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => { let subID = req.body.id; let subName = req.body.name; // if included, subID is optional - let user_uid = req.isAuthenticated() ? req.user.uid : null; - // get sub from db let subscription = null; if (subID) { - subscription = await subscriptions_api.getSubscription(subID, user_uid) + subscription = await subscriptions_api.getSubscription(subID) } else if (subName) { - subscription = await subscriptions_api.getSubscriptionByName(subName, user_uid) + subscription = await subscriptions_api.getSubscriptionByName(subName) } if (!subscription) { @@ -2124,28 +2052,6 @@ app.post('/api/getPlaylists', optionalJwt, async (req, res) => { }); }); -app.post('/api/updatePlaylistFiles', optionalJwt, async (req, res) => { - let playlistID = req.body.playlist_id; - let uids = req.body.uids; - - let success = false; - try { - if (req.isAuthenticated()) { - auth_api.updatePlaylistFiles(req.user.uid, playlistID, uids); - } else { - await db_api.updateRecord('playlists', {id: playlistID}, {uids: uids}) - } - - success = true; - } catch(e) { - logger.error(`Failed to find playlist with ID ${playlistID}`); - } - - res.send({ - success: success - }) -}); - app.post('/api/addFileToPlaylist', optionalJwt, async (req, res) => { let playlist_id = req.body.playlist_id; let file_uid = req.body.file_uid; diff --git a/backend/authentication/auth.js b/backend/authentication/auth.js index 7992aac..caea4da 100644 --- a/backend/authentication/auth.js +++ b/backend/authentication/auth.js @@ -1,7 +1,7 @@ -const path = require('path'); const config_api = require('../config'); const consts = require('../consts'); -const fs = require('fs-extra'); +const logger = require('../logger'); + const jwt = require('jsonwebtoken'); const { uuid } = require('uuidv4'); const bcrypt = require('bcryptjs'); @@ -12,15 +12,13 @@ var JwtStrategy = require('passport-jwt').Strategy, ExtractJwt = require('passport-jwt').ExtractJwt; // other required vars -let logger = null; let db_api = null; let SERVER_SECRET = null; let JWT_EXPIRATION = null; let opts = null; let saltRounds = null; -exports.initialize = function(db_api, input_logger) { - setLogger(input_logger) +exports.initialize = function(db_api) { setDB(db_api); /************************* @@ -53,10 +51,6 @@ exports.initialize = function(db_api, input_logger) { })); } -function setLogger(input_logger) { - logger = input_logger; -} - function setDB(input_db_api) { db_api = input_db_api; } @@ -291,17 +285,12 @@ exports.getUserVideo = async function(user_uid, file_uid, requireSharing = false return file; } -exports.updatePlaylistFiles = function(user_uid, playlistID, new_filenames) { - users_db.get('users').find({uid: user_uid}).get(`playlists`).find({id: playlistID}).assign({fileNames: new_filenames}); - return true; -} - exports.removePlaylist = async function(user_uid, playlistID) { await db_api.removeRecord('playlist', {playlistID: playlistID}); return true; } -exports.getUserPlaylists = async function(user_uid, user_files = null) { +exports.getUserPlaylists = async function(user_uid) { return await db_api.getRecords('playlists', {user_uid: user_uid}); } diff --git a/backend/categories.js b/backend/categories.js index fb33a88..269ae9c 100644 --- a/backend/categories.js +++ b/backend/categories.js @@ -1,17 +1,12 @@ -const config_api = require('./config'); const utils = require('./utils'); +const logger = require('./logger'); -var logger = null; -var db = null; -var users_db = null; var db_api = null; -function setDB(input_db, input_users_db, input_db_api) { db = input_db; users_db = input_users_db; db_api = input_db_api } -function setLogger(input_logger) { logger = input_logger; } +function setDB(input_db_api) { db_api = input_db_api } -function initialize(input_db, input_users_db, input_logger, input_db_api) { - setDB(input_db, input_users_db, input_db_api); - setLogger(input_logger); +function initialize(input_db_api) { + setDB(input_db_api); } /* @@ -72,7 +67,7 @@ async function getCategoriesAsPlaylists(files = null) { const categories_as_playlists = []; const available_categories = await getCategories(); if (available_categories && files) { - for (category of available_categories) { + for (let category of available_categories) { const files_that_match = utils.addUIDsToCategory(category, files); if (files_that_match && files_that_match.length > 0) { category['thumbnailURL'] = files_that_match[0].thumbnailURL; @@ -125,21 +120,21 @@ function applyCategoryRules(file_json, rules, category_name) { return rules_apply; } -async function addTagToVideo(tag, video, user_uid) { - // TODO: Implement -} - -async function removeTagFromVideo(tag, video, user_uid) { - // TODO: Implement -} - -// adds tag to list of existing tags (used for tag suggestions) -async function addTagToExistingTags(tag) { - const existing_tags = db.get('tags').value(); - if (!existing_tags.includes(tag)) { - db.get('tags').push(tag).write(); - } -} +// async function addTagToVideo(tag, video, user_uid) { +// // TODO: Implement +// } + +// async function removeTagFromVideo(tag, video, user_uid) { +// // TODO: Implement +// } + +// // adds tag to list of existing tags (used for tag suggestions) +// async function addTagToExistingTags(tag) { +// const existing_tags = db.get('tags').value(); +// if (!existing_tags.includes(tag)) { +// db.get('tags').push(tag).write(); +// } +// } module.exports = { initialize: initialize, diff --git a/backend/config.js b/backend/config.js index c0151d7..c8a8aaa 100644 --- a/backend/config.js +++ b/backend/config.js @@ -1,3 +1,5 @@ +const logger = require('./logger'); + const fs = require('fs'); let CONFIG_ITEMS = require('./consts.js')['CONFIG_ITEMS']; @@ -5,11 +7,7 @@ const debugMode = process.env.YTDL_MODE === 'debug'; let configPath = debugMode ? '../src/assets/default.json' : 'appdata/default.json'; -var logger = null; -function setLogger(input_logger) { logger = input_logger; } - -function initialize(input_logger) { - setLogger(input_logger); +function initialize() { ensureConfigFileExists(); ensureConfigItemsExist(); } @@ -175,7 +173,7 @@ module.exports = { globalArgsRequiresSafeDownload: globalArgsRequiresSafeDownload } -DEFAULT_CONFIG = { +const DEFAULT_CONFIG = { "YoutubeDLMaterial": { "Host": { "url": "http://example.com", diff --git a/backend/consts.js b/backend/consts.js index b26eb91..28ee62c 100644 --- a/backend/consts.js +++ b/backend/consts.js @@ -1,4 +1,4 @@ -let CONFIG_ITEMS = { +exports.CONFIG_ITEMS = { // Host 'ytdl_url': { 'key': 'ytdl_url', @@ -210,7 +210,7 @@ let CONFIG_ITEMS = { } }; -AVAILABLE_PERMISSIONS = [ +exports.AVAILABLE_PERMISSIONS = [ 'filemanager', 'settings', 'subscriptions', @@ -219,11 +219,6 @@ AVAILABLE_PERMISSIONS = [ 'downloads_manager' ]; -const DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details' +exports.DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details' -module.exports = { - CONFIG_ITEMS: CONFIG_ITEMS, - AVAILABLE_PERMISSIONS: AVAILABLE_PERMISSIONS, - CURRENT_VERSION: 'v4.2', - DETAILS_BIN_PATH: DETAILS_BIN_PATH -} +exports.CURRENT_VERSION = 'v4.2'; diff --git a/backend/db.js b/backend/db.js index 802694d..74a92c5 100644 --- a/backend/db.js +++ b/backend/db.js @@ -1,19 +1,18 @@ var fs = require('fs-extra') var path = require('path') -var utils = require('./utils') +const { MongoClient } = require("mongodb"); const { uuid } = require('uuidv4'); + const config_api = require('./config'); -const { MongoClient } = require("mongodb"); +var utils = require('./utils') +const logger = require('./logger'); const low = require('lowdb') const FileSync = require('lowdb/adapters/FileSync'); const local_adapter = new FileSync('./appdata/local_db.json'); const local_db = low(local_adapter); -var logger = null; -var db = null; -var users_db = null; -var database = null; +let database = null; const tables = { files: { @@ -62,13 +61,8 @@ function setDB(input_db, input_users_db) { exports.users_db = input_users_db } -function setLogger(input_logger) { - logger = input_logger; -} - -exports.initialize = (input_db, input_users_db, input_logger) => { +exports.initialize = (input_db, input_users_db) => { setDB(input_db, input_users_db); - setLogger(input_logger); // must be done here to prevent getConfigItem from being called before init using_local_db = config_api.getConfigItem('ytdl_use_local_db'); diff --git a/backend/logger.js b/backend/logger.js new file mode 100644 index 0000000..9bdd429 --- /dev/null +++ b/backend/logger.js @@ -0,0 +1,23 @@ +const winston = require('winston'); + +let debugMode = process.env.YTDL_MODE === 'debug'; + +const defaultFormat = winston.format.printf(({ level, message, label, timestamp }) => { + return `${timestamp} ${level.toUpperCase()}: ${message}`; +}); +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine(winston.format.timestamp(), defaultFormat), + defaultMeta: {}, + transports: [ + // + // - Write to all logs with level `info` and below to `combined.log` + // - Write all logs error (and below) to `error.log`. + // + new winston.transports.File({ filename: 'appdata/logs/error.log', level: 'error' }), + new winston.transports.File({ filename: 'appdata/logs/combined.log' }), + new winston.transports.Console({level: !debugMode ? 'info' : 'debug', name: 'console'}) + ] +}); + +module.exports = logger; \ No newline at end of file diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 89f0ff6..ece25f6 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -1,27 +1,20 @@ -const FileSync = require('lowdb/adapters/FileSync') +const fs = require('fs-extra'); +const path = require('path'); +const youtubedl = require('youtube-dl'); -var fs = require('fs-extra'); -const { uuid } = require('uuidv4'); -var path = require('path'); - -var youtubedl = require('youtube-dl'); const config_api = require('./config'); const twitch_api = require('./twitch'); -var utils = require('./utils'); +const utils = require('./utils'); +const logger = require('./logger'); const debugMode = process.env.YTDL_MODE === 'debug'; -var logger = null; -var db = null; -var users_db = null; let db_api = null; function setDB(input_db_api) { db_api = input_db_api } -function setLogger(input_logger) { logger = input_logger; } -function initialize(input_db_api, input_logger) { +function initialize(input_db_api) { setDB(input_db_api); - setLogger(input_logger); } async function subscribe(sub, user_uid = null) { @@ -52,7 +45,7 @@ async function subscribe(sub, user_uid = null) { getVideosForSub(sub, user_uid); } else { logger.error('Subscribe: Failed to get subscription info. Subscribe failed.') - }; + } result_obj.success = success; result_obj.sub = sub; @@ -146,7 +139,6 @@ async function unsubscribe(sub, deleteMode, user_uid = null) { basePath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, 'subscriptions'); else basePath = config_api.getConfigItem('ytdl_subscriptions_base_path'); - let result_obj = { success: false, error: '' }; let id = sub.id; @@ -451,39 +443,26 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de } async function handleOutputJSON(sub, output_json, multiUserMode = null, reset_videos = false) { - // TODO: remove streaming only mode - if (false && sub.streamingOnly) { - if (reset_videos) { - sub_db.assign({videos: []}).write(); - } - - // remove unnecessary info - output_json.formats = null; - - // add to db - sub_db.get('videos').push(output_json).write(); - } else { - path_object = path.parse(output_json['_filename']); - const path_string = path.format(path_object); + const path_object = path.parse(output_json['_filename']); + const path_string = path.format(path_object); - const file_exists = await db_api.getRecord('files', {path: path_string, sub_id: sub.id}); - if (file_exists) { - // TODO: fix issue where files of different paths due to custom path get downloaded multiple times - // file already exists in DB, return early to avoid reseting the download date - return; - } + const file_exists = await db_api.getRecord('files', {path: path_string, sub_id: sub.id}); + if (file_exists) { + // TODO: fix issue where files of different paths due to custom path get downloaded multiple times + // file already exists in DB, return early to avoid reseting the download date + return; + } - await db_api.registerFileDB2(output_json['_filename'], sub.type, sub.user_uid, null, sub.id); + await db_api.registerFileDB2(output_json['_filename'], sub.type, sub.user_uid, null, sub.id); - const url = output_json['webpage_url']; - if (sub.type === 'video' && url.includes('twitch.tv/videos/') && url.split('twitch.tv/videos/').length > 1 - && config_api.getConfigItem('ytdl_use_twitch_api') && config_api.getConfigItem('ytdl_twitch_auto_download_chat')) { - const file_name = path.basename(output_json['_filename']); - const id = file_name.substring(0, file_name.length-4); - let vodId = url.split('twitch.tv/videos/')[1]; - vodId = vodId.split('?')[0]; - twitch_api.downloadTwitchChatByVODID(vodId, id, sub.type, multiUserMode.user, sub); - } + const url = output_json['webpage_url']; + if (sub.type === 'video' && url.includes('twitch.tv/videos/') && url.split('twitch.tv/videos/').length > 1 + && config_api.getConfigItem('ytdl_use_twitch_api') && config_api.getConfigItem('ytdl_twitch_auto_download_chat')) { + const file_name = path.basename(output_json['_filename']); + const id = file_name.substring(0, file_name.length-4); + let vodId = url.split('twitch.tv/videos/')[1]; + vodId = vodId.split('?')[0]; + twitch_api.downloadTwitchChatByVODID(vodId, id, sub.type, multiUserMode.user, sub); } } @@ -505,7 +484,7 @@ async function getSubscriptionByName(subName, user_uid = null) { return await db_api.getRecord('subscriptions', {name: subName, user_uid: user_uid}); } -async function updateSubscription(sub, user_uid = null) { +async function updateSubscription(sub) { await db_api.updateRecord('subscriptions', {id: sub.id}, sub); return true; } @@ -516,7 +495,7 @@ async function updateSubscriptionPropertyMultiple(subs, assignment_obj) { }); } -async function updateSubscriptionProperty(sub, assignment_obj, user_uid = null) { +async function updateSubscriptionProperty(sub, assignment_obj) { // TODO: combine with updateSubscription await db_api.updateRecord('subscriptions', {id: sub.id}, assignment_obj); return true; @@ -585,7 +564,6 @@ module.exports = { unsubscribe : unsubscribe, deleteSubscriptionFile : deleteSubscriptionFile, getVideosForSub : getVideosForSub, - setLogger : setLogger, initialize : initialize, updateSubscriptionPropertyMultiple : updateSubscriptionPropertyMultiple } diff --git a/backend/utils.js b/backend/utils.js index 9370cf4..f37db82 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -1,6 +1,8 @@ const fs = require('fs-extra') const path = require('path') +const ffmpeg = require('fluent-ffmpeg'); const config_api = require('./config'); +const logger = require('./logger'); const CONSTS = require('./consts') const archiver = require('archiver'); @@ -349,6 +351,33 @@ function removeFileExtension(filename) { return filename_parts.join('.'); } +// ffmpeg helper functions + +async function cropFile(file_path, start, end, ext) { + return new Promise(resolve => { + const temp_file_path = `${file_path}.cropped${ext}`; + let base_ffmpeg_call = ffmpeg(file_path); + if (start) { + base_ffmpeg_call = base_ffmpeg_call.seekOutput(start); + } + if (end) { + base_ffmpeg_call = base_ffmpeg_call.duration(end - start); + } + base_ffmpeg_call + .on('end', () => { + logger.verbose(`Cropping for '${file_path}' complete.`); + fs.unlinkSync(file_path); + fs.moveSync(temp_file_path, file_path); + resolve(true); + }) + .on('error', (err) => { + logger.error(`Failed to crop ${file_path}.`); + logger.error(err); + resolve(false); + }).save(temp_file_path); + }); +} + /** * setTimeout, but its a promise. * @param {number} ms @@ -390,7 +419,7 @@ module.exports = { fixVideoMetadataPerms2: fixVideoMetadataPerms2, deleteJSONFile: deleteJSONFile, deleteJSONFile2: deleteJSONFile2, - removeIDFromArchive, removeIDFromArchive, + removeIDFromArchive: removeIDFromArchive, getDownloadedFilesByType: getDownloadedFilesByType, createContainerZipFile: createContainerZipFile, durationStringToNumber: durationStringToNumber, @@ -399,6 +428,7 @@ module.exports = { getCurrentDownloader: getCurrentDownloader, recFindByExt: recFindByExt, removeFileExtension: removeFileExtension, + cropFile: cropFile, wait: wait, File: File } diff --git a/package-lock.json b/package-lock.json index 0515bca..2ac2dfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1651,6 +1651,85 @@ } } }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", @@ -1884,6 +1963,201 @@ } } }, + "@typescript-eslint/eslint-plugin": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz", + "integrity": "sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.29.0", + "@typescript-eslint/scope-manager": "4.29.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz", + "integrity": "sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.0.tgz", + "integrity": "sha512-+92YRNHFdXgq+GhWQPT2bmjX09X7EH36JfgN2/4wmhtwV/HPxozpCNst8jrWcngLtEVd/4zAwA6BKojAlf+YqA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz", + "integrity": "sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0" + } + }, + "@typescript-eslint/types": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.0.tgz", + "integrity": "sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz", + "integrity": "sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz", + "integrity": "sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "eslint-visitor-keys": "^2.0.0" + } + }, "@videogular/ngx-videogular": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@videogular/ngx-videogular/-/ngx-videogular-2.1.0.tgz", @@ -2117,6 +2391,12 @@ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "dev": true }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, "adjust-sourcemap-loader": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", @@ -2405,6 +2685,12 @@ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -4144,6 +4430,12 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "default-gateway": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", @@ -4402,6 +4694,15 @@ "buffer-indexof": "^1.0.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-serialize": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", @@ -4684,6 +4985,15 @@ } } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", @@ -4827,6 +5137,249 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -4837,12 +5390,69 @@ "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -5220,6 +5830,12 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fastparse": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", @@ -5268,6 +5884,15 @@ "escape-string-regexp": "^1.0.5" } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "file-loader": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz", @@ -5427,6 +6052,24 @@ "resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.0.tgz", "integrity": "sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ==" }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + } + } + }, "flatted": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", @@ -5553,6 +6196,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "genfun": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", @@ -7008,6 +7657,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -7414,6 +8069,16 @@ } } }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "license-webpack-plugin": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz", @@ -7489,12 +8154,30 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -8158,6 +8841,12 @@ "dev": true, "optional": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -8674,6 +9363,20 @@ } } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "ora": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", @@ -9953,6 +10656,12 @@ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -10680,6 +11389,12 @@ } } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "regjsgen": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", @@ -10762,6 +11477,12 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -11517,6 +12238,43 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "smart-buffer": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", @@ -12379,6 +13137,40 @@ "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==", "dev": true }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "tapable": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", @@ -12815,6 +13607,15 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-fest": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", @@ -13138,6 +13939,12 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "v8flags": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", @@ -14386,6 +15193,12 @@ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", diff --git a/package.json b/package.json index a1c0482..ad1f6f9 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,11 @@ "@types/file-saver": "^2.0.1", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", + "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/parser": "^4.29.0", "codelyzer": "^6.0.0", "electron": "^8.0.1", + "eslint": "^7.32.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index bcbb183..9de15ae 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -4,7 +4,6 @@ import { PostsService } from 'app/posts.services'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-media-dialog.component'; import { TwitchChatComponent } from 'app/components/twitch-chat/twitch-chat.component';