mirror of
https://github.com/tedkulp/vidgrab
synced 2026-03-04 13:10:04 -05:00
Back to nextjs
This commit is contained in:
115
angular.json
115
angular.json
@@ -65,86 +65,38 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"client": {
|
"client": {
|
||||||
"projectType": "application",
|
|
||||||
"root": "apps/client",
|
"root": "apps/client",
|
||||||
"sourceRoot": "apps/client/src",
|
"sourceRoot": "apps/client",
|
||||||
"prefix": "vidgrab2",
|
"projectType": "application",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
"builder": "@nrwl/next:build",
|
||||||
"outputs": ["{options.outputPath}"],
|
"outputs": ["{options.outputPath}"],
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/apps/client",
|
"root": "apps/client",
|
||||||
"index": "apps/client/src/index.html",
|
"outputPath": "dist/apps/client"
|
||||||
"main": "apps/client/src/main.ts",
|
|
||||||
"polyfills": "apps/client/src/polyfills.ts",
|
|
||||||
"tsConfig": "apps/client/tsconfig.app.json",
|
|
||||||
"inlineStyleLanguage": "scss",
|
|
||||||
"assets": ["apps/client/src/favicon.ico", "apps/client/src/assets"],
|
|
||||||
"styles": ["apps/client/src/styles.scss"],
|
|
||||||
"scripts": []
|
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {}
|
||||||
"budgets": [
|
}
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "500kb",
|
|
||||||
"maximumError": "1mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "2kb",
|
|
||||||
"maximumError": "4kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "apps/client/src/environments/environment.ts",
|
|
||||||
"with": "apps/client/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputHashing": "all"
|
|
||||||
},
|
|
||||||
"development": {
|
|
||||||
"buildOptimizer": false,
|
|
||||||
"optimization": false,
|
|
||||||
"vendorChunk": true,
|
|
||||||
"extractLicenses": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"namedChunks": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultConfiguration": "production"
|
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@nrwl/next:server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "client:build",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "client:build:production"
|
"buildTarget": "client:build:production",
|
||||||
},
|
"dev": false
|
||||||
"development": {
|
|
||||||
"browserTarget": "client:build:development"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"defaultConfiguration": "development",
|
|
||||||
"options": {
|
|
||||||
"proxyConfig": "apps/client/proxy.conf.json"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"export": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "@nrwl/next:export",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "client:build"
|
"buildTarget": "client:build:production"
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"builder": "@nrwl/linter:eslint",
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": [
|
|
||||||
"apps/client/src/**/*.ts",
|
|
||||||
"apps/client/src/**/*.html"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
@@ -154,31 +106,11 @@
|
|||||||
"jestConfig": "apps/client/jest.config.js",
|
"jestConfig": "apps/client/jest.config.js",
|
||||||
"passWithNoTests": true
|
"passWithNoTests": true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"client-e2e": {
|
|
||||||
"root": "apps/client-e2e",
|
|
||||||
"sourceRoot": "apps/client-e2e/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"architect": {
|
|
||||||
"e2e": {
|
|
||||||
"builder": "@nrwl/cypress:cypress",
|
|
||||||
"options": {
|
|
||||||
"cypressConfig": "apps/client-e2e/cypress.json",
|
|
||||||
"tsConfig": "apps/client-e2e/tsconfig.e2e.json",
|
|
||||||
"devServerTarget": "client:serve:development"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "client:serve:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"builder": "@nrwl/linter:eslint",
|
"builder": "@nrwl/linter:eslint",
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": ["apps/client-e2e/**/*.{js,ts}"]
|
"lintFilePatterns": ["apps/client/**/*.{ts,tsx,js,jsx}"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,6 +144,17 @@
|
|||||||
},
|
},
|
||||||
"@nrwl/angular:component": {
|
"@nrwl/angular:component": {
|
||||||
"style": "scss"
|
"style": "scss"
|
||||||
|
},
|
||||||
|
"@nrwl/react": {
|
||||||
|
"application": {
|
||||||
|
"babel": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@nrwl/next": {
|
||||||
|
"application": {
|
||||||
|
"style": "css",
|
||||||
|
"linter": "eslint"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultProject": "client"
|
"defaultProject": "client"
|
||||||
|
|||||||
@@ -5,17 +5,19 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|||||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bull';
|
||||||
|
|
||||||
|
import configuration from '../config/configuration';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { WebModule } from '../web/web.module';
|
import { WebModule } from '../web/web.module';
|
||||||
import { YtdlModule } from '../ytdl/ytdl.module';
|
import { YtdlModule } from '../ytdl/ytdl.module';
|
||||||
|
import { JobGateway } from './job.gateway';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
ignoreEnvFile: true,
|
ignoreEnvFile: true,
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
// load: [configuration],
|
load: [configuration],
|
||||||
}),
|
}),
|
||||||
EventEmitterModule.forRoot({
|
EventEmitterModule.forRoot({
|
||||||
wildcard: true,
|
wildcard: true,
|
||||||
@@ -38,6 +40,6 @@ import { YtdlModule } from '../ytdl/ytdl.module';
|
|||||||
WebModule,
|
WebModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService, JobGateway],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
74
apps/api/src/app/job.gateway.ts
Normal file
74
apps/api/src/app/job.gateway.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import {
|
||||||
|
OnGatewayConnection,
|
||||||
|
OnGatewayDisconnect,
|
||||||
|
WebSocketGateway,
|
||||||
|
WebSocketServer,
|
||||||
|
} from '@nestjs/websockets';
|
||||||
|
import { pick } from 'lodash';
|
||||||
|
import { Server, Socket } from 'socket.io';
|
||||||
|
|
||||||
|
import { JobEvent } from '@vidgrab2/api-interfaces';
|
||||||
|
|
||||||
|
@WebSocketGateway()
|
||||||
|
export class JobGateway implements OnGatewayConnection, OnGatewayDisconnect {
|
||||||
|
private readonly logger = new Logger(JobGateway.name);
|
||||||
|
|
||||||
|
@WebSocketServer()
|
||||||
|
private server: Server | undefined;
|
||||||
|
private clients: Socket[] = [];
|
||||||
|
|
||||||
|
handleConnection(client: Socket) {
|
||||||
|
this.clients.push(client);
|
||||||
|
this.logger.verbose('handleConnection', client);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisconnect(client: Socket) {
|
||||||
|
for (let i = 0; i < this.clients.length; i++) {
|
||||||
|
if (this.clients[i] === client) {
|
||||||
|
this.clients.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose('handleDisconnect', client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private broadcast(event: string, message: any) {
|
||||||
|
const broadCastMessage = JSON.stringify(message);
|
||||||
|
for (const c of this.clients) {
|
||||||
|
c.send(event, broadCastMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent('job.added')
|
||||||
|
async sendAddedJob(payload: JobEvent) {
|
||||||
|
if (this.server && payload.job) {
|
||||||
|
const state = await payload.job.getState();
|
||||||
|
const progress = payload.job.progress();
|
||||||
|
|
||||||
|
payload.job = {
|
||||||
|
...pick(payload.job, ['id', 'name', 'data']),
|
||||||
|
progress: progress ? `${progress}%` : 'n/a',
|
||||||
|
state: state,
|
||||||
|
};
|
||||||
|
this.server.emit('job.added', payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnEvent('job.updated')
|
||||||
|
async sendJobUpdate(payload: JobEvent) {
|
||||||
|
if (this.server && payload.job) {
|
||||||
|
const state = await payload.job.getState();
|
||||||
|
const progress = payload.job.progress();
|
||||||
|
|
||||||
|
payload.job = {
|
||||||
|
...pick(payload.job, ['id', 'name', 'data']),
|
||||||
|
progress: progress ? `${progress}%` : 'n/a',
|
||||||
|
state: state,
|
||||||
|
};
|
||||||
|
this.server.emit('job.updated', payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
apps/api/src/config/configuration.ts
Normal file
7
apps/api/src/config/configuration.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default () => ({
|
||||||
|
redisHost: process.env.REDIS_HOST || 'localhost',
|
||||||
|
redisPort: process.env.REDIS_PORT
|
||||||
|
? parseInt(process.env.REDIS_PORT, 10)
|
||||||
|
: 6379,
|
||||||
|
fileDir: process.env.FILE_DIR || '/tmp',
|
||||||
|
});
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import { InjectQueue } from '@nestjs/bull';
|
import { InjectQueue } from '@nestjs/bull';
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
|
CacheInterceptor,
|
||||||
|
CacheTTL,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
HttpCode,
|
HttpCode,
|
||||||
Logger,
|
Logger,
|
||||||
Post,
|
Post,
|
||||||
Render,
|
|
||||||
Req,
|
Req,
|
||||||
|
UseInterceptors,
|
||||||
UsePipes,
|
UsePipes,
|
||||||
ValidationPipe,
|
ValidationPipe,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
@@ -28,8 +30,9 @@ export class WebController {
|
|||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get('/info')
|
||||||
@Render('Index')
|
// @Render('Index')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async root(@Req() request: any) {
|
async root(@Req() request: any) {
|
||||||
const fullUrl =
|
const fullUrl =
|
||||||
request.protocol + '://' + request.get('host') + request.originalUrl;
|
request.protocol + '://' + request.get('host') + request.originalUrl;
|
||||||
@@ -75,6 +78,8 @@ export class WebController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('/extractors')
|
@Get('/extractors')
|
||||||
|
@UseInterceptors(CacheInterceptor)
|
||||||
|
@CacheTTL(600)
|
||||||
async listExtractors() {
|
async listExtractors() {
|
||||||
const extractors = await this.ytdlService.listExtractors();
|
const extractors = await this.ytdlService.listExtractors();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BullModule } from '@nestjs/bull';
|
import { BullModule } from '@nestjs/bull';
|
||||||
import { Module } from '@nestjs/common';
|
import { CacheModule, Module } from '@nestjs/common';
|
||||||
import { YtdlModule } from '../ytdl/ytdl.module';
|
import { YtdlModule } from '../ytdl/ytdl.module';
|
||||||
|
|
||||||
import { WebController } from './web.controller';
|
import { WebController } from './web.controller';
|
||||||
@@ -9,6 +9,7 @@ import { WebController } from './web.controller';
|
|||||||
BullModule.registerQueue({
|
BullModule.registerQueue({
|
||||||
name: 'vidgrab',
|
name: 'vidgrab',
|
||||||
}),
|
}),
|
||||||
|
CacheModule.register(),
|
||||||
YtdlModule,
|
YtdlModule,
|
||||||
],
|
],
|
||||||
controllers: [WebController],
|
controllers: [WebController],
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { ConfigService } from '@nestjs/config';
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { Job } from 'bull';
|
import { Job } from 'bull';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import split from 'split2';
|
import * as split from 'split2';
|
||||||
import { QueueDto } from '@vidgrab2/api-interfaces';
|
import { QueueDto } from '@vidgrab2/api-interfaces';
|
||||||
import { raw } from 'youtube-dl-exec';
|
import { raw } from 'youtube-dl-exec';
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["src/plugins/index.js"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-undef": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"fileServerFolder": ".",
|
|
||||||
"fixturesFolder": "./src/fixtures",
|
|
||||||
"integrationFolder": "./src/integration",
|
|
||||||
"modifyObstructiveCode": false,
|
|
||||||
"pluginsFile": "./src/plugins/index",
|
|
||||||
"supportFile": "./src/support/index.ts",
|
|
||||||
"video": true,
|
|
||||||
"videosFolder": "../../dist/cypress/apps/client-e2e/videos",
|
|
||||||
"screenshotsFolder": "../../dist/cypress/apps/client-e2e/screenshots",
|
|
||||||
"chromeWebSecurity": false
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Using fixtures to represent data",
|
|
||||||
"email": "hello@cypress.io"
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { getGreeting } from '../support/app.po';
|
|
||||||
|
|
||||||
describe('client', () => {
|
|
||||||
beforeEach(() => cy.visit('/'));
|
|
||||||
|
|
||||||
it('should display welcome message', () => {
|
|
||||||
// Custom command example, see `../support/commands.ts` file
|
|
||||||
cy.login('my-email@something.com', 'myPassword');
|
|
||||||
|
|
||||||
// Function helper example, see `../support/app.po.ts` file
|
|
||||||
getGreeting().contains('Welcome to client!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example plugins/index.js can be used to load plugins
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off loading
|
|
||||||
// the plugins file with the 'pluginsFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/plugins-guide
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// This function is called when a project is opened or re-opened (e.g. due to
|
|
||||||
// the project's config changing)
|
|
||||||
|
|
||||||
const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
|
|
||||||
|
|
||||||
module.exports = (on, config) => {
|
|
||||||
// `on` is used to hook into various events Cypress emits
|
|
||||||
// `config` is the resolved Cypress config
|
|
||||||
|
|
||||||
// Preprocess Typescript file using Nx helper
|
|
||||||
on('file:preprocessor', preprocessTypescript(config));
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export const getGreeting = () => cy.get('h1');
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// This example commands.js shows you how to
|
|
||||||
// create various custom commands and overwrite
|
|
||||||
// existing commands.
|
|
||||||
//
|
|
||||||
// For more comprehensive examples of custom
|
|
||||||
// commands please read more here:
|
|
||||||
// https://on.cypress.io/custom-commands
|
|
||||||
// ***********************************************
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
declare namespace Cypress {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
interface Chainable<Subject> {
|
|
||||||
login(email: string, password: string): void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// -- This is a parent command --
|
|
||||||
Cypress.Commands.add('login', (email, password) => {
|
|
||||||
console.log('Custom command example: Login', email, password);
|
|
||||||
});
|
|
||||||
//
|
|
||||||
// -- This is a child command --
|
|
||||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a dual command --
|
|
||||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example support/index.js is processed and
|
|
||||||
// loaded automatically before your test files.
|
|
||||||
//
|
|
||||||
// This is a great place to put global configuration and
|
|
||||||
// behavior that modifies Cypress.
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off
|
|
||||||
// automatically serving support files with the
|
|
||||||
// 'supportFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
|
||||||
import './commands';
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": false,
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"allowJs": true,
|
|
||||||
"types": ["cypress", "node"],
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.js"],
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"strictInjectionParameters": true,
|
|
||||||
"strictInputAccessModifiers": true,
|
|
||||||
"strictTemplates": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"files": [],
|
|
||||||
"include": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.e2e.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
4
apps/client/.babelrc
Normal file
4
apps/client/.babelrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["@nrwl/next/babel"],
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
|
||||||
# For additional information regarding the format and rule options, please see:
|
|
||||||
# https://github.com/browserslist/browserslist#queries
|
|
||||||
|
|
||||||
# For the full list of supported browsers by the Angular framework, please see:
|
|
||||||
# https://angular.io/guide/browser-support
|
|
||||||
|
|
||||||
# You can see what browsers were selected by your queries by running:
|
|
||||||
# npx browserslist
|
|
||||||
|
|
||||||
last 1 Chrome version
|
|
||||||
last 1 Firefox version
|
|
||||||
last 2 Edge major versions
|
|
||||||
last 2 Safari major versions
|
|
||||||
last 2 iOS major versions
|
|
||||||
Firefox ESR
|
|
||||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
|
||||||
@@ -1,36 +1,26 @@
|
|||||||
{
|
{
|
||||||
"extends": ["../../.eslintrc.json"],
|
"extends": [
|
||||||
|
"plugin:@nrwl/nx/react-typescript",
|
||||||
|
"../../.eslintrc.json",
|
||||||
|
"next",
|
||||||
|
"next/core-web-vitals"
|
||||||
|
],
|
||||||
"ignorePatterns": ["!**/*"],
|
"ignorePatterns": ["!**/*"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
"extends": [
|
"rules": {}
|
||||||
"plugin:@nrwl/nx/angular",
|
|
||||||
"plugin:@angular-eslint/template/process-inline-templates"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@angular-eslint/directive-selector": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"type": "attribute",
|
|
||||||
"prefix": "vidgrab2",
|
|
||||||
"style": "camelCase"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@angular-eslint/component-selector": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"type": "element",
|
|
||||||
"prefix": "vidgrab2",
|
|
||||||
"style": "kebab-case"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.html"],
|
"files": ["*.ts", "*.tsx"],
|
||||||
"extends": ["plugin:@nrwl/nx/angular-template"],
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
"rules": {}
|
"rules": {}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"env": {
|
||||||
|
"jest": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
apps/client/components/jobs/jobs.tsx
Normal file
50
apps/client/components/jobs/jobs.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './jobs.module.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
jobs?: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container: React.FC<Props> = ({ jobs }) => {
|
||||||
|
return (
|
||||||
|
<table className="table is-striped is-hoverable is-fullwidth is-narrow">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>
|
||||||
|
<abbr title="Service">Svc</abbr>
|
||||||
|
</th>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>
|
||||||
|
<abbr title="Percent Downloaded">%</abbr>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{jobs &&
|
||||||
|
jobs.map((j) => (
|
||||||
|
<tr key={j.id}>
|
||||||
|
<td>{j.id}</td>
|
||||||
|
<td>{j.state}</td>
|
||||||
|
<td>{j.data.extractor}</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href={j.data.url}
|
||||||
|
title={j.data.title}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{j.data.title}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{j.progress}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Container;
|
||||||
6
apps/client/index.d.ts
vendored
Normal file
6
apps/client/index.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
declare module '*.svg' {
|
||||||
|
const content: any;
|
||||||
|
export const ReactComponent: any;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
@@ -1,20 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
displayName: 'client',
|
displayName: 'client',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
|
||||||
globals: {
|
|
||||||
'ts-jest': {
|
|
||||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
|
||||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
coverageDirectory: '../../coverage/apps/client',
|
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.(ts|js|html)$': 'jest-preset-angular',
|
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
||||||
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
},
|
},
|
||||||
snapshotSerializers: [
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
coverageDirectory: '../../coverage/apps/client',
|
||||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
|
||||||
'jest-preset-angular/build/serializers/html-comment',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|||||||
3
apps/client/next-env.d.ts
vendored
Normal file
3
apps/client/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/types/global" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
15
apps/client/next.config.js
Normal file
15
apps/client/next.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const withNx = require('@nrwl/next/plugins/with-nx');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
|
||||||
|
**/
|
||||||
|
const nextConfig = {
|
||||||
|
nx: {
|
||||||
|
// Set this to false if you do not want to use SVGR
|
||||||
|
// See: https://github.com/gregberge/svgr
|
||||||
|
svgr: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = withNx(nextConfig);
|
||||||
6
apps/client/pages/_app.jsx
Normal file
6
apps/client/pages/_app.jsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import '../styles/styles.css';
|
||||||
|
|
||||||
|
// This default export is required in a new `pages/_app.js` file.
|
||||||
|
export default function MyApp({ Component, pageProps }) {
|
||||||
|
return <Component {...pageProps} />;
|
||||||
|
}
|
||||||
48
apps/client/pages/_document.jsx
Normal file
48
apps/client/pages/_document.jsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import Document, { Head, Html, Main, NextScript } from 'next/document';
|
||||||
|
|
||||||
|
class MyDocument extends Document {
|
||||||
|
static async getInitialProps(ctx) {
|
||||||
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
|
return { ...initialProps };
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<section className="section has-background-light">
|
||||||
|
<div className="container">
|
||||||
|
<nav className="level">
|
||||||
|
<h1 className="title is-1 level-item has-text-centered">
|
||||||
|
Vidgrab
|
||||||
|
</h1>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Main />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer id="footer" className="footer">
|
||||||
|
<div className="content has-text-centered">
|
||||||
|
<p>
|
||||||
|
Code and “Design” by <a href="#">Ted Kulp</a>. Based
|
||||||
|
on youtube-dl.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyDocument;
|
||||||
41
apps/client/pages/extractors.tsx
Normal file
41
apps/client/pages/extractors.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { NextPage, NextPageContext } from 'next';
|
||||||
|
|
||||||
|
// The component's props type
|
||||||
|
type PageProps = {
|
||||||
|
extractors?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// extending the default next context type
|
||||||
|
type PageContext = NextPageContext & {
|
||||||
|
query: PageProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
// react component
|
||||||
|
const Page: NextPage<PageProps> = ({ extractors }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className="section">
|
||||||
|
<div className="container">
|
||||||
|
<div className="content">
|
||||||
|
<p>
|
||||||
|
Here is the list of currently supported services. This is
|
||||||
|
generated dynamically because youtube-dl adds new services all the
|
||||||
|
time.
|
||||||
|
</p>
|
||||||
|
<ul>{extractors && extractors.map((e) => <li key={e}>{e}</li>)}</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getServerSideProps({ query }: PageContext) {
|
||||||
|
const res = await fetch('http://localhost:3333/api/extractors');
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: await res?.json(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
94
apps/client/pages/getinfo.tsx
Normal file
94
apps/client/pages/getinfo.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { NextPage, NextPageContext, GetServerSideProps } from 'next';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import getRawBody from 'raw-body';
|
||||||
|
import { YtResponse } from 'youtube-dl-exec';
|
||||||
|
|
||||||
|
// The component's props type
|
||||||
|
type PageProps = {
|
||||||
|
title: string;
|
||||||
|
extractor: string;
|
||||||
|
description: string;
|
||||||
|
videoUrl: string;
|
||||||
|
thumbnails: string;
|
||||||
|
upload_date: string;
|
||||||
|
duration: number;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
formats: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// extending the default next context type
|
||||||
|
type PageContext = NextPageContext & {
|
||||||
|
query: PageProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
// react component
|
||||||
|
const Page: NextPage<PageProps> = (props) => {
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const onSubmit = (e: React.SyntheticEvent) => {
|
||||||
|
setSubmitted(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className="section">
|
||||||
|
<div className="container">
|
||||||
|
<form method="POST" action="/" onSubmit={onSubmit}>
|
||||||
|
<div className="field is-grouped">
|
||||||
|
<div className="control is-expanded">
|
||||||
|
<div className="select is-fullwidth">
|
||||||
|
<select name="format">
|
||||||
|
{props.formats &&
|
||||||
|
props.formats.map((f) => (
|
||||||
|
<option key={f.format_id} value={f.format_id}>
|
||||||
|
{f.format} ({f.ext})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="control">
|
||||||
|
<button
|
||||||
|
className={`button is-primary ${submitted?'is-loading':''}`}
|
||||||
|
id="form-submit"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Queue Download
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="url" value={props.videoUrl} />
|
||||||
|
<input type="hidden" name="title" value={props.title} />
|
||||||
|
<input type="hidden" name="extractor" value={props.extractor} />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<YtResponse> = async ({ req }) => {
|
||||||
|
let data: YtResponse;
|
||||||
|
|
||||||
|
if (req.method == "POST") {
|
||||||
|
const body = await getRawBody(req);
|
||||||
|
|
||||||
|
const res = await fetch('http://localhost:3333/api/getinfo', {
|
||||||
|
method: 'post',
|
||||||
|
body: body.toString('utf-8'),
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
data = await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
138
apps/client/pages/index.tsx
Normal file
138
apps/client/pages/index.tsx
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { NextPage, NextPageContext, GetServerSideProps } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import getRawBody from 'raw-body';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Client from 'socket.io-client';
|
||||||
|
|
||||||
|
import Jobs from '../components/jobs/jobs';
|
||||||
|
|
||||||
|
// The component's props type
|
||||||
|
type PageProps = {
|
||||||
|
bookmarklet?: string;
|
||||||
|
jobs?: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// extending the default next context type
|
||||||
|
type PageContext = NextPageContext & {
|
||||||
|
query: PageProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
// react component
|
||||||
|
const Page: NextPage<PageProps> = ({ bookmarklet, jobs }) => {
|
||||||
|
const [_socket, setSocket] = useState<any>();
|
||||||
|
const [currentJobs, setCurrentJobs] = useState<any>(jobs);
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const onSubmit = (e: React.SyntheticEvent) => {
|
||||||
|
setSubmitted(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const newSocket = Client();
|
||||||
|
setSocket(newSocket);
|
||||||
|
|
||||||
|
newSocket.on('job.added', (payload: any) => {
|
||||||
|
setCurrentJobs((current: any[]) => {
|
||||||
|
return [payload.job, ...current.slice(0, -1)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
newSocket.on('job.updated', (payload: any) => {
|
||||||
|
setCurrentJobs((current: { id: any }[]) => {
|
||||||
|
const foundIndex = current.findIndex(
|
||||||
|
(cj: { id: any }) => cj.id == payload.job.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (foundIndex > -1) {
|
||||||
|
current[foundIndex] = payload.job;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...current];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
newSocket.disconnect();
|
||||||
|
setSocket(undefined);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className="section">
|
||||||
|
<div className="container">
|
||||||
|
<form method="POST" action="/getinfo" onSubmit={onSubmit}>
|
||||||
|
<div className="field is-grouped">
|
||||||
|
<div className="control is-expanded">
|
||||||
|
<input
|
||||||
|
className="input is-medium"
|
||||||
|
name="url"
|
||||||
|
placeholder="URL to Download"
|
||||||
|
/>
|
||||||
|
<p className="help">
|
||||||
|
<Link href="/extractors">
|
||||||
|
* List of currently available services
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="control">
|
||||||
|
<button
|
||||||
|
className={`button is-primary is-medium ${submitted?'is-loading':''}`}
|
||||||
|
id="form-submit"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="container content has-text-centered">
|
||||||
|
<p>Drag this to your bookmark bar!</p>
|
||||||
|
<a
|
||||||
|
className="bookmarklet"
|
||||||
|
ref={(node) =>
|
||||||
|
node && bookmarklet && node.setAttribute('href', bookmarklet)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Vidgrab It!
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section className="section">
|
||||||
|
<div className="container">
|
||||||
|
<h2 className="subtitle is-4 has-text-centered">Recent Downloads</h2>
|
||||||
|
|
||||||
|
<Jobs jobs={currentJobs} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (req.method == "POST") {
|
||||||
|
const body = await getRawBody(req);
|
||||||
|
|
||||||
|
const res = await fetch('http://localhost:3333/api/queue', {
|
||||||
|
method: 'post',
|
||||||
|
body: body.toString('utf-8'),
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
data = await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch('http://localhost:3333/api/info');
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: await res.json(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -2,5 +2,10 @@
|
|||||||
"/api": {
|
"/api": {
|
||||||
"target": "http://localhost:3333",
|
"target": "http://localhost:3333",
|
||||||
"secure": false
|
"secure": false
|
||||||
|
},
|
||||||
|
"/socket.io": {
|
||||||
|
"target": "http://localhost:3333",
|
||||||
|
"secure": false,
|
||||||
|
"ws": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
apps/client/public/nx-logo-white.svg
Normal file
17
apps/client/public/nx-logo-white.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="262px" height="163px" viewBox="0 0 262 163" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="Styles-&-Quick-Wins" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Nx---Quick-Wins" transform="translate(-476.000000, -1284.000000)" fill-rule="nonzero">
|
||||||
|
<g id="Logos" transform="translate(-11.000000, 782.000000)">
|
||||||
|
<g id="Nx_Flat_White" transform="translate(487.000000, 502.000000)">
|
||||||
|
<polygon id="Path" fill="#FFFFFF" points="130.68 104.59 97.49 52.71 97.44 96.3 40.24 0 0 0 0 162.57 39.79 162.57 39.92 66.39 96.53 158.26"/>
|
||||||
|
<polygon id="Path" fill="#FFFFFF" points="97.5 41.79 137.24 41.79 137.33 41.33 137.33 0 97.54 0 97.49 41.33"/>
|
||||||
|
<path d="M198.66,86.86 C189.139872,86.6795216 180.538723,92.516445 177.19,101.43 C182.764789,93.0931021 193.379673,89.7432211 202.73,93.37 C207.05,95.13 212.73,97.97 217.23,96.45 C212.950306,90.4438814 206.034895,86.8725952 198.66,86.86 L198.66,86.86 Z" id="Path" fill="#96D8E9"/>
|
||||||
|
<path d="M243.75,106.42 C243.75,101.55 241.1,100.42 235.6,98.42 C231.52,97 226.89,95.4 223.52,91 C222.86,90.13 222.25,89.15 221.6,88.11 C220.14382,85.4164099 218.169266,83.037429 215.79,81.11 C212.58,78.75 208.37,77.6 202.91,77.6 C191.954261,77.6076705 182.084192,84.2206169 177.91,94.35 C183.186964,87.0278244 191.956716,83.0605026 200.940147,83.9314609 C209.923578,84.8024193 217.767888,90.3805017 221.54,98.58 C223.424615,101.689762 227.141337,103.174819 230.65,102.22 C236.02,101.07 235.65,106.15 243.76,107.87 L243.75,106.42 Z" id="Path" fill="#48C4E5"/>
|
||||||
|
<path d="M261.46,105.38 L261.46,105.27 C261.34,73.03 235.17,45.45 202.91,45.45 C183.207085,45.4363165 164.821777,55.3450614 154,71.81 L153.79,71.45 L137.23,45.45 L97.5,45.4499858 L135.25,104.57 L98.41,162.57 L137,162.57 L153.79,136.78 L170.88,162.57 L209.48,162.57 L174.48,107.49 C173.899005,106.416838 173.583536,105.220114 173.56,104 C173.557346,96.2203871 176.64661,88.7586448 182.147627,83.2576275 C187.648645,77.7566101 195.110387,74.6673462 202.89,74.67 C219.11,74.67 221.82,84.37 225.32,88.93 C232.23,97.93 246.03,93.99 246.03,105.73 L246.03,105.73 C246.071086,108.480945 247.576662,111.001004 249.979593,112.340896 C252.382524,113.680787 255.317747,113.636949 257.679593,112.225896 C260.041438,110.814842 261.471086,108.250945 261.43,105.5 L261.43,105.5 L261.43,105.38 L261.46,105.38 Z" id="Path" fill="#FFFFFF"/>
|
||||||
|
<path d="M261.5,113.68 C261.892278,116.421801 261.504116,119.218653 260.38,121.75 C258.18,126.84 254.51,125.14 254.51,125.14 C254.51,125.14 251.35,123.6 253.27,120.65 C255.4,117.36 259.61,117.74 261.5,113.68 Z" id="Path" fill="#FFFFFF"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
11
apps/client/public/star.svg
Normal file
11
apps/client/public/star.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg
|
||||||
|
className="material-icons"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 347 B |
11
apps/client/specs/index.spec.tsx
Normal file
11
apps/client/specs/index.spec.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
import Index from '../pages/index';
|
||||||
|
|
||||||
|
describe('Index', () => {
|
||||||
|
it('should render successfully', () => {
|
||||||
|
const { baseElement } = render(<Index />);
|
||||||
|
expect(baseElement).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<!-- <div style="text-align: center">
|
|
||||||
<h1>Welcome to client!</h1>
|
|
||||||
<img
|
|
||||||
width="450"
|
|
||||||
src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png"
|
|
||||||
alt="Nx - Smart, Extensible Build Framework"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>Message: {{ hello$ | async | json }}</div> -->
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Remove template code below
|
|
||||||
*/
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
font-family: sans-serif;
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 50px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter-left {
|
|
||||||
margin-left: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-span-2 {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background-color: #143055;
|
|
||||||
color: white;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 0 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
margin-left: 18px;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 40px 0 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resources {
|
|
||||||
text-align: center;
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 9px;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resource {
|
|
||||||
color: #0094ba;
|
|
||||||
height: 36px;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 3px 9px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resource:hover {
|
|
||||||
background-color: rgba(68, 138, 255, 0.04);
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 9px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: black;
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
details {
|
|
||||||
border-radius: 4px;
|
|
||||||
color: #333;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
padding: 3px 9px;
|
|
||||||
margin-bottom: 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
height: 36px;
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-container {
|
|
||||||
margin-top: 12px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-container a {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge {
|
|
||||||
color: #24292e;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 3px 10px;
|
|
||||||
border: 1px solid rgba(27, 31, 35, 0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
|
|
||||||
margin-left: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.github-star-badge:hover {
|
|
||||||
background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
|
|
||||||
border-color: rgba(27, 31, 35, 0.35);
|
|
||||||
background-position: -0.5em;
|
|
||||||
}
|
|
||||||
.github-star-badge .material-icons {
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { TestBed, async } from '@angular/core/testing';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [AppComponent],
|
|
||||||
imports: [HttpClientModule],
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create the app', () => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.debugElement.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Message } from '@vidgrab2/api-interfaces';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'vidgrab2-root',
|
|
||||||
templateUrl: './app.component.html',
|
|
||||||
styleUrls: ['./app.component.scss'],
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
hello$ = this.http.get<Message>('/api/hello');
|
|
||||||
constructor(private http: HttpClient) {}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { RouterModule, Routes } from '@angular/router'
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
import { ExtractorsComponent } from './extractors/extractors.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{ path: '', component: AppComponent },
|
|
||||||
{ path: 'extractors', component: ExtractorsComponent },
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [AppComponent, ExtractorsComponent],
|
|
||||||
imports: [BrowserModule, HttpClientModule, RouterModule.forRoot(routes)],
|
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent],
|
|
||||||
})
|
|
||||||
export class AppModule {}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
Here is the list of currently supported services. This is generated
|
|
||||||
dynamically because youtube-dl adds new services all the time.
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li *ngFor="let e of extractors$ | async">{{ e }}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/* eslint-disable @angular-eslint/no-empty-lifecycle-method */
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'vidgrab2-extractors',
|
|
||||||
templateUrl: './extractors.component.html',
|
|
||||||
styleUrls: ['./extractors.component.scss']
|
|
||||||
})
|
|
||||||
export class ExtractorsComponent implements OnInit {
|
|
||||||
|
|
||||||
extractors$ = this.http.get<{extractors: string[]}>('/api/extractors').pipe(map(e => e.extractors));
|
|
||||||
constructor(private http: HttpClient) {}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
// Does a thing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: true,
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// This file can be replaced during build by using the `fileReplacements` array.
|
|
||||||
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
|
||||||
// The list of file replacements can be found in `angular.json`.
|
|
||||||
|
|
||||||
export const environment = {
|
|
||||||
production: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For easier debugging in development mode, you can import the following file
|
|
||||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
|
||||||
*
|
|
||||||
* This import should be commented out in production mode because it will have a negative impact
|
|
||||||
* on performance if an error is thrown.
|
|
||||||
*/
|
|
||||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,35 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Client</title>
|
|
||||||
<base href="/" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"
|
|
||||||
/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="wrapper">
|
|
||||||
<section class="section has-background-light">
|
|
||||||
<div class="container">
|
|
||||||
<nav class="level">
|
|
||||||
<h1 class="title is-1 level-item has-text-centered">Vidgrab</h1>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<vidgrab2-root></vidgrab2-root>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer id="footer" class="footer">
|
|
||||||
<div class="content has-text-centered">
|
|
||||||
<p>
|
|
||||||
Code and "Design" by <a href="#">Ted Kulp</a>. Based on youtube-dl.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
|
||||||
import { environment } from './environments/environment';
|
|
||||||
|
|
||||||
if (environment.production) {
|
|
||||||
enableProdMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformBrowserDynamic()
|
|
||||||
.bootstrapModule(AppModule)
|
|
||||||
.catch((err) => console.error(err));
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
|
||||||
* You can add your own extra polyfills to this file.
|
|
||||||
*
|
|
||||||
* This file is divided into 2 sections:
|
|
||||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
|
||||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
|
||||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
|
||||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
|
||||||
*
|
|
||||||
* Learn more in https://angular.io/guide/browser-support
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* BROWSER POLYFILLS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IE11 requires the following for NgClass support on SVG elements
|
|
||||||
*/
|
|
||||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Web Animations `@angular/platform-browser/animations`
|
|
||||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
|
||||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
|
||||||
*/
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
|
||||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
|
||||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
|
||||||
* will put import in the top of bundle, so user need to create a separate file
|
|
||||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
|
||||||
* into that file, and then add the following code before importing zone.js.
|
|
||||||
* import './zone-flags';
|
|
||||||
*
|
|
||||||
* The flags allowed in zone-flags.ts are listed here.
|
|
||||||
*
|
|
||||||
* The following flags will work for all browsers.
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
|
||||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
|
||||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
|
||||||
*
|
|
||||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
|
||||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
|
||||||
*
|
|
||||||
* (window as any).__Zone_enable_cross_context_check = true;
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* Zone JS is required by default for Angular itself.
|
|
||||||
*/
|
|
||||||
import 'zone.js'; // Included with Angular CLI.
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* APPLICATION IMPORTS
|
|
||||||
*/
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
import 'jest-preset-angular/setup-jest';
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"types": []
|
|
||||||
},
|
|
||||||
"files": ["src/main.ts", "src/polyfills.ts"],
|
|
||||||
"include": ["src/**/*.d.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"include": ["**/*.ts"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"types": ["jest", "node"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,17 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"files": [],
|
|
||||||
"include": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.app.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.editor.json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"jsx": "preserve",
|
||||||
|
"allowJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"types": ["node", "jest"],
|
||||||
|
"strict": false,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"strict": true,
|
"noEmit": true,
|
||||||
"noImplicitReturns": true,
|
"resolveJsonModule": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"isolatedModules": true
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"],
|
||||||
"strictInjectionParameters": true,
|
"exclude": ["node_modules"]
|
||||||
"strictInputAccessModifiers": true,
|
|
||||||
"strictTemplates": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,14 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc",
|
"outDir": "../../dist/out-tsc",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"types": ["jest", "node"]
|
"types": ["jest", "node"],
|
||||||
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
"files": ["src/test-setup.ts"],
|
"include": [
|
||||||
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.tsx",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.spec.jsx",
|
||||||
|
"**/*.d.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
3
babel.config.json
Normal file
3
babel.config.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"babelrcRoots": ["*"]
|
||||||
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
|
|
||||||
* and faster execution of tasks.
|
|
||||||
*
|
|
||||||
* It does this by:
|
|
||||||
*
|
|
||||||
* - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
|
|
||||||
* - Symlinking the ng to nx command, so all commands run through the Nx CLI
|
|
||||||
* - Updating the package.json postinstall script to give you control over this script
|
|
||||||
*
|
|
||||||
* The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
|
|
||||||
* Every command you run should work the same when using the Nx CLI, except faster.
|
|
||||||
*
|
|
||||||
* Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
|
|
||||||
* will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
|
|
||||||
* The Nx CLI simply does some optimizations before invoking the Angular CLI.
|
|
||||||
*
|
|
||||||
* To opt out of this patch:
|
|
||||||
* - Replace occurrences of nx with ng in your package.json
|
|
||||||
* - Remove the script from your postinstall script in your package.json
|
|
||||||
* - Delete and reinstall your node_modules
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const os = require('os');
|
|
||||||
const cp = require('child_process');
|
|
||||||
const isWindows = os.platform() === 'win32';
|
|
||||||
let output;
|
|
||||||
try {
|
|
||||||
output = require('@nrwl/workspace').output;
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Angular CLI could not be decorated to enable computation caching. Please ensure @nrwl/workspace is installed.');
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
|
|
||||||
* invoke the Nx CLI and get the benefits of computation caching.
|
|
||||||
*/
|
|
||||||
function symlinkNgCLItoNxCLI() {
|
|
||||||
try {
|
|
||||||
const ngPath = './node_modules/.bin/ng';
|
|
||||||
const nxPath = './node_modules/.bin/nx';
|
|
||||||
if (isWindows) {
|
|
||||||
/**
|
|
||||||
* This is the most reliable way to create symlink-like behavior on Windows.
|
|
||||||
* Such that it works in all shells and works with npx.
|
|
||||||
*/
|
|
||||||
['', '.cmd', '.ps1'].forEach(ext => {
|
|
||||||
if (fs.existsSync(nxPath + ext)) fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If unix-based, symlink
|
|
||||||
cp.execSync(`ln -sf ./nx ${ngPath}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
output.error({ title: 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + e.message });
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
symlinkNgCLItoNxCLI();
|
|
||||||
require('@nrwl/cli/lib/decorate-cli').decorateCli();
|
|
||||||
output.log({ title: 'Angular CLI has been decorated to enable computation caching.' });
|
|
||||||
} catch(e) {
|
|
||||||
output.error({ title: 'Decoration of the Angular CLI did not complete successfully' });
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,8 @@ export interface QueueDto {
|
|||||||
extractor?: string;
|
extractor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type JobEvent = {
|
export interface JobEvent {
|
||||||
job?: unknown;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
job?: any;
|
||||||
err?: Error;
|
err?: Error;
|
||||||
};
|
};
|
||||||
|
|||||||
4
nx.json
4
nx.json
@@ -35,10 +35,6 @@
|
|||||||
},
|
},
|
||||||
"client": {
|
"client": {
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
|
||||||
"client-e2e": {
|
|
||||||
"tags": [],
|
|
||||||
"implicitDependencies": ["client"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19314
package-lock.json
generated
19314
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -4,16 +4,17 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "nx",
|
"ng": "nx",
|
||||||
"postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2015 browser module main",
|
|
||||||
"nx": "nx",
|
"nx": "nx",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"start:fe": "ng serve client",
|
"start:fe": "ng serve client",
|
||||||
|
"start:fe:prod": "sleep 10 && ng serve client --prod",
|
||||||
"start:be": "ng serve api",
|
"start:be": "ng serve api",
|
||||||
"build": "ng build",
|
"start:be:prod": "node dist/apps/api/main.js",
|
||||||
|
"build": "ng build api --prod && ng build client",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "nx workspace-lint && ng lint",
|
"lint": "nx workspace-lint && ng lint",
|
||||||
"e2e": "ng e2e",
|
"dev": "concurrently -p=\"{name}\" -n=\"Next,NestJS\" -c=\"green,blue\" \"npm run start:fe\" \"npm run start:be\"",
|
||||||
"dev": "concurrently -p=\"{name}\" -n=\"Angular,NestJS\" -c=\"green,blue\" \"npm run start:fe\" \"npm run start:be\"",
|
"prod": "concurrently -p=\"{name}\" -n=\"Next,NestJS\" -c=\"green,blue\" \"npm run start:fe:prod\" \"npm run start:be:prod\"",
|
||||||
"affected:apps": "nx affected:apps",
|
"affected:apps": "nx affected:apps",
|
||||||
"affected:libs": "nx affected:libs",
|
"affected:libs": "nx affected:libs",
|
||||||
"affected:build": "nx affected:build",
|
"affected:build": "nx affected:build",
|
||||||
@@ -32,14 +33,6 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^12.1.0",
|
|
||||||
"@angular/common": "^12.1.0",
|
|
||||||
"@angular/compiler": "^12.1.0",
|
|
||||||
"@angular/core": "^12.1.0",
|
|
||||||
"@angular/forms": "^12.1.0",
|
|
||||||
"@angular/platform-browser": "^12.1.0",
|
|
||||||
"@angular/platform-browser-dynamic": "^12.1.0",
|
|
||||||
"@angular/router": "^12.1.0",
|
|
||||||
"@nestjs/bull": "^0.4.0",
|
"@nestjs/bull": "^0.4.0",
|
||||||
"@nestjs/common": "^7.0.0",
|
"@nestjs/common": "^7.0.0",
|
||||||
"@nestjs/config": "^1.0.1",
|
"@nestjs/config": "^1.0.1",
|
||||||
@@ -49,12 +42,18 @@
|
|||||||
"@nestjs/platform-socket.io": "^7.6.18",
|
"@nestjs/platform-socket.io": "^7.6.18",
|
||||||
"@nestjs/serve-static": "^2.2.2",
|
"@nestjs/serve-static": "^2.2.2",
|
||||||
"@nestjs/websockets": "^7.6.18",
|
"@nestjs/websockets": "^7.6.18",
|
||||||
"@nrwl/angular": "12.6.3",
|
|
||||||
"bull": "^3.27.0",
|
"bull": "^3.27.0",
|
||||||
|
"cache-manager": "^3.4.4",
|
||||||
"class-transformer": "^0.4.0",
|
"class-transformer": "^0.4.0",
|
||||||
"class-validator": "^0.13.1",
|
"class-validator": "^0.13.1",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"document-register-element": "1.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"next": "11.0.1",
|
||||||
|
"react": "17.0.2",
|
||||||
|
"react-dom": "17.0.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"regenerator-runtime": "0.13.7",
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"split2": "^3.2.2",
|
"split2": "^3.2.2",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
@@ -62,34 +61,42 @@
|
|||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^12.1.0",
|
"@angular/cli": "^12.1.4",
|
||||||
"@angular-eslint/eslint-plugin": "~12.3.0",
|
"@babel/core": "7.12.13",
|
||||||
"@angular-eslint/eslint-plugin-template": "~12.3.0",
|
"@babel/preset-env": "7.12.13",
|
||||||
"@angular-eslint/template-parser": "~12.3.0",
|
"@babel/preset-react": "7.12.13",
|
||||||
"@angular/cli": "^12.0.0",
|
"@babel/preset-typescript": "7.12.13",
|
||||||
"@angular/compiler-cli": "^12.1.0",
|
|
||||||
"@angular/language-service": "^12.1.0",
|
|
||||||
"@nestjs/schematics": "^7.0.0",
|
"@nestjs/schematics": "^7.0.0",
|
||||||
"@nestjs/testing": "^7.0.0",
|
"@nestjs/testing": "^7.0.0",
|
||||||
"@nrwl/cli": "12.6.3",
|
"@nrwl/cli": "12.6.3",
|
||||||
"@nrwl/cypress": "12.6.3",
|
|
||||||
"@nrwl/eslint-plugin-nx": "12.6.3",
|
"@nrwl/eslint-plugin-nx": "12.6.3",
|
||||||
"@nrwl/jest": "12.6.3",
|
"@nrwl/jest": "12.6.3",
|
||||||
"@nrwl/linter": "12.6.3",
|
"@nrwl/linter": "12.6.3",
|
||||||
"@nrwl/nest": "^12.6.3",
|
"@nrwl/nest": "^12.6.3",
|
||||||
|
"@nrwl/next": "^12.6.3",
|
||||||
"@nrwl/node": "12.6.3",
|
"@nrwl/node": "12.6.3",
|
||||||
|
"@nrwl/react": "12.6.3",
|
||||||
"@nrwl/tao": "12.6.3",
|
"@nrwl/tao": "12.6.3",
|
||||||
|
"@nrwl/web": "12.6.3",
|
||||||
"@nrwl/workspace": "12.6.3",
|
"@nrwl/workspace": "12.6.3",
|
||||||
|
"@testing-library/react": "11.2.6",
|
||||||
|
"@types/cache-manager": "^3.4.2",
|
||||||
"@types/jest": "26.0.24",
|
"@types/jest": "26.0.24",
|
||||||
"@types/node": "14.14.33",
|
"@types/node": "14.14.33",
|
||||||
|
"@types/react": "17.0.3",
|
||||||
|
"@types/react-dom": "17.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "~4.28.3",
|
"@typescript-eslint/eslint-plugin": "~4.28.3",
|
||||||
"@typescript-eslint/parser": "~4.28.3",
|
"@typescript-eslint/parser": "~4.28.3",
|
||||||
|
"babel-jest": "27.0.6",
|
||||||
"concurrently": "^6.2.0",
|
"concurrently": "^6.2.0",
|
||||||
"cypress": "^7.3.0",
|
|
||||||
"dotenv": "~10.0.0",
|
"dotenv": "~10.0.0",
|
||||||
"eslint": "7.22.0",
|
"eslint": "7.22.0",
|
||||||
|
"eslint-config-next": "11.0.1",
|
||||||
"eslint-config-prettier": "8.1.0",
|
"eslint-config-prettier": "8.1.0",
|
||||||
"eslint-plugin-cypress": "^2.10.3",
|
"eslint-plugin-import": "2.22.1",
|
||||||
|
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||||
|
"eslint-plugin-react": "7.23.1",
|
||||||
|
"eslint-plugin-react-hooks": "4.2.0",
|
||||||
"jest": "27.0.3",
|
"jest": "27.0.3",
|
||||||
"jest-preset-angular": "9.0.4",
|
"jest-preset-angular": "9.0.4",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user