mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 20:10:03 +03:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
505b145bb3 | ||
|
|
ba5592015d | ||
|
|
10c90a01f2 | ||
|
|
abaa799628 | ||
|
|
9e148d1464 | ||
|
|
9fa646132d | ||
|
|
f98ba00551 | ||
|
|
bc1e0ea542 | ||
|
|
9ebb684d5c | ||
|
|
91713f1140 | ||
|
|
377676cda4 | ||
|
|
8e445bb80d | ||
|
|
f0bde1efc4 | ||
|
|
4be6f341da | ||
|
|
dd62f1a7b3 | ||
|
|
1cdd4d0e15 | ||
|
|
e4e1e67855 | ||
|
|
867d2394de | ||
|
|
eb54c2fda5 | ||
|
|
deacf5a49a |
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"project": {
|
||||
"name": "youtube-dl-material"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "src",
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico",
|
||||
"backend/audio",
|
||||
"backend/video",
|
||||
"backend",
|
||||
{ "glob": "default.json", "input": "./", "output": "../backend/config/", "allowOutsideOutDir": true }
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"polyfills": "polyfills.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.app.json",
|
||||
"testTsconfig": "tsconfig.spec.json",
|
||||
"prefix": "app",
|
||||
"styles": [
|
||||
"styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"environmentSource": "environments/environment.ts",
|
||||
"environments": {
|
||||
"dev": "environments/environment.ts",
|
||||
"prod": "environments/environment.prod.ts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": [
|
||||
{
|
||||
"project": "src/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"project": "src/tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"project": "e2e/tsconfig.e2e.json"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"styleExt": "css",
|
||||
"component": {}
|
||||
}
|
||||
}
|
||||
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2018 Isaac Grynsztein
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
68
README.md
68
README.md
@@ -1,28 +1,66 @@
|
||||
# YoutubeDLMaterial
|
||||
# YoutubeDL-Material
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.1.2.
|
||||
YoutubeDL-Material is a material design frontend for [youtube-dl](https://rg3.github.io/youtube-dl/). It's coded using [Angular 5](https://angular.io/) for the frontend, and [Nodejs](https://nodejs.org/) on the backend.
|
||||
|
||||
## Development server
|
||||
## Getting Started
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
Check out the prerequisites, and go to the installation section. Easy as pie!
|
||||
|
||||
## Code scaffolding
|
||||
Here's an image of what it'll look like once you're done:
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|module`.
|
||||

|
||||
|
||||
## Build
|
||||
With optional file management enabled (default):
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||

|
||||
|
||||
## Running unit tests
|
||||
### Prerequisites
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
You need to have a functioning web server for this to work. Also make sure you have these dependencies installed on your system: ffmpeg, nodejs, python. If you don't, run this command:
|
||||
|
||||
## Running end-to-end tests
|
||||
```
|
||||
sudo apt-get install ffmpeg nodejs python
|
||||
```
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
Before running the tests make sure you are serving the app via `ng serve`.
|
||||
### Installing
|
||||
|
||||
## Further help
|
||||
First, download the [latest release](https://github.com/Tzahi12345/YoutubeDL-Material/releases/latest)!
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
Drag all the files in `youtubedl-material` to a location accessible to a web server. It works best if it's the root (usually right inside `public_html`. Once that's done, navigate to `backend` and edit the `default.json` file. If you're using SSL encryption, look at the `encrypted.json` file for a template.
|
||||
|
||||
Port forward `17442` if you're going to access YoutubeDL-Material from out of your network. This is an important step. Make sure the configuration reflects this appropriately.
|
||||
|
||||
Once the configuration is done, type `sudo nodejs app.js`. This will run the backend server. On your browser, navigate to your installation folder. Try putting in a youtube link to see if it works. If it does, viola! YoutubeDL-Material is now up and running.
|
||||
|
||||
If you experience problems, know that it's usually caused by a configuration problem. The first thing you should do is check the console. To get there, right click anywhere on the page and click "Inspect element." Then on the menu that pops up, click console. Look at the error there, and try to investigate.
|
||||
|
||||
## Deployment
|
||||
|
||||
If you'd like to install YoutubeDL-Material, go to the Installation section. If you want to build it yourself and/or develop the repository, then this section is for you.
|
||||
|
||||
To deploy, simply clone the repository, and go into the `youtubedl-material` directory. Type `npm install` and all the dependencies will install. Then type `cd backend` and again type `npm install` to install the dependencies for the backend.
|
||||
|
||||
Once you do that, you're almost up and running. All you need to do is edit the configuration in `youtubedl-material/backend/config`, go back into the `youtubedl-material` directory, and type `ng build --prod`. This will build the app, and put the output files in the `youtubedl-material/dist` folder. Drag those files into a web server, and drag the `backend` directory into the same folder. This folder should have `index.html` in it as well. If it does **not**, you're in the wrong directory.
|
||||
|
||||
The frontend is now complete. The backend is much easier. Just go into the `youtubedl-material/backend` folder, and type `sudo nodejs app.js`.
|
||||
|
||||
Finally, port forward the port `17442` and point it to the server's IP address. Make sure the port is also allowed through the firewall.
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to submit a pull request! I have no guidelines as of yet, so no need to worry about that.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Isaac Grynsztein** (me!) - *Initial work*
|
||||
|
||||
See also the list of [contributors](https://github.com/your/project/contributors) who participated in this project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
* youtube-dl
|
||||
* [AllTube](https://github.com/Rudloff/alltube) (for the inspiration)
|
||||
|
||||
134
angular.json
Normal file
134
angular.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"youtube-dl-material": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/favicon.ico",
|
||||
"src/backend/audio",
|
||||
"src/backend/video",
|
||||
"src/backend"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "youtube-dl-material:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "youtube-dl-material:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "youtube-dl-material:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"karmaConfig": "./karma.conf.js",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"scripts": [],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"assets": [
|
||||
"src/assets",
|
||||
"src/favicon.ico",
|
||||
"src/backend/audio",
|
||||
"src/backend/video",
|
||||
"src/backend"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"youtube-dl-material-e2e": {
|
||||
"root": "e2e",
|
||||
"sourceRoot": "e2e",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "./protractor.conf.js",
|
||||
"devServerTarget": "youtube-dl-material:serve"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"e2e/tsconfig.e2e.json"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "youtube-dl-material",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"styleext": "css"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
}
|
||||
}
|
||||
}
|
||||
290
backend/app.js
290
backend/app.js
@@ -6,9 +6,10 @@ var config = require('config');
|
||||
var https = require('https');
|
||||
var express = require("express");
|
||||
var bodyParser = require("body-parser");
|
||||
var pem = require('https-pem');
|
||||
var app = express();
|
||||
|
||||
var URL = require('url').URL;
|
||||
|
||||
var frontendUrl = config.get("YoutubeDLMaterial.Host.frontendurl");
|
||||
var backendUrl = config.get("YoutubeDLMaterial.Host.backendurl")
|
||||
var backendPort = 17442;
|
||||
@@ -37,8 +38,10 @@ if (usingEncryption)
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
var url_domain = new URL(frontendUrl);
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
res.header("Access-Control-Allow-Origin", frontendUrl);
|
||||
res.header("Access-Control-Allow-Origin", url_domain.origin);
|
||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||
next();
|
||||
});
|
||||
@@ -48,38 +51,31 @@ app.get('/using-encryption', function(req, res) {
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
// objects
|
||||
|
||||
app.post('/tomp3', function(req, res) {
|
||||
var url = req.body.url;
|
||||
var date = Date.now();
|
||||
var path = audioPath;
|
||||
var audiopath = Date.now();
|
||||
youtubedl.exec(url, ['-o', path + audiopath + ".mp3", '-x', '--audio-format', 'mp3', '--write-info-json'], {}, function(err, output) {
|
||||
if (err) {
|
||||
audiopath = "-1";
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
function File(id, title, thumbnailURL, isAudio, duration) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.thumbnailURL = thumbnailURL;
|
||||
this.isAudio = isAudio;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
// write file info
|
||||
// actual functions
|
||||
|
||||
youtubedl.getInfo(url, function(err, info) {
|
||||
if (err) throw err;
|
||||
|
||||
var size = info.size;
|
||||
fs.writeFile("data/"+audiopath, size, function(err) {
|
||||
if(err) {
|
||||
return console.log(err);
|
||||
}
|
||||
|
||||
console.log("The file was saved!");
|
||||
});
|
||||
});
|
||||
var completeString = "done";
|
||||
var audiopathEncoded = encodeURIComponent(audiopath);
|
||||
res.send(audiopathEncoded);
|
||||
res.end("yes");
|
||||
});
|
||||
function getThumbnailMp3(name)
|
||||
{
|
||||
var obj = getJSONMp3(name);
|
||||
var thumbnailLink = obj.thumbnail;
|
||||
return thumbnailLink;
|
||||
}
|
||||
|
||||
function getThumbnailMp4(name)
|
||||
{
|
||||
var obj = getJSONMp4(name);
|
||||
var thumbnailLink = obj.thumbnail;
|
||||
return thumbnailLink;
|
||||
}
|
||||
|
||||
function getFileSizeMp3(name)
|
||||
{
|
||||
@@ -93,19 +89,6 @@ function getFileSizeMp3(name)
|
||||
return obj.filesize;
|
||||
}
|
||||
|
||||
function getAmountDownloadedMp3(name)
|
||||
{
|
||||
var partPath = audioPath+name+".mp3.part";
|
||||
if (fs.existsSync(partPath))
|
||||
{
|
||||
const stats = fs.statSync(partPath);
|
||||
const fileSizeInBytes = stats.size;
|
||||
return fileSizeInBytes;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getFileSizeMp4(name)
|
||||
{
|
||||
var jsonPath = videoPath+name+".info.json";
|
||||
@@ -126,6 +109,43 @@ function getFileSizeMp4(name)
|
||||
return filesize;
|
||||
}
|
||||
|
||||
function getJSONMp3(name)
|
||||
{
|
||||
var jsonPath = audioPath+name+".mp3.info.json";
|
||||
if (fs.existsSync(jsonPath))
|
||||
var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||
else
|
||||
var obj = 0;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function getJSONMp4(name)
|
||||
{
|
||||
var jsonPath = videoPath+name+".info.json";
|
||||
if (fs.existsSync(jsonPath))
|
||||
{
|
||||
var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
||||
return obj;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
function getAmountDownloadedMp3(name)
|
||||
{
|
||||
var partPath = audioPath+name+".mp3.part";
|
||||
if (fs.existsSync(partPath))
|
||||
{
|
||||
const stats = fs.statSync(partPath);
|
||||
const fileSizeInBytes = stats.size;
|
||||
return fileSizeInBytes;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getAmountDownloadedMp4(name)
|
||||
{
|
||||
var format = getVideoFormatID(name);
|
||||
@@ -151,12 +171,48 @@ function getVideoFormatID(name)
|
||||
}
|
||||
}
|
||||
|
||||
app.post('/tomp3', function(req, res) {
|
||||
var url = req.body.url;
|
||||
var date = Date.now();
|
||||
var path = audioPath;
|
||||
var audiopath = Date.now();
|
||||
youtubedl.exec(url, ['--external-downloader', 'aria2c', '-o', path + audiopath + ".mp3", '-x', '--audio-format', 'mp3', '--write-info-json'], {}, function(err, output) {
|
||||
if (err) {
|
||||
audiopath = "-1";
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
// write file info
|
||||
|
||||
/*
|
||||
youtubedl.getInfo(url, function(err, info) {
|
||||
if (err) throw err;
|
||||
|
||||
var size = info.size;
|
||||
fs.writeFile("data/"+audiopath, size, function(err) {
|
||||
if(err) {
|
||||
return console.log(err);
|
||||
}
|
||||
|
||||
console.log("The file was saved!");
|
||||
});
|
||||
});
|
||||
*/
|
||||
var completeString = "done";
|
||||
var audiopathEncoded = encodeURIComponent(audiopath);
|
||||
res.send({
|
||||
audiopathEncoded: audiopathEncoded
|
||||
});
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
app.post('/tomp4', function(req, res) {
|
||||
var url = req.body.url;
|
||||
var date = Date.now();
|
||||
var path = videoPath;
|
||||
var videopath = Date.now();
|
||||
youtubedl.exec(url, ['-o', path + videopath + ".mp4", '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4', '--write-info-json'], {}, function(err, output) {
|
||||
youtubedl.exec(url, ['--external-downloader', 'aria2c', '-o', path + videopath + ".mp4", '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4', '--write-info-json'], {}, function(err, output) {
|
||||
if (err) {
|
||||
videopath = "-1";
|
||||
throw err;
|
||||
@@ -164,11 +220,14 @@ app.post('/tomp4', function(req, res) {
|
||||
});
|
||||
var completeString = "done";
|
||||
var videopathEncoded = encodeURIComponent(videopath);
|
||||
res.send(videopathEncoded);
|
||||
res.send({
|
||||
videopathEncoded: videopathEncoded
|
||||
});
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
app.post('/mp3fileexists', function(req, res) {
|
||||
// gets the status of the mp3 file that's being downloaded
|
||||
app.post('/fileStatusMp3', function(req, res) {
|
||||
var name = req.body.name + "";
|
||||
var exists = "";
|
||||
var fullpath = audioPath + name + ".mp3";
|
||||
@@ -189,15 +248,14 @@ app.post('/mp3fileexists', function(req, res) {
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
app.post('/mp4fileexists', function(req, res) {
|
||||
// gets the status of the mp4 file that's being downloaded
|
||||
app.post('/fileStatusMp4', function(req, res) {
|
||||
var name = req.body.name;
|
||||
var exists = "";
|
||||
var fullpath = videoPath + name + ".mp4";
|
||||
if (fs.existsSync(fullpath)) {
|
||||
exists = [basePath + videoPath + name, getFileSizeMp4(name)];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var percent = 0;
|
||||
var size = getFileSizeMp4(name);
|
||||
var downloaded = getAmountDownloadedMp4(name);
|
||||
@@ -210,6 +268,136 @@ app.post('/mp4fileexists', function(req, res) {
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
// gets all download mp3s
|
||||
app.post('/getMp3s', function(req, res) {
|
||||
var mp3s = [];
|
||||
var fullpath = audioPath;
|
||||
var files = fs.readdirSync(audioPath);
|
||||
|
||||
for (var i in files)
|
||||
{
|
||||
var nameLength = path.basename(files[i]).length;
|
||||
var ext = path.basename(files[i]).substring(nameLength-4, nameLength);
|
||||
if (ext == ".mp3")
|
||||
{
|
||||
var jsonobj = getJSONMp3(path.basename(files[i]).substring(0, path.basename(files[i]).length-4));
|
||||
if (!jsonobj) continue;
|
||||
var id = path.basename(files[i]).substring(0, path.basename(files[i]).length-4);
|
||||
var title = jsonobj.title;
|
||||
|
||||
if (title.length > 14) // edits title if it's too long
|
||||
{
|
||||
title = title.substring(0,12) + "...";
|
||||
}
|
||||
|
||||
var thumbnail = jsonobj.thumbnail;
|
||||
var duration = jsonobj.duration;
|
||||
var isaudio = true;
|
||||
var file = new File(id, title, thumbnail, isaudio, duration);
|
||||
mp3s.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
res.send({
|
||||
mp3s: mp3s
|
||||
});
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
// gets all download mp4s
|
||||
app.post('/getMp4s', function(req, res) {
|
||||
var mp4s = [];
|
||||
var fullpath = videoPath;
|
||||
var files = fs.readdirSync(videoPath);
|
||||
|
||||
for (var i in files)
|
||||
{
|
||||
var nameLength = path.basename(files[i]).length;
|
||||
var ext = path.basename(files[i]).substring(nameLength-4, nameLength);
|
||||
if (ext == ".mp4")
|
||||
{
|
||||
var jsonobj = getJSONMp4(path.basename(files[i]).substring(0, path.basename(files[i]).length-4));
|
||||
if (!jsonobj) continue;
|
||||
var id = path.basename(files[i]).substring(0, path.basename(files[i]).length-4);
|
||||
var title = jsonobj.title;
|
||||
|
||||
if (title.length > 14) // edits title if it's too long
|
||||
{
|
||||
title = title.substring(0,12) + "...";
|
||||
}
|
||||
|
||||
var thumbnail = jsonobj.thumbnail;
|
||||
var duration = jsonobj.duration;
|
||||
var isaudio = false;
|
||||
var file = new File(id, title, thumbnail, isaudio, duration);
|
||||
mp4s.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
res.send({
|
||||
mp4s: mp4s
|
||||
});
|
||||
res.end("yes");
|
||||
});
|
||||
|
||||
// deletes mp3 file
|
||||
app.post('/deleteMp3', function(req, res) {
|
||||
var name = req.body.name;
|
||||
var fullpath = audioPath + name + ".mp3";
|
||||
var wasDeleted = false;
|
||||
if (fs.existsSync(fullpath))
|
||||
{
|
||||
fs.unlink(fullpath, call => {
|
||||
|
||||
});
|
||||
wasDeleted = true;
|
||||
res.send(wasDeleted);
|
||||
res.end("yes");
|
||||
}
|
||||
else
|
||||
{
|
||||
wasDeleted = false;
|
||||
res.send(wasDeleted);
|
||||
res.end("yes");
|
||||
}
|
||||
});
|
||||
|
||||
// deletes mp4 file
|
||||
app.post('/deleteMp4', function(req, res) {
|
||||
var name = req.body.name;
|
||||
var fullpath = videoPath + name + ".mp4";
|
||||
var wasDeleted = false;
|
||||
if (fs.existsSync(fullpath))
|
||||
{
|
||||
fs.unlink(fullpath, call => {
|
||||
// console.log(call);
|
||||
});
|
||||
wasDeleted = true;
|
||||
res.send(wasDeleted);
|
||||
res.end("yes");
|
||||
}
|
||||
else
|
||||
{
|
||||
wasDeleted = false;
|
||||
res.send(wasDeleted);
|
||||
res.end("yes");
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/downloadFile', function(req, res) {
|
||||
let fileName = req.body.fileName;
|
||||
let type = req.body.type;
|
||||
let file = null;
|
||||
if (type === 'audio') {
|
||||
file = __dirname + '/' + 'audio/' + fileName + '.mp3';
|
||||
} else if (type === 'video') {
|
||||
file = __dirname + '/' + 'video/' + fileName + '.mp4';
|
||||
}
|
||||
|
||||
res.sendFile(file);
|
||||
});
|
||||
|
||||
|
||||
app.get('/video/:id', function(req , res){
|
||||
var head;
|
||||
const path = "video/" + req.params.id + ".mp4";
|
||||
|
||||
BIN
backend/aria2c.exe
Normal file
BIN
backend/aria2c.exe
Normal file
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"YoutubeDL-Material": {
|
||||
"Host": {
|
||||
"frontend-url": "http://localhost:4200",
|
||||
"backend-url": "youtubedl.grynsztein.com",
|
||||
"backend-port": "8088"
|
||||
},
|
||||
"Encryption": {
|
||||
"use-encryption": false,
|
||||
"cert-file-path": "fullchain.pem",
|
||||
"key-file-path": "privkey.pem"
|
||||
},
|
||||
"Downloader": {
|
||||
"path-base": "http://localhost:8088/",
|
||||
"path-audio": "audio/",
|
||||
"path-video": "video/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"YoutubeDLMaterial": {
|
||||
"Host": {
|
||||
"frontendurl": "http://localhost:4200",
|
||||
"backendurl": "https://youtubedl.grynsztein.com/"
|
||||
"frontendurl": "http://example.com",
|
||||
"backendurl": "http://example.com:17442/"
|
||||
},
|
||||
"Encryption": {
|
||||
"use-encryption": false,
|
||||
"cert-file-path": "cert.pem",
|
||||
"key-file-path": "privkey.pem"
|
||||
"cert-file-path": "/etc/letsencrypt/live/example.com/fullchain.pem",
|
||||
"key-file-path": "/etc/letsencrypt/live/example.com/privkey.pem"
|
||||
},
|
||||
"Downloader": {
|
||||
"path-base": "http://localhost:8088/",
|
||||
"path-base": "http://example.com:17442/",
|
||||
"path-audio": "audio/",
|
||||
"path-video": "video/"
|
||||
},
|
||||
"Extra": {
|
||||
"title_top": "Youtube Downloader"
|
||||
"title_top": "Youtube Downloader",
|
||||
"download_only_mode": false,
|
||||
"file_manager_enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
backend/config/encrypted.json
Normal file
22
backend/config/encrypted.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"YoutubeDLMaterial": {
|
||||
"Host": {
|
||||
"frontendurl": "https://example.com",
|
||||
"backendurl": "https://example.com:17442/"
|
||||
},
|
||||
"Encryption": {
|
||||
"use-encryption": true,
|
||||
"cert-file-path": "/etc/letsencrypt/live/example.com/fullchain.pem",
|
||||
"key-file-path": "/etc/letsencrypt/live/example.com/privkey.pem"
|
||||
},
|
||||
"Downloader": {
|
||||
"path-base": "https://example.com:17442/",
|
||||
"path-audio": "audio/",
|
||||
"path-video": "video/"
|
||||
},
|
||||
"Extra": {
|
||||
"title_top": "Youtube Downloader",
|
||||
"file_manager_enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/Tzahi12345/hda-backend#readme",
|
||||
"dependencies": {
|
||||
"async": "^3.1.0",
|
||||
"config": "^3.2.3",
|
||||
"exe": "^1.0.2",
|
||||
"youtube-dl": "1.11.1"
|
||||
"express": "^4.17.1",
|
||||
"youtube-dl": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
12
browserslist
Normal file
12
browserslist
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@@ -4,24 +4,22 @@
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular/cli'],
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular/cli/plugins/karma')
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
environment: 'dev'
|
||||
},
|
||||
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
|
||||
44
package.json
44
package.json
@@ -12,40 +12,46 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^5.2.0",
|
||||
"@angular/cdk": "^5.0.4",
|
||||
"@angular/common": "^5.2.0",
|
||||
"@angular/compiler": "^5.2.0",
|
||||
"@angular/core": "^5.2.0",
|
||||
"@angular/forms": "^5.0.0",
|
||||
"@angular/http": "^5.0.0",
|
||||
"@angular/material": "^5.0.4",
|
||||
"@angular/platform-browser": "^5.0.0",
|
||||
"@angular/platform-browser-dynamic": "^5.0.0",
|
||||
"@angular/router": "^5.0.0",
|
||||
"@angular-devkit/core": "^8.3.12",
|
||||
"@angular/animations": "^8.2.11",
|
||||
"@angular/cdk": "^8.2.3",
|
||||
"@angular/common": "^8.2.11",
|
||||
"@angular/compiler": "^8.2.11",
|
||||
"@angular/core": "^8.2.11",
|
||||
"@angular/forms": "^8.2.11",
|
||||
"@angular/http": "^7.2.15",
|
||||
"@angular/material": "^8.2.3",
|
||||
"@angular/platform-browser": "^8.2.11",
|
||||
"@angular/platform-browser-dynamic": "^8.2.11",
|
||||
"@angular/router": "^8.2.11",
|
||||
"core-js": "^2.4.1",
|
||||
"file-saver": "^2.0.2",
|
||||
"ng4-configure": "^0.1.7",
|
||||
"rxjs": "^5.5.3",
|
||||
"zone.js": "^0.8.4"
|
||||
"rxjs": "^6.5.3",
|
||||
"rxjs-compat": "^6.0.0-rc.0",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.5.5",
|
||||
"@angular/compiler-cli": "^5.0.0",
|
||||
"@angular/language-service": "^4.0.0",
|
||||
"@angular-devkit/build-angular": "^0.803.24",
|
||||
"@angular/cli": "^8.3.12",
|
||||
"@angular/compiler-cli": "^8.2.11",
|
||||
"@angular/language-service": "^8.2.11",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/jasmine": "2.5.45",
|
||||
"@types/node": "~6.0.60",
|
||||
"codelyzer": "~3.0.1",
|
||||
"codelyzer": "^5.0.1",
|
||||
"jasmine-core": "~2.6.2",
|
||||
"jasmine-spec-reporter": "~4.1.0",
|
||||
"karma": "~1.7.0",
|
||||
"karma-chrome-launcher": "~2.1.1",
|
||||
"karma-cli": "~1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"protractor": "~5.1.2",
|
||||
"ts-node": "~3.0.4",
|
||||
"tslint": "~5.3.2",
|
||||
"typescript": "~2.5.3"
|
||||
"typescript": "~3.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ mat-toolbar.top {
|
||||
}*/
|
||||
|
||||
.big {
|
||||
width: 60%;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -38,4 +38,19 @@ mat-toolbar.top {
|
||||
|
||||
mat-form-field.mat-form-field {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
margin-left: -28px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.make-room-for-spinner {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.equal-sizes {
|
||||
padding-right: 20px;
|
||||
}
|
||||
@@ -1,45 +1,105 @@
|
||||
<mat-toolbar color="primary" class="top">
|
||||
<table width="100%" height="100%">
|
||||
<td class="topbar" style="text-align: left; left:0px; font-size: 15px">
|
||||
</td>
|
||||
<td class="topbar" style="text-align: center">
|
||||
<div style="margin-top: 14px">{{topBarTitle}}</div>
|
||||
</td>
|
||||
<td class="topbar" style="text-align: right">
|
||||
|
||||
</td>
|
||||
</table>
|
||||
<table width="100%" height="100%">
|
||||
<td class="topbar" style="text-align: left; left:0px; font-size: 15px">
|
||||
</td>
|
||||
<td class="topbar" style="text-align: center">
|
||||
<div style="margin-top: 14px">{{topBarTitle}}</div>
|
||||
</td>
|
||||
<td class="topbar" style="text-align: right">
|
||||
|
||||
</td>
|
||||
</table>
|
||||
</mat-toolbar>
|
||||
|
||||
<br/>
|
||||
|
||||
<mat-card id="card" class="big demo-basic">
|
||||
<mat-card-title>
|
||||
<mat-toolbar color="primary">Youtube Downloader</mat-toolbar>
|
||||
</mat-card-title>
|
||||
<div style="width: 100%; height: 100%; padding-left: 24px; padding-right: 24px; position: relative">
|
||||
<div class="big demo-basic">
|
||||
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;">
|
||||
<mat-card-title>
|
||||
Youtube Downloader
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form class="example-form">
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput [(ngModel)]="url" placeholder="URL" type="url" name="url" [formControl]="urlForm" required>
|
||||
<mat-error *ngIf="urlError || urlForm.invalid">Please enter a valid URL!</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<br/>
|
||||
<mat-checkbox [(ngModel)]="audioOnly" style="float: left; margin-top: -12px">Only Audio</mat-checkbox>
|
||||
<button style="float: right; margin-top: -16px" (click)="downloadClicked()" [disabled]="downloadingfile" type="submit" mat-raised-button color="primary">Download</button>
|
||||
<div style="position: relative;">
|
||||
<form class="example-form">
|
||||
<mat-form-field class="example-full-width">
|
||||
<input matInput [(ngModel)]="url" placeholder="URL" type="url" name="url" [formControl]="urlForm" required>
|
||||
<mat-error *ngIf="urlError || urlForm.invalid">Please enter a valid URL!</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<br/>
|
||||
<mat-checkbox [(ngModel)]="audioOnly" style="float: left; margin-top: -12px">Only Audio</mat-checkbox>
|
||||
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
</mat-card>
|
||||
<mat-card-actions>
|
||||
<button style="margin-left: 8px; margin-bottom: 8px" (click)="downloadClicked()" [disabled]="downloadingfile" type="submit" mat-stroked-button
|
||||
color="primary">Download</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="centered big" id="bar_div" *ngIf="downloadingfile;else nofile">
|
||||
<div *ngIf="determinateProgress;else indeterminateprogress">
|
||||
<mat-progress-bar mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
|
||||
</div>
|
||||
<ng-template #indeterminateprogress>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
<div [ngClass]="(determinateProgress && percentDownloaded === 100)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px" *ngIf="determinateProgress;else indeterminateprogress">
|
||||
<mat-progress-bar mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
|
||||
<br/>
|
||||
</div>
|
||||
<div *ngIf="determinateProgress && percentDownloaded === 100" class="spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
<ng-template #indeterminateprogress>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
<br/>
|
||||
</div>
|
||||
<ng-template #nofile>
|
||||
|
||||
</ng-template>
|
||||
<div style="margin: 20px" *ngIf="fileManagerEnabled">
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel class="big">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Audio
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
Your audio files are here
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<div *ngIf="mp3s.length > 0;else nomp3s">
|
||||
<mat-grid-list cols="4" rowHeight="150px">
|
||||
<mat-grid-tile *ngFor="let file of mp3s; index as i;">
|
||||
<app-file-card (removeFile)="removeFromMp3($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
||||
[length]="file.duration" [isAudio]="true"></app-file-card>
|
||||
</mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
</div>
|
||||
|
||||
</mat-expansion-panel>
|
||||
<mat-expansion-panel class="big">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
Video
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
Your video files are here
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<div *ngIf="mp4s.length > 0;else nomp4s">
|
||||
<mat-grid-list cols="4" rowHeight="150px">
|
||||
<mat-grid-tile *ngFor="let file of mp4s; index as i;">
|
||||
<app-file-card (removeFile)="removeFromMp4($event)" [title]="file.title" [name]="file.id" [thumbnailURL]="file.thumbnailURL"
|
||||
[length]="file.duration" [isAudio]="false"></app-file-card>
|
||||
</mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
</div>
|
||||
|
||||
<ng-template #nomp3s>
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template #nomp4s>
|
||||
|
||||
</ng-template>
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {PostsService} from './posts.services';
|
||||
import {FileCardComponent} from './file-card/file-card.component';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import {FormControl, Validators} from '@angular/forms';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MatSnackBar} from '@angular/material';
|
||||
import { saveAs } from 'file-saver';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/mapTo';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
@@ -13,161 +15,210 @@ import 'rxjs/add/operator/toPromise';
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
determinateProgress: boolean = false;
|
||||
downloadingfile: boolean = false;
|
||||
export class AppComponent implements OnInit {
|
||||
iOS = false;
|
||||
|
||||
determinateProgress = false;
|
||||
downloadingfile = false;
|
||||
audioOnly: boolean;
|
||||
urlError: boolean = false;
|
||||
path: string = '';
|
||||
url: string = '';
|
||||
exists: string = "";
|
||||
topBarTitle: string = "Youtube Downloader";
|
||||
urlError = false;
|
||||
path = '';
|
||||
url = '';
|
||||
exists = '';
|
||||
topBarTitle = 'Youtube Downloader';
|
||||
percentDownloaded: number;
|
||||
constructor(private postsService: PostsService, public snackBar: MatSnackBar) {
|
||||
this.audioOnly = true;
|
||||
fileManagerEnabled = false;
|
||||
downloadOnlyMode = false;
|
||||
|
||||
|
||||
mp3s: any[] = [];
|
||||
mp4s: any[] = [];
|
||||
|
||||
urlForm = new FormControl('', [Validators.required]);
|
||||
|
||||
constructor(private postsService: PostsService, public snackBar: MatSnackBar) {
|
||||
this.audioOnly = false;
|
||||
|
||||
|
||||
// loading config
|
||||
this.postsService.loadNavItems().subscribe(result => { // loads settings
|
||||
var backendUrl = result.YoutubeDLMaterial.Host.backendurl;
|
||||
this.topBarTitle = result.YoutubeDLMaterial.Extra.title_top;
|
||||
const backendUrl = result['YoutubeDLMaterial']['Host']['backendurl'];
|
||||
this.topBarTitle = result['YoutubeDLMaterial']['Extra']['title_top'];
|
||||
this.fileManagerEnabled = result['YoutubeDLMaterial']['Extra']['file_manager_enabled'];
|
||||
this.downloadOnlyMode = result['YoutubeDLMaterial']['Extra']['download_only_mode'];
|
||||
|
||||
this.postsService.path = backendUrl;
|
||||
this.postsService.startPath = backendUrl;
|
||||
this.postsService.startPathSSL = backendUrl;
|
||||
|
||||
if (this.fileManagerEnabled) {
|
||||
this.getMp3s();
|
||||
this.getMp4s();
|
||||
}
|
||||
}, error => {
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// file manager stuff
|
||||
|
||||
getMp3s() {
|
||||
this.postsService.getMp3s().subscribe(result => {
|
||||
const mp3s = result['mp3s'];
|
||||
this.mp3s = mp3s;
|
||||
}, error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
getMp4s() {
|
||||
this.postsService.getMp4s().subscribe(result => {
|
||||
const mp4s = result['mp4s'];
|
||||
this.mp4s = mp4s;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
urlForm = new FormControl('', [Validators.required]);
|
||||
|
||||
doHandshake(url: string) {
|
||||
this.postsService.startHandshake(url).subscribe(theurl => {
|
||||
this.postsService.path = theurl;
|
||||
this.postsService.handShakeComplete = true;
|
||||
console.log("Handshake complete!");
|
||||
},
|
||||
error => {
|
||||
console.log("Initial handshake failed on http.");
|
||||
this.doHandshakeSSL(url);
|
||||
});
|
||||
public goToFile(name, isAudio) {
|
||||
if (isAudio) {
|
||||
this.downloadHelperMp3(name, true);
|
||||
} else {
|
||||
this.downloadHelperMp4(name, true);
|
||||
}
|
||||
}
|
||||
|
||||
doHandshakeSSL(url: string) {
|
||||
this.postsService.startHandshakeSSL(url).subscribe(theurl => {
|
||||
this.postsService.path = theurl;
|
||||
this.postsService.handShakeComplete = true;
|
||||
console.log("Handshake complete!");
|
||||
},
|
||||
error => {
|
||||
console.log("Initial handshake failed on https too! Make sure port 17442 is open.");
|
||||
this.postsService.handShakeComplete = false;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
downloadHelperMp3(name: string)
|
||||
{
|
||||
this.postsService.getFileStatusMp3(name).subscribe(fileExists => {
|
||||
var exists = fileExists;
|
||||
this.exists = exists[0];
|
||||
if (exists[0] == "failed")
|
||||
{
|
||||
var percent = exists[2];
|
||||
console.log(percent);
|
||||
if (percent > 0.30)
|
||||
{
|
||||
this.determinateProgress = true;
|
||||
this.percentDownloaded = percent*100;
|
||||
}
|
||||
setTimeout(() => this.downloadHelperMp3(name), 500);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.href = this.exists;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
downloadHelperMp4(name: string)
|
||||
{
|
||||
this.postsService.getFileStatusMp4(name).subscribe(fileExists => {
|
||||
var exists = fileExists;
|
||||
this.exists = exists[0];
|
||||
if (exists[0] == "failed")
|
||||
{
|
||||
var percent = exists[2];
|
||||
if (percent > 0.30)
|
||||
{
|
||||
this.determinateProgress = true;
|
||||
this.percentDownloaded = percent*100;
|
||||
}
|
||||
setTimeout(() => this.downloadHelperMp4(name), 500);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.href = this.exists;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
downloadClicked()
|
||||
{
|
||||
if (this.ValidURL(this.url))
|
||||
{
|
||||
this.urlError = false;
|
||||
this.path = "";
|
||||
|
||||
if (this.audioOnly)
|
||||
{
|
||||
this.downloadingfile = true;
|
||||
this.postsService.makeMP3(this.url).subscribe(posts => {
|
||||
this.path = posts;
|
||||
if (this.path != "-1")
|
||||
{
|
||||
this.downloadHelperMp3(this.path);
|
||||
}
|
||||
},
|
||||
error => { // can't access server
|
||||
this.downloadingfile = false;
|
||||
this.openSnackBar("Download failed!", "OK.");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.downloadingfile = true;
|
||||
this.postsService.makeMP4(this.url).subscribe(posts => {
|
||||
this.path = posts;
|
||||
if (this.path != "-1")
|
||||
{
|
||||
this.downloadHelperMp4(this.path);
|
||||
}
|
||||
},
|
||||
error => { // can't access server
|
||||
this.downloadingfile = false;
|
||||
this.openSnackBar("Download failed!", "OK.");
|
||||
});
|
||||
public removeFromMp3(name: string) {
|
||||
for (let i = 0; i < this.mp3s.length; i++) {
|
||||
if (this.mp3s[i].id === name) {
|
||||
this.mp3s.splice(i, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
public removeFromMp4(name: string) {
|
||||
// console.log(name);
|
||||
// console.log(this.mp4s);
|
||||
for (let i = 0; i < this.mp4s.length; i++) {
|
||||
if (this.mp4s[i].id === name) {
|
||||
this.mp4s.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// app initialization.
|
||||
ngOnInit() {
|
||||
this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window['MSStream'];
|
||||
}
|
||||
|
||||
// download helpers
|
||||
|
||||
downloadHelperMp3(name: string, forceView = false) {
|
||||
this.postsService.getFileStatusMp3(name).subscribe(fileExists => {
|
||||
const exists = fileExists;
|
||||
this.exists = exists[0];
|
||||
if (exists[0] === 'failed') {
|
||||
const percent = exists[2];
|
||||
// console.log(percent);
|
||||
if (percent > 0.30) {
|
||||
this.determinateProgress = true;
|
||||
this.percentDownloaded = percent * 100;
|
||||
}
|
||||
setTimeout(() => this.downloadHelperMp3(name), 500);
|
||||
} else {
|
||||
// if download only mode, just download the file. no redirect
|
||||
if (forceView === false && this.downloadOnlyMode && !this.iOS) {
|
||||
this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => {
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, name + '.mp3');
|
||||
this.downloadingfile = false;
|
||||
});
|
||||
} else {
|
||||
window.location.href = this.exists;
|
||||
}
|
||||
|
||||
// reloads mp3s
|
||||
this.getMp3s();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
downloadHelperMp4(name: string, forceView = false) {
|
||||
this.postsService.getFileStatusMp4(name).subscribe(fileExists => {
|
||||
const exists = fileExists;
|
||||
this.exists = exists[0];
|
||||
if (exists[0] === 'failed') {
|
||||
const percent = exists[2];
|
||||
if (percent > 0.30) {
|
||||
this.determinateProgress = true;
|
||||
this.percentDownloaded = percent * 100;
|
||||
}
|
||||
setTimeout(() => this.downloadHelperMp4(name), 500);
|
||||
} else {
|
||||
// if download only mode, just download the file. no redirect
|
||||
if (forceView === false && this.downloadOnlyMode) {
|
||||
this.postsService.downloadFileFromServer(name, 'video').subscribe(res => {
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, name + '.mp4');
|
||||
this.downloadingfile = false;
|
||||
});
|
||||
} else {
|
||||
window.location.href = this.exists;
|
||||
}
|
||||
|
||||
// reloads mp4s
|
||||
this.getMp4s();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// download click handler
|
||||
downloadClicked() {
|
||||
if (this.ValidURL(this.url)) {
|
||||
this.urlError = false;
|
||||
this.path = '';
|
||||
|
||||
if (this.audioOnly) {
|
||||
this.downloadingfile = true;
|
||||
this.postsService.makeMP3(this.url).subscribe(posts => {
|
||||
this.path = posts['audiopathEncoded'];
|
||||
if (this.path !== '-1') {
|
||||
this.downloadHelperMp3(this.path);
|
||||
}
|
||||
}, error => { // can't access server
|
||||
this.downloadingfile = false;
|
||||
this.openSnackBar('Download failed!', 'OK.');
|
||||
});
|
||||
} else {
|
||||
this.downloadingfile = true;
|
||||
this.postsService.makeMP4(this.url).subscribe(posts => {
|
||||
this.path = posts['videopathEncoded'];
|
||||
if (this.path !== '-1') {
|
||||
this.downloadHelperMp4(this.path);
|
||||
}
|
||||
}, error => { // can't access server
|
||||
this.downloadingfile = false;
|
||||
this.openSnackBar('Download failed!', 'OK.');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.urlError = true;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if url is a valid URL
|
||||
ValidURL(str) {
|
||||
var strRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;
|
||||
var re=new RegExp(strRegex);
|
||||
// tslint:disable-next-line: max-line-length
|
||||
const strRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;
|
||||
const re = new RegExp(strRegex);
|
||||
return re.test(str);
|
||||
}
|
||||
|
||||
openSnackBar(message: string, action: string) {
|
||||
// snackbar helper
|
||||
public openSnackBar(message: string, action: string) {
|
||||
this.snackBar.open(message, action, {
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import {MatNativeDateModule, MatRadioModule, MatInputModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule,
|
||||
MatSnackBarModule, MatCardModule, MatSelectModule, MatToolbarModule, MatCheckboxModule,
|
||||
MatProgressBarModule } from '@angular/material';
|
||||
MatSnackBarModule, MatCardModule, MatSelectModule, MatToolbarModule, MatCheckboxModule, MatGridListModule,
|
||||
MatProgressBarModule, MatExpansionModule,
|
||||
MatGridList,
|
||||
MatProgressSpinnerModule} from '@angular/material';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import { AppComponent } from './app.component';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import { FileCardComponent } from './file-card/file-card.component';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
FileCardComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@@ -34,7 +39,11 @@ import {APP_BASE_HREF} from '@angular/common';
|
||||
MatSidenavModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatProgressBarModule
|
||||
MatGridListModule,
|
||||
MatExpansionModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
RouterModule
|
||||
],
|
||||
providers: [PostsService],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
33
src/app/file-card/file-card.component.css
Normal file
33
src/app/file-card/file-card.component.css
Normal file
@@ -0,0 +1,33 @@
|
||||
.example-card {
|
||||
width: 150px;
|
||||
height: 125px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.deleteButton {
|
||||
top:-5px;
|
||||
right:-5px;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
/* Coerce the <span> icon container away from display:inline */
|
||||
.mat-icon-button .mat-button-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image {
|
||||
max-width:100%;
|
||||
max-height:100%;
|
||||
}
|
||||
|
||||
.example-full-width-height {
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.centered {
|
||||
margin: 0 auto;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
13
src/app/file-card/file-card.component.html
Normal file
13
src/app/file-card/file-card.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<mat-card class="example-card">
|
||||
<button (click)="deleteFile()" class="deleteButton" mat-icon-button><mat-icon>delete_forever</mat-icon></button>
|
||||
<div style="padding:5px">
|
||||
<b><a href="javascript:void(0)" (click)="appComponent.goToFile(name, isAudio)">{{title}}</a></b>
|
||||
<br/>
|
||||
ID: {{name}}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="centered example-full-width-height"><img class="image" src="{{thumbnailURL}}" alt="Thumbnail"></div>
|
||||
|
||||
</mat-card>
|
||||
25
src/app/file-card/file-card.component.spec.ts
Normal file
25
src/app/file-card/file-card.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { FileCardComponent } from './file-card.component';
|
||||
|
||||
describe('FileCardComponent', () => {
|
||||
let component: FileCardComponent;
|
||||
let fixture: ComponentFixture<FileCardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ FileCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FileCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
43
src/app/file-card/file-card.component.ts
Normal file
43
src/app/file-card/file-card.component.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Component, OnInit, Input, Output } from '@angular/core';
|
||||
import {PostsService} from '../posts.services';
|
||||
import {MatSnackBar} from '@angular/material';
|
||||
import {AppComponent} from '../app.component';
|
||||
import {EventEmitter} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-file-card',
|
||||
templateUrl: './file-card.component.html',
|
||||
styleUrls: ['./file-card.component.css']
|
||||
})
|
||||
export class FileCardComponent implements OnInit {
|
||||
|
||||
@Input() title: string;
|
||||
@Input() length: string;
|
||||
@Input() name: string;
|
||||
@Input() thumbnailURL: string;
|
||||
@Input() isAudio = true;
|
||||
@Output() removeFile: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
constructor(private postsService: PostsService, public snackBar: MatSnackBar, public appComponent: AppComponent) { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
deleteFile() {
|
||||
this.postsService.deleteFile(this.name, this.isAudio).subscribe(result => {
|
||||
if (result === true) {
|
||||
this.openSnackBar('Delete success!', 'OK.');
|
||||
this.removeFile.emit(this.name);
|
||||
} else {
|
||||
this.openSnackBar('Delete failed!', 'OK.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openSnackBar(message: string, action: string) {
|
||||
this.snackBar.open(message, action, {
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Http} from '@angular/http';
|
||||
import {Injectable, isDevMode} from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpRequest, HttpResponseBase } from '@angular/common/http';
|
||||
import config from '../assets/default.json';
|
||||
import 'rxjs/add/operator/map';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
@@ -8,67 +9,75 @@ import 'rxjs/add/observable/throw';
|
||||
|
||||
@Injectable()
|
||||
export class PostsService {
|
||||
path: string = "";
|
||||
audioFolder: string = "";
|
||||
videoFolder: string = "";
|
||||
startPath: string = "http://localhost:17442/";
|
||||
startPathSSL: string = "https://localhost:17442/"
|
||||
handShakeComplete: boolean = false;
|
||||
path = '';
|
||||
audioFolder = '';
|
||||
videoFolder = '';
|
||||
startPath = 'http://localhost:17442/';
|
||||
startPathSSL = 'https://localhost:17442/'
|
||||
handShakeComplete = false;
|
||||
|
||||
constructor(private http: Http){
|
||||
constructor(private http: HttpClient) {
|
||||
console.log('PostsService Initialized...');
|
||||
}
|
||||
|
||||
startHandshake(url: string): Observable<string>
|
||||
{
|
||||
return this.http.get(url + "geturl")
|
||||
.map(res => res.json());
|
||||
startHandshake(url: string) {
|
||||
return this.http.get(url + 'geturl');
|
||||
}
|
||||
|
||||
startHandshakeSSL(url: string): Observable<string>
|
||||
{
|
||||
return this.http.get(url + "geturl")
|
||||
.map(res => res.json());
|
||||
startHandshakeSSL(url: string) {
|
||||
return this.http.get(url + 'geturl');
|
||||
}
|
||||
|
||||
getVideoFolder(): Observable<string>
|
||||
{
|
||||
return this.http.get(this.startPath + "videofolder")
|
||||
.map(res => res.json());
|
||||
getVideoFolder() {
|
||||
return this.http.get(this.startPath + 'videofolder');
|
||||
}
|
||||
|
||||
getAudioFolder(): Observable<string>
|
||||
{
|
||||
return this.http.get(this.startPath + "audiofolder")
|
||||
.map(res => res.json());
|
||||
getAudioFolder() {
|
||||
return this.http.get(this.startPath + 'audiofolder');
|
||||
}
|
||||
|
||||
makeMP3(url: string): Observable<string>
|
||||
{
|
||||
return this.http.post(this.path + "tomp3",{url: url})
|
||||
.map(res => res.json());
|
||||
makeMP3(url: string) {
|
||||
return this.http.post(this.path + 'tomp3', {url: url});
|
||||
}
|
||||
|
||||
makeMP4(url: string): Observable<string>
|
||||
{
|
||||
return this.http.post(this.path + "tomp4",{url: url})
|
||||
.map(res => res.json());
|
||||
makeMP4(url: string) {
|
||||
return this.http.post(this.path + 'tomp4', {url: url});
|
||||
}
|
||||
|
||||
getFileStatusMp3(name: string): Observable<any> {
|
||||
return this.http.post(this.path + "mp3fileexists",{name: name})
|
||||
.map(res => res.json());
|
||||
getFileStatusMp3(name: string) {
|
||||
return this.http.post(this.path + 'fileStatusMp3', {name: name});
|
||||
}
|
||||
|
||||
getFileStatusMp4(name: string): Observable<any> {
|
||||
return this.http.post(this.path + "mp4fileexists",{name: name})
|
||||
.map(res => res.json());
|
||||
getFileStatusMp4(name: string) {
|
||||
return this.http.post(this.path + 'fileStatusMp4', {name: name});
|
||||
}
|
||||
|
||||
loadNavItems() {
|
||||
console.log("Config location: " + window.location.href + "backend/config/default.json");
|
||||
return this.http.get(window.location.href + "backend/config/default.json")
|
||||
.map(res => res.json());
|
||||
if (isDevMode()) {
|
||||
return this.http.get('./assets/default.json');
|
||||
}
|
||||
console.log('Config location: ' + window.location.href + 'backend/config/default.json');
|
||||
return this.http.get(window.location.href + 'backend/config/default.json');
|
||||
}
|
||||
|
||||
deleteFile(name: string, isAudio: boolean) {
|
||||
if (isAudio) {
|
||||
return this.http.post(this.path + 'deleteMp3', {name: name});
|
||||
} else {
|
||||
return this.http.post(this.path + 'deleteMp4', {name: name});
|
||||
}
|
||||
}
|
||||
|
||||
getMp3s() {
|
||||
return this.http.post(this.path + 'getMp3s', {});
|
||||
}
|
||||
|
||||
getMp4s() {
|
||||
return this.http.post(this.path + 'getMp4s', {});
|
||||
}
|
||||
|
||||
downloadFileFromServer(fileName, type) {
|
||||
return this.http.post(this.path + 'downloadFile', {fileName: fileName, type: type}, {responseType: 'blob'});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
22
src/assets/default.json
Normal file
22
src/assets/default.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"YoutubeDLMaterial": {
|
||||
"Host": {
|
||||
"frontendurl": "http://localhost:4200",
|
||||
"backendurl": "http://localhost:17442/"
|
||||
},
|
||||
"Encryption": {
|
||||
"use-encryption": false,
|
||||
"cert-file-path": "/etc/letsencrypt/live/example.com/fullchain.pem",
|
||||
"key-file-path": "/etc/letsencrypt/live/example.com/privkey.pem"
|
||||
},
|
||||
"Downloader": {
|
||||
"path-base": "http://localhost:17442/",
|
||||
"path-audio": "audio/",
|
||||
"path-video": "video/"
|
||||
},
|
||||
"Extra": {
|
||||
"title_top": "Youtube Downloader",
|
||||
"file_manager_enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "es2015",
|
||||
"baseUrl": "",
|
||||
"types": []
|
||||
},
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"baseUrl": "",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"baseUrl": "",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"test.ts",
|
||||
"polyfills.ts"
|
||||
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"downlevelIteration": true,
|
||||
"importHelpers": true,
|
||||
"outDir": "./dist/out-tsc",
|
||||
"baseUrl": "src",
|
||||
"sourceMap": true,
|
||||
@@ -8,13 +10,16 @@
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"target": "es2015",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2016",
|
||||
"dom"
|
||||
]
|
||||
],
|
||||
"module": "esnext"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
tslint.json
11
tslint.json
@@ -14,8 +14,7 @@
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs"
|
||||
true
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": [
|
||||
@@ -119,12 +118,12 @@
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"use-input-property-decorator": true,
|
||||
"use-output-property-decorator": true,
|
||||
"use-host-property-decorator": true,
|
||||
"no-inputs-metadata-property": true,
|
||||
"no-outputs-metadata-property": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
"no-output-rename": true,
|
||||
"use-life-cycle-interface": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true,
|
||||
"component-class-suffix": true,
|
||||
"directive-class-suffix": true,
|
||||
|
||||
Reference in New Issue
Block a user