From 46087f622eae6514a90c8f69d7801d7b38157315 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 26 Sep 2021 02:40:05 -0600 Subject: [PATCH 1/3] Switched forever.js to pm2 Updated winston --- Dockerfile | 5 +- backend/ecosystem.config.js | 8 +++ backend/entrypoint.sh | 2 +- backend/package-lock.json | 137 +++++++++++++++--------------------- backend/package.json | 2 +- 5 files changed, 69 insertions(+), 85 deletions(-) create mode 100644 backend/ecosystem.config.js diff --git a/Dockerfile b/Dockerfile index d2b9d75..8324e3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,6 @@ ENV UID=1000 \ USER=youtube ENV NO_UPDATE_NOTIFIER=true -ENV FOREVER_ROOT=/app/.forever RUN addgroup -S $USER -g $GID && adduser -D -S $USER -G $USER -u $UID @@ -37,7 +36,7 @@ RUN apk add --no-cache \ WORKDIR /app COPY --chown=$UID:$GID [ "backend/package.json", "backend/package-lock.json", "/app/" ] -RUN npm install forever -g +RUN npm install pm2 -g RUN npm install && chown -R $UID:$GID ./ COPY --chown=$UID:$GID --from=frontend [ "/build/backend/public/", "/app/public/" ] @@ -45,4 +44,4 @@ COPY --chown=$UID:$GID [ "/backend/", "/app/" ] EXPOSE 17442 ENTRYPOINT [ "/app/entrypoint.sh" ] -CMD [ "forever", "app.js" ] +CMD [ "pm2-runtime", "ecosystem.config.js" ] diff --git a/backend/ecosystem.config.js b/backend/ecosystem.config.js new file mode 100644 index 0000000..70b6e9d --- /dev/null +++ b/backend/ecosystem.config.js @@ -0,0 +1,8 @@ +module.exports = { + apps : [{ + name : "YoutubeDL-Material", + script : "./app.js", + watch : "placeholder", + watch_delay: 5000 + }] +} diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index 611bcc8..c4c0edc 100755 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh set -eu -CMD="forever app.js" +CMD="pm2-runtime ecosystem.config.js" # if the first arg starts with "-" pass it to program if [ "${1#-}" != "$1" ]; then diff --git a/backend/package-lock.json b/backend/package-lock.json index 3093a80..6554427 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4,6 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@oozcitak/dom": { "version": "1.15.10", "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", @@ -725,11 +735,6 @@ "simple-swizzle": "^0.2.2" } }, - "colornames": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", - "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" - }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -983,16 +988,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "diagnostics": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", - "requires": { - "colorspace": "1.1.x", - "enabled": "1.0.x", - "kuler": "1.0.x" - } - }, "dicer": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", @@ -1104,12 +1099,9 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "enabled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", - "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", - "requires": { - "env-variable": "0.0.x" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "encodeurl": { "version": "1.0.2", @@ -1124,11 +1116,6 @@ "once": "^1.4.0" } }, - "env-variable": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", - "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1238,15 +1225,10 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, "fecha": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", - "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" }, "fill-range": { "version": "7.0.1", @@ -1303,6 +1285,11 @@ } } }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "follow-redirects": { "version": "1.14.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", @@ -1821,12 +1808,9 @@ } }, "kuler": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", - "requires": { - "colornames": "^1.1.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, "latest-version": { "version": "5.1.0", @@ -2032,21 +2016,21 @@ } }, "logform": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", - "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", "requires": { "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^2.3.3", + "fecha": "^4.2.0", "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", "triple-beam": "^1.3.0" }, "dependencies": { "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -2510,9 +2494,12 @@ } }, "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } }, "onetime": { "version": "5.1.0", @@ -2934,6 +2921,11 @@ "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, + "safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3529,42 +3521,27 @@ } }, "winston": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", - "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", "requires": { - "async": "^2.6.1", - "diagnostics": "^1.1.1", - "is-stream": "^1.1.0", - "logform": "^2.1.1", - "one-time": "0.0.4", - "readable-stream": "^3.1.1", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.3.0" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - } + "winston-transport": "^4.4.0" } }, "winston-transport": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", - "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", "requires": { - "readable-stream": "^2.3.6", + "readable-stream": "^2.3.7", "triple-beam": "^1.2.0" }, "dependencies": { diff --git a/backend/package.json b/backend/package.json index 234ab9c..ef5d119 100644 --- a/backend/package.json +++ b/backend/package.json @@ -65,7 +65,7 @@ "shortid": "^2.2.15", "unzipper": "^0.10.10", "uuidv4": "^6.0.6", - "winston": "^3.2.1", + "winston": "^3.3.3", "xmlbuilder2": "^3.0.2", "youtube-dl": "^3.0.2" } From dbeeb32d48e16407c426b0b056457f39d0fe2bdc Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 27 Sep 2021 18:11:38 -0600 Subject: [PATCH 2/3] Updated Dockerfile and entrypoint to use pm2 instead of forever --- Dockerfile | 2 +- backend/entrypoint.sh | 2 +- backend/pm2.config.js | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 backend/pm2.config.js diff --git a/Dockerfile b/Dockerfile index 8324e3c..4a2d3b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,4 +44,4 @@ COPY --chown=$UID:$GID [ "/backend/", "/app/" ] EXPOSE 17442 ENTRYPOINT [ "/app/entrypoint.sh" ] -CMD [ "pm2-runtime", "ecosystem.config.js" ] +CMD [ "pm2-runtime", "pm2.config.js" ] diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index c4c0edc..5074353 100755 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh set -eu -CMD="pm2-runtime ecosystem.config.js" +CMD="pm2-runtime pm2.config.js" # if the first arg starts with "-" pass it to program if [ "${1#-}" != "$1" ]; then diff --git a/backend/pm2.config.js b/backend/pm2.config.js new file mode 100644 index 0000000..42e78c5 --- /dev/null +++ b/backend/pm2.config.js @@ -0,0 +1,7 @@ +module.exports = { + apps : [{ + name : "YoutubeDL-Material", + script : "./app.js", + watch : "placeholder" + }] +} \ No newline at end of file From 84187b94742d53b7e806cfdcef79fd04d673fa87 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 28 Sep 2021 20:14:57 -0600 Subject: [PATCH 3/3] Fixed issue where selecting video quality would Main component cleanup Removed deprecated file card component --- backend/downloader.js | 2 +- src/app/app.module.ts | 2 - src/app/file-card/file-card.component.css | 68 ------ src/app/file-card/file-card.component.html | 29 --- src/app/file-card/file-card.component.spec.ts | 25 --- src/app/file-card/file-card.component.ts | 120 ---------- src/app/main/main.component.html | 10 +- src/app/main/main.component.ts | 209 +++++------------- 8 files changed, 62 insertions(+), 403 deletions(-) delete mode 100644 src/app/file-card/file-card.component.css delete mode 100644 src/app/file-card/file-card.component.html delete mode 100644 src/app/file-card/file-card.component.spec.ts delete mode 100644 src/app/file-card/file-card.component.ts diff --git a/backend/downloader.js b/backend/downloader.js index f1ada44..e151f87 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -417,7 +417,7 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f downloadConfig = customArgs.split(',,'); } else { if (customQualityConfiguration) { - qualityPath = ['-f', customQualityConfiguration]; + qualityPath = ['-f', customQualityConfiguration, '--merge-output-format', 'mp4']; } else if (selectedHeight && selectedHeight !== '' && !is_audio) { qualityPath = ['-f', `'(mp4)[height=${selectedHeight}'`]; } else if (is_audio) { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dcaa9ec..410a412 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -34,7 +34,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { PostsService } from 'app/posts.services'; -import { FileCardComponent } from './file-card/file-card.component'; import { RouterModule } from '@angular/router'; import { AppRoutingModule } from './app-routing.module'; import { MainComponent } from './main/main.component'; @@ -98,7 +97,6 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible @NgModule({ declarations: [ AppComponent, - FileCardComponent, MainComponent, PlayerComponent, InputDialogComponent, diff --git a/src/app/file-card/file-card.component.css b/src/app/file-card/file-card.component.css deleted file mode 100644 index a581bca..0000000 --- a/src/app/file-card/file-card.component.css +++ /dev/null @@ -1,68 +0,0 @@ -.example-card { - width: 150px; - height: 125px; - padding: 0px; -} - -.deleteButton { - top:-5px; - right:-5px; - position:absolute; -} - -/* Coerce the icon container away from display:inline */ -.mat-icon-button .mat-button-wrapper { - display: flex; - justify-content: center; -} - -.image { - width: 100%; -} - -.example-full-width-height { - width: 100%; - height: 100% -} - -.centered { - margin: 0 auto; - top: 50%; - left: 50%; -} - -.img-div { - height: 60px; - padding: 0px; - margin: 8px 0px 0px -5px; - width: calc(100% + 5px + 5px); - overflow: hidden; - border-radius: 0px 0px 4px 4px; -} - -.max-two-lines { - display: -webkit-box; - display: -moz-box; - max-height: 2.4em; - line-height: 1.2em; - overflow: hidden; - text-overflow: ellipsis; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; -} - -.file-link { - width: 80%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: block; -} - -@media (max-width: 576px){ - - .example-card { - width: 125px !important; - } - -} \ No newline at end of file diff --git a/src/app/file-card/file-card.component.html b/src/app/file-card/file-card.component.html deleted file mode 100644 index f3c7ffe..0000000 --- a/src/app/file-card/file-card.component.html +++ /dev/null @@ -1,29 +0,0 @@ - -
-
-
- {{title}} -
- ID: {{name}} -
Count: {{count}}
-
-
- Thumbnail - - - -
-
- - - - - - - - - - - - -
diff --git a/src/app/file-card/file-card.component.spec.ts b/src/app/file-card/file-card.component.spec.ts deleted file mode 100644 index 0e940d9..0000000 --- a/src/app/file-card/file-card.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { FileCardComponent } from './file-card.component'; - -describe('FileCardComponent', () => { - let component: FileCardComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ FileCardComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(FileCardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/file-card/file-card.component.ts b/src/app/file-card/file-card.component.ts deleted file mode 100644 index 5596eec..0000000 --- a/src/app/file-card/file-card.component.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Component, OnInit, Input, Output } from '@angular/core'; -import {PostsService} from '../posts.services'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import {EventEmitter} from '@angular/core'; -import { MainComponent } from 'app/main/main.component'; -import { Subject, Observable } from 'rxjs'; -import 'rxjs/add/observable/merge'; -import { MatDialog } from '@angular/material/dialog'; -import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component'; -import { ModifyPlaylistComponent } from '../dialogs/modify-playlist/modify-playlist.component'; - -@Component({ - selector: 'app-file-card', - templateUrl: './file-card.component.html', - styleUrls: ['./file-card.component.css'] -}) -export class FileCardComponent implements OnInit { - @Input() file: any; - @Input() title: string; - @Input() length: string; - @Input() name: string; - @Input() uid: string; - @Input() thumbnailURL: string; - @Input() isAudio = true; - @Output() removeFile: EventEmitter = new EventEmitter(); - @Input() playlist = null; - @Input() count = null; - @Input() use_youtubedl_archive = false; - type; - image_loaded = false; - image_errored = false; - - scrollSubject; - scrollAndLoad; - - constructor(private postsService: PostsService, public snackBar: MatSnackBar, public mainComponent: MainComponent, - private dialog: MatDialog) { - - this.scrollSubject = new Subject(); - this.scrollAndLoad = Observable.merge( - Observable.fromEvent(window, 'scroll'), - this.scrollSubject - ); - } - - ngOnInit() { - this.type = this.isAudio ? 'audio' : 'video'; - - if (this.file && this.file.url && this.file.url.includes('youtu')) { - const string_id = (this.playlist ? '?list=' : '?v=') - const index_offset = (this.playlist ? 6 : 3); - const end_index = this.file.url.indexOf(string_id) + index_offset; - this.name = this.file.url.substring(end_index, this.file.url.length); - } - } - - deleteFile(blacklistMode = false) { - if (!this.playlist) { - this.postsService.deleteFile(this.uid, blacklistMode).subscribe(result => { - if (result) { - this.openSnackBar('Delete success!', 'OK.'); - this.removeFile.emit(this.name); - } else { - this.openSnackBar('Delete failed!', 'OK.'); - } - }, err => { - this.openSnackBar('Delete failed!', 'OK.'); - }); - } else { - this.removeFile.emit(this.name); - } - - } - - openVideoInfoDialog() { - const dialogRef = this.dialog.open(VideoInfoDialogComponent, { - data: { - file: this.file, - }, - minWidth: '50vw' - }); - } - - editPlaylistDialog() { - const dialogRef = this.dialog.open(ModifyPlaylistComponent, { - data: { - playlist_id: this.playlist.id, - width: '65vw' - } - }); - - dialogRef.afterClosed().subscribe(res => { - // updates playlist in file manager if it changed - if (dialogRef.componentInstance.playlist_updated) { - this.playlist = dialogRef.componentInstance.original_playlist; - this.title = this.playlist.name; - this.count = this.playlist.fileNames.length; - } - }); - } - - onImgError(event) { - this.image_errored = true; - } - - onHoverResponse() { - this.scrollSubject.next(); - } - - imageLoaded(loaded) { - this.image_loaded = true; - } - - public openSnackBar(message: string, action: string) { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - -} diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index cba6565..18a4ea9 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -19,7 +19,7 @@ Quality - + Max @@ -117,7 +117,7 @@ - + No need to include URL, just everything after. Args are delimited using two commas like so: ,, @@ -132,7 +132,7 @@ - + Documentation. Path is relative to the config download path. Don't include extension. @@ -146,12 +146,12 @@ - +
- +
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 00e1c61..93a8d53 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit, ElementRef, ViewChild, ViewChildren, QueryList } from '@angular/core'; import {PostsService} from '../posts.services'; -import {FileCardComponent} from '../file-card/file-card.component'; import { Observable, Subject } from 'rxjs'; import {FormControl, Validators} from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; @@ -90,7 +89,6 @@ export class MainComponent implements OnInit { mp3s: any[] = []; mp4s: any[] = []; - files_cols = null; playlists = {'audio': [], 'video': []}; playlist_thumbnails = {}; downloading_content = {'audio': {}, 'video': {}}; @@ -197,8 +195,6 @@ export class MainComponent implements OnInit { @ViewChild('urlinput', { read: ElementRef }) urlInput: ElementRef; @ViewChild('recentVideos') recentVideos: RecentVideosComponent; - @ViewChildren('audiofilecard') audioFileCards: QueryList; - @ViewChildren('videofilecard') videoFileCards: QueryList; last_valid_url = ''; last_url_check = 0; @@ -220,14 +216,14 @@ export class MainComponent implements OnInit { this.audioOnly = false; } - async configLoad() { + async configLoad(): Promise { await this.loadConfig(); if (this.autoStartDownload) { this.downloadClicked(); } } - async loadConfig() { + async loadConfig(): Promise { // loading config this.fileManagerEnabled = this.postsService.config['Extra']['file_manager_enabled'] && this.postsService.hasPermission('filemanager'); @@ -289,7 +285,7 @@ export class MainComponent implements OnInit { } // app initialization. - ngOnInit() { + ngOnInit(): void { if (this.postsService.initialized) { this.configLoad(); } else { @@ -331,62 +327,17 @@ export class MainComponent implements OnInit { .subscribe((should_simulate) => { if (should_simulate) this.getSimulatedOutput(); }); - - this.setCols(); } - ngAfterViewInit() { + ngAfterViewInit(): void { if (this.youtubeSearchEnabled && this.youtubeAPIKey) { this.youtubeSearch.initializeAPI(this.youtubeAPIKey); this.attachToInput(); } } - public setCols() { - if (window.innerWidth <= 350) { - this.files_cols = 1; - } else if (window.innerWidth <= 500) { - this.files_cols = 2; - } else if (window.innerWidth <= 750) { - this.files_cols = 3 - } else { - this.files_cols = 4; - } - } - - public goToFile(container, isAudio, uid) { - this.downloadHelper(container, isAudio ? 'audio' : 'video', false, false, true); - } - - public goToPlaylist(playlistID, type) { - const playlist = this.getPlaylistObjectByID(playlistID, type); - if (playlist) { - if (this.downloadOnlyMode) { - this.downloading_content[type][playlistID] = true; - this.downloadPlaylist(playlist); - } else { - localStorage.setItem('player_navigator', this.router.url); - const fileNames = playlist.fileNames; - this.router.navigate(['/player', {fileNames: fileNames.join('|nvr|'), type: type, id: playlistID, uid: playlistID}]); - } - } else { - // playlist not found - console.error(`Playlist with ID ${playlistID} not found!`); - } - } - - getPlaylistObjectByID(playlistID, type) { - for (let i = 0; i < this.playlists[type].length; i++) { - const playlist = this.playlists[type][i]; - if (playlist.id === playlistID) { - return playlist; - } - } - return null; - } - // download helpers - downloadHelper(container, type, is_playlist = false, force_view = false, navigate_mode = false) { + downloadHelper(container, type: string, is_playlist = false, force_view = false, navigate_mode = false): void { this.downloadingfile = false; if (!this.autoplay && !this.downloadOnlyMode && !navigate_mode) { // do nothing @@ -412,7 +363,7 @@ export class MainComponent implements OnInit { } // download click handler - downloadClicked() { + downloadClicked(): void { if (!this.ValidURL(this.url)) { this.urlError = true; return; @@ -453,16 +404,19 @@ export class MainComponent implements OnInit { } } + const selected_quality = this.selectedQuality; + this.selectedQuality = ''; + this.downloadingfile = true; - this.postsService.downloadFile(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality), + this.postsService.downloadFile(this.url, type, (selected_quality === '' ? null : selected_quality), customQualityConfiguration, customArgs, additionalArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => { this.current_download = res['download']; this.downloads.push(res['download']); this.download_uids.push(res['download']['uid']); - }, error => { // can't access server + }, () => { // can't access server this.downloadingfile = false; this.current_download = null; - this.openSnackBar('Download failed!', 'OK.'); + this.postsService.openSnackBar('Download failed!', 'OK.'); }); if (!this.autoplay) { @@ -474,7 +428,7 @@ export class MainComponent implements OnInit { } // download canceled handler - cancelDownload(download_to_cancel = null) { + cancelDownload(download_to_cancel = null): void { // if one is provided, cancel that one. otherwise, remove the current one if (download_to_cancel) { this.removeDownloadFromCurrentDownloads(download_to_cancel) @@ -485,33 +439,32 @@ export class MainComponent implements OnInit { this.current_download = null; } - getSelectedAudioFormat() { + getSelectedAudioFormat(): string { if (this.selectedQuality === '') { return null; } const cachedFormatsExists = this.cachedAvailableFormats[this.url] && this.cachedAvailableFormats[this.url]['formats']; if (cachedFormatsExists) { - const audio_formats = this.cachedAvailableFormats[this.url]['formats']['audio']; return this.selectedQuality['format_id']; } else { return null; } } - getSelectedVideoFormat() { + getSelectedVideoFormat(): string { if (this.selectedQuality === '') { return null; } const cachedFormats = this.cachedAvailableFormats[this.url] && this.cachedAvailableFormats[this.url]['formats']; if (cachedFormats) { - const video_formats = cachedFormats['video']; if (this.selectedQuality) { let selected_video_format = this.selectedQuality['format_id']; // add in audio format if necessary - if (!this.selectedQuality['acodec'] && cachedFormats['best_audio_format']) selected_video_format += `+${cachedFormats['best_audio_format']}`; + const audio_missing = !this.selectedQuality['acodec'] || this.selectedQuality['acodec'] === 'none'; + if (audio_missing && cachedFormats['best_audio_format']) selected_video_format += `+${cachedFormats['best_audio_format']}`; return selected_video_format; } } return null; } - getDownloadByUID(uid) { + getDownloadByUID(uid: string) { const index = this.downloads.findIndex(download => download.uid === uid); if (index !== -1) { return this.downloads[index]; @@ -520,7 +473,7 @@ export class MainComponent implements OnInit { } } - removeDownloadFromCurrentDownloads(download_to_remove) { + removeDownloadFromCurrentDownloads(download_to_remove): boolean { if (this.current_download === download_to_remove) { this.current_download = null; } @@ -533,7 +486,7 @@ export class MainComponent implements OnInit { } } - downloadFileFromServer(file, type) { + downloadFileFromServer(file, type: string): void { const ext = type === 'audio' ? 'mp3' : 'mp4' this.downloading_content[type][file.id] = true; this.postsService.downloadFileFromServer(file.uid).subscribe(res => { @@ -543,13 +496,12 @@ export class MainComponent implements OnInit { if (!this.fileManagerEnabled) { // tell server to delete the file once downloaded - this.postsService.deleteFile(file.uid).subscribe(delRes => { - }); + this.postsService.deleteFile(file.uid).subscribe(() => {}); } }); } - downloadPlaylist(playlist) { + downloadPlaylist(playlist): void { this.postsService.downloadPlaylistFromServer(playlist.id).subscribe(res => { if (playlist.id) { this.downloading_content[playlist.type][playlist.id] = false }; const blob: Blob = res; @@ -558,25 +510,25 @@ export class MainComponent implements OnInit { } - clearInput() { + clearInput(): void { this.url = ''; this.results_showing = false; } - onInputBlur() { + onInputBlur(): void { this.results_showing = false; } - visitURL(url) { + visitURL(url: string): void { window.open(url); } - useURL(url) { + useURL(url: string): void { this.results_showing = false; this.url = url; } - inputChanged(new_val) { + inputChanged(new_val: string): void { if (new_val === '' || !new_val) { this.results_showing = false; } else { @@ -587,7 +539,7 @@ export class MainComponent implements OnInit { } // checks if url is a valid URL - ValidURL(str) { + ValidURL(str: string): boolean { // 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); @@ -603,21 +555,14 @@ export class MainComponent implements OnInit { if (str !== this.last_valid_url && this.allowQualitySelect) { // get info this.getURLInfo(str); - this.argsChangedSubject.next(true); + this.argsChanged(); } this.last_valid_url = str; } return valid; } - // snackbar helper - public openSnackBar(message: string, action: string) { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - - getURLInfo(url) { + getURLInfo(url: string): void { // if url is a youtube playlist, skip getting url info if (url.includes('playlist')) { return; @@ -635,18 +580,14 @@ export class MainComponent implements OnInit { return; } this.cachedAvailableFormats[url]['formats'] = this.getAudioAndVideoFormats(infos.formats); - }, err => { + }, () => { this.errorFormats(url); }); } } - argChanged(): void { - this.argsChangedSubject.next(true); - } - getSimulatedOutput(): void { - // this function should be very similar to downloadFile() + // this function should be very similar to downloadClicked() const customArgs = (this.customArgsEnabled && this.replaceArgs ? this.customArgs : null); const additionalArgs = (this.customArgsEnabled && !this.replaceArgs ? this.customArgs : null); const customOutput = (this.customOutputEnabled ? this.customOutput : null); @@ -681,12 +622,12 @@ export class MainComponent implements OnInit { }); } - errorFormats(url) { + errorFormats(url: string): void { this.cachedAvailableFormats[url]['formats_loading'] = false; console.error('Could not load formats for url ' + url); } - attachToInput() { + attachToInput(): void { Observable.fromEvent(this.urlInput.nativeElement, 'keyup') .map((e: any) => e.target.value) // extract the value of input .filter((text: string) => text.length > 1) // filter out if empty @@ -715,41 +656,41 @@ export class MainComponent implements OnInit { ); } - onResize(event) { - this.setCols(); - } - - videoModeChanged(new_val) { - this.selectedQuality = ''; - localStorage.setItem('audioOnly', new_val.checked.toString()); + argsChanged(): void { this.argsChangedSubject.next(true); } - autoplayChanged(new_val) { + videoModeChanged(new_val): void { + this.selectedQuality = ''; + localStorage.setItem('audioOnly', new_val.checked.toString()); + this.argsChanged(); + } + + autoplayChanged(new_val): void { localStorage.setItem('autoplay', new_val.checked.toString()); } - customArgsEnabledChanged(new_val) { + customArgsEnabledChanged(new_val): void { localStorage.setItem('customArgsEnabled', new_val.checked.toString()); - this.argsChangedSubject.next(true); + this.argsChanged(); } - replaceArgsChanged(new_val) { + replaceArgsChanged(new_val): void { localStorage.setItem('replaceArgs', new_val.checked.toString()); - this.argsChangedSubject.next(true); + this.argsChanged(); } - customOutputEnabledChanged(new_val) { + customOutputEnabledChanged(new_val): void { localStorage.setItem('customOutputEnabled', new_val.checked.toString()); - this.argsChangedSubject.next(true); + this.argsChanged(); } - youtubeAuthEnabledChanged(new_val) { + youtubeAuthEnabledChanged(new_val): void { localStorage.setItem('youtubeAuthEnabled', new_val.checked.toString()); - this.argsChangedSubject.next(true); + this.argsChanged(); } - getAudioAndVideoFormats(formats) { + getAudioAndVideoFormats(formats): void { const audio_formats: any = {}; const video_formats: any = {}; @@ -808,7 +749,7 @@ export class MainComponent implements OnInit { return parsed_formats; } - getBestAudioFormatForMp4(audio_formats) { + getBestAudioFormatForMp4(audio_formats): void { let best_audio_format_for_mp4 = null; let best_audio_format_bitrate = 0; const available_audio_format_keys = Object.keys(audio_formats); @@ -824,46 +765,8 @@ export class MainComponent implements OnInit { return best_audio_format_for_mp4; } - accordionEntered(type) { - if (type === 'audio') { - audioFilesMouseHovering = true; - this.audioFileCards.forEach(filecard => { - filecard.onHoverResponse(); - }); - } else if (type === 'video') { - videoFilesMouseHovering = true; - this.videoFileCards.forEach(filecard => { - filecard.onHoverResponse(); - }); - } - } - - accordionLeft(type) { - if (type === 'audio') { - audioFilesMouseHovering = false; - } else if (type === 'video') { - videoFilesMouseHovering = false; - } - } - - accordionOpened(type) { - if (type === 'audio') { - audioFilesOpened = true; - } else if (type === 'video') { - videoFilesOpened = true; - } - } - - accordionClosed(type) { - if (type === 'audio') { - audioFilesOpened = false; - } else if (type === 'video') { - videoFilesOpened = false; - } - } - // modify custom args - openArgsModifierDialog() { + openArgsModifierDialog(): void { const dialogRef = this.dialog.open(ArgModifierDialogComponent, { data: { initial_args: this.customArgs @@ -876,7 +779,7 @@ export class MainComponent implements OnInit { }); } - getCurrentDownload() { + getCurrentDownload(): void { if (!this.current_download) { return; } @@ -893,7 +796,7 @@ export class MainComponent implements OnInit { } else if (this.current_download['finished'] && this.current_download['error']) { this.downloadingfile = false; this.current_download = null; - this.openSnackBar('Download failed!', 'OK.'); + this.postsService.openSnackBar('Download failed!', 'OK.'); } } else { // console.log('failed to get new download'); @@ -901,7 +804,7 @@ export class MainComponent implements OnInit { }); } - reloadRecentVideos() { + reloadRecentVideos(): void { this.postsService.files_changed.next(true); } }