From 895c385d6b909f67704280e4f48b0659eb6b2b56 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sat, 18 Jun 2022 19:36:20 -0400 Subject: [PATCH 01/40] Updated version to 4.3 --- backend/consts.js | 2 +- chart/Chart.yaml | 2 +- package.json | 2 +- src/app/consts.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/consts.js b/backend/consts.js index 9bd2905..222d6bc 100644 --- a/backend/consts.js +++ b/backend/consts.js @@ -301,4 +301,4 @@ const YTDL_ARGS_WITH_VALUES = [ // we're using a Set here for performance exports.YTDL_ARGS_WITH_VALUES = new Set(YTDL_ARGS_WITH_VALUES); -exports.CURRENT_VERSION = 'v4.2'; +exports.CURRENT_VERSION = 'v4.3'; diff --git a/chart/Chart.yaml b/chart/Chart.yaml index a86aa60..aa4cac1 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "4.2" +appVersion: "4.3" diff --git a/package.json b/package.json index 8571420..07d79e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "youtube-dl-material", - "version": "4.2.0", + "version": "4.3.0", "license": "MIT", "scripts": { "ng": "ng", diff --git a/src/app/consts.ts b/src/app/consts.ts index 950aff6..013e146 100644 --- a/src/app/consts.ts +++ b/src/app/consts.ts @@ -1 +1 @@ -export const CURRENT_VERSION = 'v4.2'; +export const CURRENT_VERSION = 'v4.3'; From aca86e022879c60dcf5246acf21920ae04b83b94 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sat, 18 Jun 2022 20:09:11 -0400 Subject: [PATCH 02/40] Added test for ID3 tagging --- backend/test/tests.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/test/tests.js b/backend/test/tests.js index 448db7b..c87045a 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -39,6 +39,7 @@ const utils = require('../utils'); const subscriptions_api = require('../subscriptions'); const fs = require('fs-extra'); const { uuid } = require('uuidv4'); +const NodeID3 = require('node-id3'); db_api.initialize(db, users_db); @@ -399,6 +400,19 @@ describe('Downloader', function() { }); + it('Tag file', async function() { + const audio_path = './test/sample.mp3'; + const sample_json = fs.readJSONSync('./test/sample.info.json'); + const tags = { + title: sample_json['title'], + artist: sample_json['artist'] ? sample_json['artist'] : sample_json['uploader'], + TRCK: '27' + } + NodeID3.write(tags, audio_path); + const written_tags = NodeID3.read(audio_path); + assert(written_tags['raw']['TRCK'] === '27'); + }); + it('Queue file', async function() { this.timeout(300000); const returned_download = await downloader_api.createDownload(url, 'video', options); From 0dd617b4381753e2251b6b5f454e8563e696717c Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sat, 18 Jun 2022 20:11:47 -0400 Subject: [PATCH 03/40] Updated translation source file --- src/assets/i18n/messages.en.xlf | 256 +++++++++++++++++++++++--------- 1 file changed, 185 insertions(+), 71 deletions(-) diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 95f5ddc..1d963b3 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -44,6 +44,10 @@ src/app/app.component.html 44 + + src/app/components/login/login.component.html + 3 + src/app/components/login/login.component.html 34 @@ -176,10 +180,6 @@ src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html 84,85 - - src/app/dialogs/confirm-dialog/confirm-dialog.component.html - 16 - src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html 54 @@ -254,13 +254,13 @@ Resume all downloads - - Clear finished downloads + + Clear downloads src/app/components/downloads/downloads.component.html 85 - Clear finished downloads + Clear downloads No downloads available! @@ -298,57 +298,136 @@ 61 - - Clear finished downloads + + Clear downloads src/app/components/downloads/downloads.component.ts - 129 + 130 - - Would you like to clear your finished downloads? + + Select downloads to clear src/app/components/downloads/downloads.component.ts - 130 + 131 Clear src/app/components/downloads/downloads.component.ts - 131 + 132 + + + + Finished downloads + + src/app/components/downloads/downloads.component.ts + 137 + + + + Paused downloads + + src/app/components/downloads/downloads.component.ts + 141 + + + + Errored downloads + + src/app/components/downloads/downloads.component.ts + 145 + + + + Failed to clear finished downloads! + + src/app/components/downloads/downloads.component.ts + 156 + + + + Cleared downloads! + + src/app/components/downloads/downloads.component.ts + 158 Error for src/app/components/downloads/downloads.component.ts - 238 + 258 Copy to clipboard src/app/components/downloads/downloads.component.ts - 240 + 260 Close src/app/components/downloads/downloads.component.ts - 241 + 261 Copied to clipboard! src/app/components/downloads/downloads.component.ts - 249 + 269 + + + + User name + + src/app/components/login/login.component.html + 6 + + + src/app/components/login/login.component.html + 18 + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 6 + + User name + + + Password + + src/app/components/login/login.component.html + 11 + + + src/app/components/login/login.component.html + 23 + + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 11 + + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 10 + + + src/app/main/main.component.html + 154,156 + + Password Register + + src/app/components/login/login.component.html + 15 + src/app/components/login/login.component.html 38 @@ -359,6 +438,14 @@ Register + + Confirm Password + + src/app/components/login/login.component.html + 28 + + Confirm Password + Lines: @@ -447,7 +534,7 @@ src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 35 + 62 Close @@ -553,6 +640,10 @@ src/app/dialogs/modify-playlist/modify-playlist.component.html 43 + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 63 + src/app/settings/settings.component.html 489 @@ -1000,6 +1091,10 @@ src/app/dialogs/modify-playlist/modify-playlist.component.html 8 + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 8 + Playlist name placeholder @@ -1174,30 +1269,6 @@ Register user dialog title - - User name - - src/app/dialogs/add-user-dialog/add-user-dialog.component.html - 6 - - User name placeholder - - - Password - - src/app/dialogs/add-user-dialog/add-user-dialog.component.html - 11 - - - src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html - 10 - - - src/app/main/main.component.html - 154,156 - - Password placeholder - Modify youtube-dl args @@ -1262,6 +1333,13 @@ Arg modifier modify button + + Cancel + + src/app/dialogs/confirm-dialog/confirm-dialog.component.ts + 15 + + Upload new cookies @@ -1632,6 +1710,10 @@ src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html 8 + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 11 + src/app/settings/settings.component.html 10 @@ -1676,10 +1758,6 @@ src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html 9 - - src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 9 - Subscription URL property @@ -1746,6 +1824,14 @@ Recurring + + Choose a date + + src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.html + 22 + + Choose a date + Update @@ -1768,10 +1854,6 @@ src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html 6 - - src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 5 - Name @@ -1806,45 +1888,77 @@ Logout - - Uploader: + + Uploader src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 13 + 17 - Video ID property + Uploader - - File size: + + Upload date src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 17 + 20 - Video file size property + Upload date - - Path: + + Thumbnail path src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 21 + 26 - Video path property + Thumbnail path - - Upload Date: + + Thumbnail URL src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 25 + 29 - Video upload date property + Thumbnail URL - - Category: + + Category src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 29 + 32 + + Category + + + View count + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 42 - Category property + View count + + + Local view count + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 45 + + Local view count + + + File size: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 51 + + Video file size property + + + Path: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 55 + + Video path property Quality From b5ee0d365c842efaf2d46b01b2a2e859b97dfdcd Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Jun 2022 01:14:59 -0400 Subject: [PATCH 04/40] Subscription file cards are now replaced with unified file cards GetAllFiles can now filter by sub_id Improved API models and added request body docs for GetAllFiles --- Public API v1.yaml | 40 ++++++++ backend/app.js | 14 ++- src/api-types/index.ts | 3 + src/api-types/models/FileTypeFilter.ts | 9 ++ src/api-types/models/GetAllFilesRequest.ts | 20 ++++ src/api-types/models/Sort.ts | 14 +++ src/app/app.module.ts | 2 - .../recent-videos.component.html | 6 +- .../recent-videos/recent-videos.component.ts | 11 ++- src/app/posts.services.ts | 10 +- .../subscription-file-card.component.html | 20 ---- .../subscription-file-card.component.scss | 76 -------------- .../subscription-file-card.component.spec.ts | 25 ----- .../subscription-file-card.component.ts | 98 ------------------- .../subscription/subscription.component.html | 33 +------ .../subscription/subscription.component.ts | 29 ++---- src/assets/i18n/messages.en.xlf | 20 ++-- 17 files changed, 126 insertions(+), 304 deletions(-) create mode 100644 src/api-types/models/FileTypeFilter.ts create mode 100644 src/api-types/models/GetAllFilesRequest.ts create mode 100644 src/api-types/models/Sort.ts delete mode 100644 src/app/subscription/subscription-file-card/subscription-file-card.component.html delete mode 100644 src/app/subscription/subscription-file-card/subscription-file-card.component.scss delete mode 100644 src/app/subscription/subscription-file-card/subscription-file-card.component.spec.ts delete mode 100644 src/app/subscription/subscription-file-card/subscription-file-card.component.ts diff --git a/Public API v1.yaml b/Public API v1.yaml index f554398..0931cf6 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -97,6 +97,11 @@ paths: summary: Get all files description: Gets all files and playlists stored in the db operationId: get-getAllFiles + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GetAllFilesRequest' responses: '200': description: OK @@ -1724,6 +1729,41 @@ components: description: All video playlists items: $ref: '#/components/schemas/Playlist' + GetAllFilesRequest: + type: object + properties: + sort: + $ref: '#/components/schemas/Sort' + range: + type: array + items: + type: number + description: Two elements allowed, start index and end index + minItems: 2 + maxItems: 2 + text_search: + type: string + description: Filter files by title + file_type_filter: + $ref: '#/components/schemas/FileTypeFilter' + sub_id: + type: string + description: Include if you want to filter by subscription + Sort: + type: object + properties: + by: + type: string + description: Property to sort by + order: + type: number + description: 1 for ascending, -1 for descending + FileTypeFilter: + type: string + enum: + - audio_only + - video_only + - both GetAllFilesResponse: required: - files diff --git a/backend/app.js b/backend/app.js index a508702..4c57139 100644 --- a/backend/app.js +++ b/backend/app.js @@ -912,11 +912,11 @@ app.post('/api/getFile', optionalJwt, async function (req, res) { app.post('/api/getAllFiles', optionalJwt, async function (req, res) { // these are returned let files = null; - let playlists = null; - let sort = req.body.sort; - let range = req.body.range; - let text_search = req.body.text_search; - let file_type_filter = req.body.file_type_filter; + const sort = req.body.sort; + const range = req.body.range; + const text_search = req.body.text_search; + const file_type_filter = req.body.file_type_filter; + const sub_id = req.body.sub_id; const uuid = req.isAuthenticated() ? req.user.uid : null; const filter_obj = {user_uid: uuid}; @@ -929,6 +929,10 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) { } } + if (sub_id) { + filter_obj['sub_id'] = sub_id; + } + if (file_type_filter === 'audio_only') filter_obj['isAudio'] = true; else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false; diff --git a/src/api-types/index.ts b/src/api-types/index.ts index db5be04..973aeca 100644 --- a/src/api-types/index.ts +++ b/src/api-types/index.ts @@ -40,11 +40,13 @@ export type { DownloadTwitchChatByVODIDRequest } from './models/DownloadTwitchCh export type { DownloadTwitchChatByVODIDResponse } from './models/DownloadTwitchChatByVODIDResponse'; export type { DownloadVideosForSubscriptionRequest } from './models/DownloadVideosForSubscriptionRequest'; export { FileType } from './models/FileType'; +export { FileTypeFilter } from './models/FileTypeFilter'; export type { GenerateArgsResponse } from './models/GenerateArgsResponse'; export type { GenerateNewApiKeyResponse } from './models/GenerateNewApiKeyResponse'; export type { GetAllCategoriesResponse } from './models/GetAllCategoriesResponse'; export type { GetAllDownloadsRequest } from './models/GetAllDownloadsRequest'; export type { GetAllDownloadsResponse } from './models/GetAllDownloadsResponse'; +export type { GetAllFilesRequest } from './models/GetAllFilesRequest'; export type { GetAllFilesResponse } from './models/GetAllFilesResponse'; export type { GetAllSubscriptionsResponse } from './models/GetAllSubscriptionsResponse'; export type { GetAllTasksResponse } from './models/GetAllTasksResponse'; @@ -82,6 +84,7 @@ export type { RestoreDBBackupRequest } from './models/RestoreDBBackupRequest'; export { Schedule } from './models/Schedule'; export type { SetConfigRequest } from './models/SetConfigRequest'; export type { SharingToggle } from './models/SharingToggle'; +export type { Sort } from './models/Sort'; export type { SubscribeRequest } from './models/SubscribeRequest'; export type { SubscribeResponse } from './models/SubscribeResponse'; export type { Subscription } from './models/Subscription'; diff --git a/src/api-types/models/FileTypeFilter.ts b/src/api-types/models/FileTypeFilter.ts new file mode 100644 index 0000000..d1fac98 --- /dev/null +++ b/src/api-types/models/FileTypeFilter.ts @@ -0,0 +1,9 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export enum FileTypeFilter { + AUDIO_ONLY = 'audio_only', + VIDEO_ONLY = 'video_only', + BOTH = 'both', +} \ No newline at end of file diff --git a/src/api-types/models/GetAllFilesRequest.ts b/src/api-types/models/GetAllFilesRequest.ts new file mode 100644 index 0000000..0acd1c3 --- /dev/null +++ b/src/api-types/models/GetAllFilesRequest.ts @@ -0,0 +1,20 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { FileTypeFilter } from './FileTypeFilter'; +import type { Sort } from './Sort'; + +export type GetAllFilesRequest = { + sort?: Sort; + range?: Array; + /** + * Filter files by title + */ + text_search?: string; + file_type_filter?: FileTypeFilter; + /** + * Include if you want to filter by subscription + */ + sub_id?: string; +}; \ No newline at end of file diff --git a/src/api-types/models/Sort.ts b/src/api-types/models/Sort.ts new file mode 100644 index 0000000..438f884 --- /dev/null +++ b/src/api-types/models/Sort.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Sort = { + /** + * Property to sort by + */ + by?: string; + /** + * 1 for ascending, -1 for descending + */ + order?: number; +}; \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a9e2ad4..238e9e8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -49,7 +49,6 @@ import { CreatePlaylistComponent } from './create-playlist/create-playlist.compo import { SubscriptionsComponent } from './subscriptions/subscriptions.component'; import { SubscribeDialogComponent } from './dialogs/subscribe-dialog/subscribe-dialog.component'; import { SubscriptionComponent } from './subscription//subscription/subscription.component'; -import { SubscriptionFileCardComponent } from './subscription/subscription-file-card/subscription-file-card.component'; import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component'; import { SettingsComponent } from './settings/settings.component'; import { MatChipsModule } from '@angular/material/chips'; @@ -102,7 +101,6 @@ registerLocaleData(es, 'es'); SubscriptionsComponent, SubscribeDialogComponent, SubscriptionComponent, - SubscriptionFileCardComponent, SubscriptionInfoDialogComponent, SettingsComponent, AboutDialogComponent, diff --git a/src/app/components/recent-videos/recent-videos.component.html b/src/app/components/recent-videos/recent-videos.component.html index fa2ba2a..1f83685 100644 --- a/src/app/components/recent-videos/recent-videos.component.html +++ b/src/app/components/recent-videos/recent-videos.component.html @@ -17,7 +17,7 @@
-

My videos

+

My files

@@ -35,7 +35,7 @@
- No videos found. + No files found.
@@ -46,7 +46,7 @@ -
+
File type diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 76e297f..11f8713 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -1,7 +1,7 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { PostsService } from 'app/posts.services'; import { Router } from '@angular/router'; -import { FileType } from '../../../api-types'; +import { FileType, FileTypeFilter } from '../../../api-types'; import { MatPaginator } from '@angular/material/paginator'; import { Subject } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; @@ -13,6 +13,9 @@ import { distinctUntilChanged } from 'rxjs/operators'; }) export class RecentVideosComponent implements OnInit { + @Input() usePaginator = true; + @Input() sub_id = null; + cached_file_count = 0; loading_files = null; @@ -104,7 +107,7 @@ export class RecentVideosComponent implements OnInit { // set file type filter to cached value const cached_file_type_filter = localStorage.getItem('file_type_filter'); - if (cached_file_type_filter) { + if (this.usePaginator && cached_file_type_filter) { this.fileTypeFilter = cached_file_type_filter; } @@ -163,7 +166,7 @@ export class RecentVideosComponent implements OnInit { const current_file_index = (this.paginator?.pageIndex ? this.paginator.pageIndex : 0)*this.pageSize; const sort = {by: this.filterProperty['property'], order: this.descendingMode ? -1 : 1}; const range = [current_file_index, current_file_index + this.pageSize]; - this.postsService.getAllFiles(sort, range, this.search_mode ? this.search_text : null, this.fileTypeFilter).subscribe(res => { + this.postsService.getAllFiles(sort, range, this.search_mode ? this.search_text : null, this.fileTypeFilter as FileTypeFilter, this.sub_id).subscribe(res => { this.file_count = res['file_count']; this.paged_data = res['files']; for (let i = 0; i < this.paged_data.length; i++) { diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 186418a..5ec95d9 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -97,7 +97,10 @@ import { Schedule, ClearDownloadsRequest, Category, - UpdateFileRequest + UpdateFileRequest, + Sort, + FileTypeFilter, + GetAllFilesRequest } from '../api-types'; import { isoLangs } from './settings/locales_list'; import { Title } from '@angular/platform-browser'; @@ -355,8 +358,9 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getFile', body, this.httpOptions); } - getAllFiles(sort, range, text_search, file_type_filter) { - return this.http.post(this.path + 'getAllFiles', {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter}, this.httpOptions); + getAllFiles(sort: Sort, range: number[], text_search: string, file_type_filter: FileTypeFilter, sub_id: string) { + const body: GetAllFilesRequest = {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter, sub_id: sub_id}; + return this.http.post(this.path + 'getAllFiles', body, this.httpOptions); } updateFile(uid: string, change_obj: Object) { diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.html b/src/app/subscription/subscription-file-card/subscription-file-card.component.html deleted file mode 100644 index 81897db..0000000 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
-
- Length: {{formattedDuration}} -
- - - - - - - -
-
- Thumbnail -
- - {{file.title}} -
-
-
diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.scss b/src/app/subscription/subscription-file-card/subscription-file-card.component.scss deleted file mode 100644 index 65cd869..0000000 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.scss +++ /dev/null @@ -1,76 +0,0 @@ -.example-card { - width: 200px; - height: 200px; - padding: 0px; - cursor: pointer; - } - - .menuButton { - right: 0px; - top: -1px; - position: absolute; - z-index: 999; - - } - - /* Coerce the icon container away from display:inline */ - .mat-icon-button .mat-button-wrapper { - display: flex; - justify-content: center; - } - - .image { - width: 200px; - height: 112.5px; - object-fit: cover; - } - - .example-full-width-height { - width: 100%; - height: 100% - } - - .centered { - margin: 0 auto; - top: 50%; - left: 50%; - } - - .img-div { - max-height: 80px; - padding: 0px; - margin: 32px 0px 0px -5px; - width: calc(100% + 5px + 5px); - } - - .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; - bottom: 5px; - position: absolute; - } - - .duration-time { - position: absolute; - left: 5px; - top: 5px; - z-index: 99999; - } - - @media (max-width: 576px){ - - .example-card { - width: 175px !important; - } - - .image { - width: 175px; - } - - } diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.spec.ts b/src/app/subscription/subscription-file-card/subscription-file-card.component.spec.ts deleted file mode 100644 index 42facb5..0000000 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { SubscriptionFileCardComponent } from './subscription-file-card.component'; - -describe('SubscriptionFileCardComponent', () => { - let component: SubscriptionFileCardComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ SubscriptionFileCardComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SubscriptionFileCardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.ts b/src/app/subscription/subscription-file-card/subscription-file-card.component.ts deleted file mode 100644 index 2257fd8..0000000 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { Router } from '@angular/router'; -import { PostsService } from 'app/posts.services'; -import { MatDialog } from '@angular/material/dialog'; -import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component'; - -@Component({ - selector: 'app-subscription-file-card', - templateUrl: './subscription-file-card.component.html', - styleUrls: ['./subscription-file-card.component.scss'] -}) -export class SubscriptionFileCardComponent implements OnInit { - image_errored = false; - image_loaded = false; - - formattedDuration = null; - - @Input() file; - @Input() sub; - @Input() use_youtubedl_archive = false; - - @Output() goToFileEmit = new EventEmitter(); - @Output() reloadSubscription = new EventEmitter(); - - constructor(private snackBar: MatSnackBar, private postsService: PostsService, private dialog: MatDialog) {} - - ngOnInit() { - if (this.file.duration) { - this.formattedDuration = fancyTimeFormat(this.file.duration); - } - } - - onImgError(event) { - this.image_errored = true; - } - - imageLoaded(loaded) { - this.image_loaded = true; - } - - goToFile() { - const emit_obj = { - uid: this.file.uid, - url: this.file.requested_formats ? this.file.requested_formats[0].url : this.file.url - } - this.goToFileEmit.emit(emit_obj); - } - - openSubscriptionInfoDialog() { - const dialogRef = this.dialog.open(VideoInfoDialogComponent, { - data: { - file: this.file, - }, - minWidth: '50vw' - }); - } - - deleteAndRedownload() { - this.postsService.deleteSubscriptionFile(this.sub, this.file.id, false, this.file.uid).subscribe(res => { - this.reloadSubscription.emit(true); - this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.'); - }); - } - - deleteForever() { - this.postsService.deleteSubscriptionFile(this.sub, this.file.id, true, this.file.uid).subscribe(res => { - this.reloadSubscription.emit(true); - this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.'); - }); - } - - public openSnackBar(message: string, action: string) { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - -} - -function fancyTimeFormat(time) { - // Hours, minutes and seconds - const hrs = ~~(time / 3600); - const mins = ~~((time % 3600) / 60); - const secs = ~~time % 60; - - // Output like "1:01" or "4:03:59" or "123:03:59" - let ret = ''; - - if (hrs > 0) { - ret += '' + hrs + ':' + (mins < 10 ? '0' : ''); - } - - ret += '' + mins + ':' + (secs < 10 ? '0' : ''); - ret += '' + secs; - return ret; -} diff --git a/src/app/subscription/subscription/subscription.component.html b/src/app/subscription/subscription/subscription.component.html index 892108a..586879c 100644 --- a/src/app/subscription/subscription/subscription.component.html +++ b/src/app/subscription/subscription/subscription.component.html @@ -10,38 +10,7 @@
-
-
-
- - - {{filterOption['value']['label']}} - - -
-
- -
-
-
-
-
-

Videos

-
-
- - - search - -
-
-
-
-
- -
-
-
+
diff --git a/src/app/subscription/subscription/subscription.component.ts b/src/app/subscription/subscription/subscription.component.ts index 967b5bd..09baef2 100644 --- a/src/app/subscription/subscription/subscription.component.ts +++ b/src/app/subscription/subscription/subscription.component.ts @@ -100,22 +100,12 @@ export class SubscriptionComponent implements OnInit, OnDestroy { }); } - getConfig() { + getConfig(): void { this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive']; } - goToFile(emit_obj) { - const uid = emit_obj['uid']; - const url = emit_obj['url']; - localStorage.setItem('player_navigator', this.router.url); - if (this.subscription.streamingOnly) { - this.router.navigate(['/player', {uid: uid, url: url}]); - } else { - this.router.navigate(['/player', {uid: uid}]); - } - } - onSearchInputChanged(newvalue) { + onSearchInputChanged(newvalue: string): void { if (newvalue.length > 0) { this.search_mode = true; this.filterFiles(newvalue); @@ -129,7 +119,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy { this.filtered_files = this.files.filter(option => option.id.toLowerCase().includes(filterValue)); } - filterByProperty(prop) { + filterByProperty(prop: string): void { if (this.descendingMode) { this.filtered_files = this.filtered_files.sort((a, b) => (a[prop] > b[prop] ? -1 : 1)); } else { @@ -142,17 +132,12 @@ export class SubscriptionComponent implements OnInit, OnDestroy { localStorage.setItem('filter_property', value['key']); } - toggleModeChange() { + toggleModeChange(): void { this.descendingMode = !this.descendingMode; this.filterByProperty(this.filterProperty['property']); } - downloadContent() { - const fileNames = []; - for (let i = 0; i < this.files.length; i++) { - fileNames.push(this.files[i].path); - } - + downloadContent(): void { this.downloading = true; this.postsService.downloadSubFromServer(this.subscription.id).subscribe(res => { this.downloading = false; @@ -164,7 +149,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy { }); } - editSubscription() { + editSubscription(): void { this.dialog.open(EditSubscriptionDialogComponent, { data: { sub: this.postsService.getSubscriptionByID(this.subscription.id) @@ -172,7 +157,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy { }); } - watchSubscription() { + watchSubscription(): void { this.router.navigate(['/player', {sub_id: this.subscription.id}]) } diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 1d963b3..8eff8b7 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -592,10 +592,6 @@ src/app/components/recent-videos/recent-videos.component.html 24 - - src/app/subscription/subscription/subscription.component.html - 33 - search field description @@ -682,21 +678,21 @@ Edit role - - My videos + + My files src/app/components/recent-videos/recent-videos.component.html 20 - My videos title + My files title - - No videos found. + + No files found. src/app/components/recent-videos/recent-videos.component.html 38 - No videos found + No files found File type @@ -1135,10 +1131,6 @@ src/app/create-playlist/create-playlist.component.html 20 - - src/app/subscription/subscription/subscription.component.html - 29 - Videos title From 690cc38899e2e3dff3a077e500b492660870e1d6 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Jun 2022 23:09:30 -0400 Subject: [PATCH 05/40] Updated playlist file selection to use recent videos component Playlists are now file type agnostic Updated translations --- Public API v1.yaml | 14 ++-- backend/app.js | 4 +- backend/db.js | 3 +- backend/subscriptions.js | 2 +- src/api-types/models/CreatePlaylistRequest.ts | 3 - src/api-types/models/DeletePlaylistRequest.ts | 3 - src/api-types/models/GetPlaylistResponse.ts | 7 +- .../custom-playlists.component.ts | 21 +++--- .../recent-videos.component.html | 30 +++++++- .../recent-videos.component.scss | 10 +++ .../recent-videos/recent-videos.component.ts | 28 ++++++- .../unified-file-card.component.html | 2 +- .../create-playlist.component.html | 26 +------ .../create-playlist.component.ts | 54 ++++---------- .../modify-playlist.component.ts | 12 +-- src/app/main/main.component.html | 2 +- src/app/main/main.component.ts | 15 +--- src/app/posts.services.ts | 9 +-- src/assets/i18n/messages.en.xlf | 74 ++----------------- 19 files changed, 121 insertions(+), 198 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 0931cf6..464871f 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -1939,7 +1939,6 @@ components: - uids - playlistName - thumbnailURL - - type type: object properties: playlistName: @@ -1948,8 +1947,6 @@ components: type: array items: type: string - type: - $ref: '#/components/schemas/FileType' thumbnailURL: type: string CreatePlaylistResponse: @@ -1979,15 +1976,17 @@ components: required: - playlist - success - - type type: object properties: playlist: $ref: '#/components/schemas/Playlist' - type: - $ref: '#/components/schemas/FileType' success: type: boolean + file_objs: + type: array + description: File objects for every uid in the playlist's uids property, in the same order + items: + $ref: '#/components/schemas/DatabaseFile' GetPlaylistsRequest: type: object properties: @@ -2012,13 +2011,10 @@ components: DeletePlaylistRequest: required: - playlist_id - - type type: object properties: playlist_id: type: string - type: - $ref: '#/components/schemas/FileType' DownloadFileRequest: type: object properties: diff --git a/backend/app.js b/backend/app.js index 4c57139..322bb44 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1339,9 +1339,8 @@ app.post('/api/getSubscriptions', optionalJwt, async (req, res) => { app.post('/api/createPlaylist', optionalJwt, async (req, res) => { let playlistName = req.body.playlistName; let uids = req.body.uids; - let type = req.body.type; - const new_playlist = await db_api.createPlaylist(playlistName, uids, type, req.isAuthenticated() ? req.user.uid : null); + const new_playlist = await db_api.createPlaylist(playlistName, uids, req.isAuthenticated() ? req.user.uid : null); res.send({ new_playlist: new_playlist, @@ -1369,7 +1368,6 @@ app.post('/api/getPlaylist', optionalJwt, async (req, res) => { res.send({ playlist: playlist, file_objs: file_objs, - type: playlist && playlist.type, success: !!playlist }); }); diff --git a/backend/db.js b/backend/db.js index 73f56f1..e35ddca 100644 --- a/backend/db.js +++ b/backend/db.js @@ -357,7 +357,7 @@ exports.addMetadataPropertyToDB = async (property_key) => { } } -exports.createPlaylist = async (playlist_name, uids, type, user_uid = null) => { +exports.createPlaylist = async (playlist_name, uids, user_uid = null) => { const first_video = await exports.getVideo(uids[0]); const thumbnailToUse = first_video['thumbnailURL']; @@ -366,7 +366,6 @@ exports.createPlaylist = async (playlist_name, uids, type, user_uid = null) => { uids: uids, id: uuid(), thumbnailURL: thumbnailToUse, - type: type, registered: Date.now(), randomize_order: false }; diff --git a/backend/subscriptions.js b/backend/subscriptions.js index e14f121..bad2064 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -178,7 +178,7 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null, ]); if (jsonExists) { - retrievedID = JSON.parse(await fs.readFile(jsonPath, 'utf8'))['id']; + retrievedID = fs.readJSONSync(jsonPath)['id']; await fs.unlink(jsonPath); } diff --git a/src/api-types/models/CreatePlaylistRequest.ts b/src/api-types/models/CreatePlaylistRequest.ts index c747d8d..e61a2a0 100644 --- a/src/api-types/models/CreatePlaylistRequest.ts +++ b/src/api-types/models/CreatePlaylistRequest.ts @@ -2,11 +2,8 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; - export type CreatePlaylistRequest = { playlistName: string; uids: Array; - type: FileType; thumbnailURL: string; }; \ No newline at end of file diff --git a/src/api-types/models/DeletePlaylistRequest.ts b/src/api-types/models/DeletePlaylistRequest.ts index 07cf9f7..82e25b2 100644 --- a/src/api-types/models/DeletePlaylistRequest.ts +++ b/src/api-types/models/DeletePlaylistRequest.ts @@ -2,9 +2,6 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; - export type DeletePlaylistRequest = { playlist_id: string; - type: FileType; }; \ No newline at end of file diff --git a/src/api-types/models/GetPlaylistResponse.ts b/src/api-types/models/GetPlaylistResponse.ts index daca2d4..14ff0c0 100644 --- a/src/api-types/models/GetPlaylistResponse.ts +++ b/src/api-types/models/GetPlaylistResponse.ts @@ -2,11 +2,14 @@ /* tslint:disable */ /* eslint-disable */ -import type { FileType } from './FileType'; +import type { DatabaseFile } from './DatabaseFile'; import type { Playlist } from './Playlist'; export type GetPlaylistResponse = { playlist: Playlist; - type: FileType; success: boolean; + /** + * File objects for every uid in the playlist's uids property, in the same order + */ + file_objs?: Array; }; \ No newline at end of file diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index e659724..86a76ee 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -4,6 +4,7 @@ import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.component'; import { ModifyPlaylistComponent } from 'app/dialogs/modify-playlist/modify-playlist.component'; +import { Playlist } from 'api-types'; @Component({ selector: 'app-custom-playlists', @@ -32,7 +33,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - getAllPlaylists() { + getAllPlaylists(): void { this.playlists_received = false; // must call getAllFiles as we need to get category playlists as well this.postsService.getPlaylists(true).subscribe(res => { @@ -42,10 +43,10 @@ export class CustomPlaylistsComponent implements OnInit { } // creating a playlist - openCreatePlaylistDialog() { + openCreatePlaylistDialog(): void { const dialogRef = this.dialog.open(CreatePlaylistComponent, { - data: { - } + minWidth: '90vw', + minHeight: '95vh' }); dialogRef.afterClosed().subscribe(result => { if (result) { @@ -57,7 +58,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - goToPlaylist(info_obj) { + goToPlaylist(info_obj: { file: Playlist; }): void { const playlist = info_obj.file; const playlistID = playlist.id; @@ -76,7 +77,7 @@ export class CustomPlaylistsComponent implements OnInit { } } - downloadPlaylist(playlist_id, playlist_name) { + downloadPlaylist(playlist_id: string, playlist_name: string): void { this.downloading_content[playlist_id] = true; this.postsService.downloadPlaylistFromServer(playlist_id).subscribe(res => { this.downloading_content[playlist_id] = false; @@ -86,11 +87,11 @@ export class CustomPlaylistsComponent implements OnInit { } - deletePlaylist(args) { + deletePlaylist(args: { file: Playlist; index: number; }): void { const playlist = args.file; const index = args.index; const playlistID = playlist.id; - this.postsService.removePlaylist(playlistID, playlist.type).subscribe(res => { + this.postsService.removePlaylist(playlistID).subscribe(res => { if (res['success']) { this.playlists.splice(index, 1); this.postsService.openSnackBar('Playlist successfully removed.', ''); @@ -99,7 +100,7 @@ export class CustomPlaylistsComponent implements OnInit { }); } - editPlaylistDialog(args) { + editPlaylistDialog(args: { playlist: Playlist; index: number; }): void { const playlist = args.playlist; const index = args.index; const dialogRef = this.dialog.open(ModifyPlaylistComponent, { @@ -109,7 +110,7 @@ export class CustomPlaylistsComponent implements OnInit { } }); - dialogRef.afterClosed().subscribe(res => { + dialogRef.afterClosed().subscribe(() => { // updates playlist in file manager if it changed if (dialogRef.componentInstance.playlist_updated) { this.playlists[index] = dialogRef.componentInstance.original_playlist; diff --git a/src/app/components/recent-videos/recent-videos.component.html b/src/app/components/recent-videos/recent-videos.component.html index 1f83685..5603378 100644 --- a/src/app/components/recent-videos/recent-videos.component.html +++ b/src/app/components/recent-videos/recent-videos.component.html @@ -17,7 +17,8 @@
-

My files

+

My files

+

{{customHeader}}

@@ -28,7 +29,7 @@
-
+
@@ -46,6 +47,31 @@
+
+ + +
+
+
+ {{(file.type === 'audio' || file.isAudio) ? 'audiotrack' : 'movie'}} + {{file.title}} +
+
{{file.registered | date:'shortDate'}}
+
+
+ +
+
+ + + + + + + + +
+
diff --git a/src/app/components/recent-videos/recent-videos.component.scss b/src/app/components/recent-videos/recent-videos.component.scss index 49023ec..b9fd2fb 100644 --- a/src/app/components/recent-videos/recent-videos.component.scss +++ b/src/app/components/recent-videos/recent-videos.component.scss @@ -61,4 +61,14 @@ .my-videos-title { top: 0px; } +} + +.list-ghosts { + position: relative; + top: 4px; +} + +.audio-video-icon { + position: relative; + top: 6px; } \ No newline at end of file diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 11f8713..7437524 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -1,7 +1,7 @@ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { PostsService } from 'app/posts.services'; import { Router } from '@angular/router'; -import { FileType, FileTypeFilter } from '../../../api-types'; +import { DatabaseFile, FileType, FileTypeFilter } from '../../../api-types'; import { MatPaginator } from '@angular/material/paginator'; import { Subject } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; @@ -14,7 +14,10 @@ import { distinctUntilChanged } from 'rxjs/operators'; export class RecentVideosComponent implements OnInit { @Input() usePaginator = true; + @Input() selectMode = false; @Input() sub_id = null; + @Input() customHeader = null; + @Output() fileSelectionEmitter = new EventEmitter<{new_selection: string[], thumbnailURL: string}>(); cached_file_count = 0; loading_files = null; @@ -61,7 +64,9 @@ export class RecentVideosComponent implements OnInit { playlists = null; pageSize = 10; - paged_data = null; + paged_data: DatabaseFile[] = null; + + selected_data: string[] = []; @ViewChild('paginator') paginator: MatPaginator @@ -358,4 +363,21 @@ export class RecentVideosComponent implements OnInit { this.loading_files = Array(this.pageSize).fill(0); this.getAllFiles(); } + + fileSelectionChanged(event): void { + const adding = event.option._selected; + const value = event.option.value; + if (adding) + this.selected_data.push(value); + else + this.selected_data = this.selected_data.filter(e => e !== value); + + let thumbnail_url = null; + if (this.selected_data.length) { + const file_obj = this.paged_data.find(file => file.uid === this.selected_data[0]); + if (file_obj) { thumbnail_url = file_obj['thumbnailURL'] } + } + + this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: thumbnail_url}); + } } diff --git a/src/app/components/unified-file-card/unified-file-card.component.html b/src/app/components/unified-file-card/unified-file-card.component.html index 1fef6d8..b14f9cd 100644 --- a/src/app/components/unified-file-card/unified-file-card.component.html +++ b/src/app/components/unified-file-card/unified-file-card.component.html @@ -23,7 +23,7 @@ - + diff --git a/src/app/create-playlist/create-playlist.component.html b/src/app/create-playlist/create-playlist.component.html index ad21ec0..d82c23e 100644 --- a/src/app/create-playlist/create-playlist.component.html +++ b/src/app/create-playlist/create-playlist.component.html @@ -1,34 +1,12 @@

Create a playlist

-
+
-
- - - Audio - Video - - -
-
- - Audio files - Videos - - {{file.id}} - {{file.id}} - {{file.id}} - - - -
- No files available. -
-
+
diff --git a/src/app/create-playlist/create-playlist.component.ts b/src/app/create-playlist/create-playlist.component.ts index c22d32d..6a5e308 100644 --- a/src/app/create-playlist/create-playlist.component.ts +++ b/src/app/create-playlist/create-playlist.component.ts @@ -17,42 +17,20 @@ export class CreatePlaylistComponent implements OnInit { audiosToSelectFrom = null; videosToSelectFrom = null; name = ''; + cached_thumbnail_url = null; create_in_progress = false; - constructor(@Inject(MAT_DIALOG_DATA) public data: any, - private postsService: PostsService, + constructor(private postsService: PostsService, public dialogRef: MatDialogRef) { } - ngOnInit() { - if (this.data) { - this.filesToSelectFrom = this.data.filesToSelectFrom; - this.type = this.data.type; - } + ngOnInit(): void {} - if (!this.filesToSelectFrom) { - this.getMp3s(); - this.getMp4s(); - } - } - - getMp3s() { - this.postsService.getMp3s().subscribe(result => { - this.audiosToSelectFrom = result['mp3s']; - }); - } - - getMp4s() { - this.postsService.getMp4s().subscribe(result => { - this.videosToSelectFrom = result['mp4s']; - }); - } - - createPlaylist() { + createPlaylist(): void { const thumbnailURL = this.getThumbnailURL(); this.create_in_progress = true; - this.postsService.createPlaylist(this.name, this.filesSelect.value, this.type, thumbnailURL).subscribe(res => { + this.postsService.createPlaylist(this.name, this.filesSelect.value, thumbnailURL).subscribe(res => { this.create_in_progress = false; if (res['success']) { this.dialogRef.close(true); @@ -62,19 +40,13 @@ export class CreatePlaylistComponent implements OnInit { }); } - getThumbnailURL() { - let properFilesToSelectFrom = this.filesToSelectFrom; - if (!this.filesToSelectFrom) { - properFilesToSelectFrom = this.type === 'audio' ? this.audiosToSelectFrom : this.videosToSelectFrom; - } - for (let i = 0; i < properFilesToSelectFrom.length; i++) { - const file = properFilesToSelectFrom[i]; - if (file.id === this.filesSelect.value[0]) { - // different services store the thumbnail in different places - if (file.thumbnailURL) { return file.thumbnailURL }; - if (file.thumbnail) { return file.thumbnail }; - } - } - return null; + getThumbnailURL(): string { + return this.cached_thumbnail_url; + } + + fileSelectionChanged({new_selection, thumbnailURL}: {new_selection: string[], thumbnailURL: string}): void { + this.filesSelect.setValue(new_selection); + if (new_selection.length) this.cached_thumbnail_url = thumbnailURL; + else this.cached_thumbnail_url = null; } } diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.ts b/src/app/dialogs/modify-playlist/modify-playlist.component.ts index 18a0f2a..17b2afd 100644 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.ts +++ b/src/app/dialogs/modify-playlist/modify-playlist.component.ts @@ -35,15 +35,9 @@ export class ModifyPlaylistComponent implements OnInit { } getFiles() { - if (this.playlist.type === 'audio') { - this.postsService.getMp3s().subscribe(res => { - this.processFiles(res['mp3s']); - }); - } else { - this.postsService.getMp4s().subscribe(res => { - this.processFiles(res['mp4s']); - }); - } + this.postsService.getAllFiles().subscribe(res => { + this.processFiles(res['files']); + }); } processFiles(new_files = null) { diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index e8d14a8..84f7e7f 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -60,7 +60,7 @@

- + Only Audio diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 69e5eb7..25719ad 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -10,12 +10,7 @@ import { Router, ActivatedRoute } from '@angular/router'; import { Platform } from '@angular/cdk/platform'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { RecentVideosComponent } from 'app/components/recent-videos/recent-videos.component'; -import { Download, FileType } from 'api-types'; - -export let audioFilesMouseHovering = false; -export let videoFilesMouseHovering = false; -export let audioFilesOpened = false; -export let videoFilesOpened = false; +import { DatabaseFile, Download, FileType, Playlist } from 'api-types'; @Component({ selector: 'app-root', @@ -78,7 +73,6 @@ export class MainComponent implements OnInit { mp4s: any[] = []; playlists = {'audio': [], 'video': []}; playlist_thumbnails = {}; - downloading_content = {'audio': {}, 'video': {}}; downloads: Download[] = []; download_uids: string[] = []; current_download: Download = null; @@ -466,11 +460,9 @@ export class MainComponent implements OnInit { } } - downloadFileFromServer(file, type: string): void { + downloadFileFromServer(file: DatabaseFile, type: string): void { const ext = type === 'audio' ? 'mp3' : 'mp4' - this.downloading_content[type][file.id] = true; this.postsService.downloadFileFromServer(file.uid).subscribe(res => { - this.downloading_content[type][file.id] = false; const blob: Blob = res; saveAs(blob, decodeURIComponent(file.id) + `.${ext}`); @@ -481,9 +473,8 @@ export class MainComponent implements OnInit { }); } - downloadPlaylist(playlist): void { + downloadPlaylist(playlist: Playlist): void { this.postsService.downloadPlaylistFromServer(playlist.id).subscribe(res => { - if (playlist.id) { this.downloading_content[playlist.type][playlist.id] = false }; const blob: Blob = res; saveAs(blob, playlist.name + '.zip'); }); diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 5ec95d9..6417f5c 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -358,7 +358,7 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getFile', body, this.httpOptions); } - getAllFiles(sort: Sort, range: number[], text_search: string, file_type_filter: FileTypeFilter, sub_id: string) { + getAllFiles(sort: Sort = null, range: number[] = null, text_search: string = null, file_type_filter: FileTypeFilter = FileTypeFilter.BOTH, sub_id: string = null) { const body: GetAllFilesRequest = {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter, sub_id: sub_id}; return this.http.post(this.path + 'getAllFiles', body, this.httpOptions); } @@ -447,10 +447,9 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'disableSharing', body, this.httpOptions); } - createPlaylist(playlistName: string, uids: string[], type: FileType, thumbnailURL: string) { + createPlaylist(playlistName: string, uids: string[], thumbnailURL: string) { const body: CreatePlaylistRequest = {playlistName: playlistName, uids: uids, - type: type, thumbnailURL: thumbnailURL}; return this.http.post(this.path + 'createPlaylist', body, this.httpOptions); } @@ -475,8 +474,8 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'updatePlaylist', body, this.httpOptions); } - removePlaylist(playlist_id: string, type: FileType) { - const body: DeletePlaylistRequest = {playlist_id: playlist_id, type: type}; + removePlaylist(playlist_id: string) { + const body: DeletePlaylistRequest = {playlist_id: playlist_id}; return this.http.post(this.path + 'deletePlaylist', body, this.httpOptions); } diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 8eff8b7..a24d0b6 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -590,7 +590,7 @@ src/app/components/recent-videos/recent-videos.component.html - 24 + 25 search field description @@ -690,7 +690,7 @@ No files found. src/app/components/recent-videos/recent-videos.component.html - 38 + 39 No files found @@ -698,7 +698,7 @@ File type src/app/components/recent-videos/recent-videos.component.html - 52 + 78 File type @@ -706,7 +706,7 @@ Both src/app/components/recent-videos/recent-videos.component.html - 54 + 80 Both @@ -714,7 +714,7 @@ Video only src/app/components/recent-videos/recent-videos.component.html - 55 + 81 Video only @@ -722,7 +722,7 @@ Audio only src/app/components/recent-videos/recent-videos.component.html - 56 + 82 Audio only @@ -991,10 +991,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 24 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 7 - Video info button @@ -1019,10 +1015,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 34 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 8 - Delete and redownload subscription video button @@ -1031,10 +1023,6 @@ src/app/components/unified-file-card/unified-file-card.component.html 37 - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 9 - Delete forever subscription video button @@ -1093,46 +1081,6 @@ Playlist name placeholder - - Type - - src/app/create-playlist/create-playlist.component.html - 11 - - Type select - - - Audio - - src/app/create-playlist/create-playlist.component.html - 12 - - Audio - - - Video - - src/app/create-playlist/create-playlist.component.html - 13 - - Video - - - Audio files - - src/app/create-playlist/create-playlist.component.html - 19 - - Audio files title - - - Videos - - src/app/create-playlist/create-playlist.component.html - 20 - - Videos title - About YoutubeDL-Material @@ -2108,7 +2056,7 @@ Download for has been queued! src/app/main/main.component.ts - 403 + 397 @@ -2906,14 +2854,6 @@ 48 - - Length: - - src/app/subscription/subscription-file-card/subscription-file-card.component.html - 3 - - Video duration label - Your subscriptions From 7f47fb339b95defcedc96c4a0eaf21da29d33ff1 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Jun 2022 23:46:24 -0400 Subject: [PATCH 06/40] Updated modify playlist size Fixed issue where playlist order could not be rearranged --- .../custom-playlists.component.ts | 6 ++-- .../modify-playlist.component.ts | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index 86a76ee..1c4aa3b 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -105,9 +105,9 @@ export class CustomPlaylistsComponent implements OnInit { const index = args.index; const dialogRef = this.dialog.open(ModifyPlaylistComponent, { data: { - playlist_id: playlist.id, - width: '65vw' - } + playlist_id: playlist.id + }, + minWidth: '85vw' }); dialogRef.afterClosed().subscribe(() => { diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.ts b/src/app/dialogs/modify-playlist/modify-playlist.component.ts index 17b2afd..ae53c16 100644 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.ts +++ b/src/app/dialogs/modify-playlist/modify-playlist.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { PostsService } from 'app/posts.services'; +import { DatabaseFile, Playlist } from 'api-types'; @Component({ selector: 'app-modify-playlist', @@ -10,11 +11,11 @@ import { PostsService } from 'app/posts.services'; }) export class ModifyPlaylistComponent implements OnInit { - playlist_id = null; + playlist_id: string = null; - original_playlist = null; - playlist = null; - playlist_file_objs = null; + original_playlist: Playlist = null; + playlist: Playlist = null; + playlist_file_objs: DatabaseFile[] = null; available_files = []; all_files = []; @@ -34,18 +35,18 @@ export class ModifyPlaylistComponent implements OnInit { this.reverse_order = localStorage.getItem('default_playlist_order_reversed') === 'true'; } - getFiles() { + getFiles(): void { this.postsService.getAllFiles().subscribe(res => { this.processFiles(res['files']); }); } - processFiles(new_files = null) { + processFiles(new_files: DatabaseFile[] = null): void { if (new_files) { this.all_files = new_files; } this.available_files = this.all_files.filter(e => !this.playlist_file_objs.includes(e)) } - updatePlaylist() { + updatePlaylist(): void { this.playlist['uids'] = this.playlist_file_objs.map(playlist_file_obj => playlist_file_obj['uid']) this.postsService.updatePlaylist(this.playlist).subscribe(res => { this.playlist_updated = true; @@ -55,11 +56,11 @@ export class ModifyPlaylistComponent implements OnInit { }); } - playlistChanged() { + playlistChanged(): boolean { return JSON.stringify(this.playlist) !== JSON.stringify(this.original_playlist); } - getPlaylist() { + getPlaylist(): void { this.postsService.getPlaylist(this.playlist_id, null, true).subscribe(res => { if (res['playlist']) { this.playlist = res['playlist']; @@ -70,13 +71,13 @@ export class ModifyPlaylistComponent implements OnInit { }); } - addContent(file) { + addContent(file: DatabaseFile): void { this.playlist_file_objs.push(file); this.playlist.uids.push(file.uid); this.processFiles(); } - removeContent(index) { + removeContent(index: number): void { if (this.reverse_order) { index = this.playlist_file_objs.length - 1 - index; } @@ -85,17 +86,18 @@ export class ModifyPlaylistComponent implements OnInit { this.processFiles(); } - togglePlaylistOrder() { + togglePlaylistOrder(): void { this.reverse_order = !this.reverse_order; localStorage.setItem('default_playlist_order_reversed', '' + this.reverse_order); } - drop(event: CdkDragDrop) { + drop(event: CdkDragDrop): void { if (this.reverse_order) { event.previousIndex = this.playlist_file_objs.length - 1 - event.previousIndex; event.currentIndex = this.playlist_file_objs.length - 1 - event.currentIndex; } moveItemInArray(this.playlist_file_objs, event.previousIndex, event.currentIndex); + this.playlist.uids = this.playlist_file_objs.map(file => file.uid); } } From e1cb56e8e9f8edcb04fbf48ab33af640ab8eff3a Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 20 Jun 2022 16:02:15 -0400 Subject: [PATCH 07/40] Unified create and modify playlist components --- .../custom-playlists.component.ts | 10 +- .../recent-videos.component.html | 62 +++++++---- .../recent-videos.component.scss | 38 +++++++ .../recent-videos/recent-videos.component.ts | 102 ++++++++++++------ .../create-playlist.component.html | 39 ++++--- .../create-playlist.component.scss | 9 ++ .../create-playlist.component.ts | 46 +++++++- 7 files changed, 235 insertions(+), 71 deletions(-) diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index 1c4aa3b..3dec80a 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -45,6 +45,9 @@ export class CustomPlaylistsComponent implements OnInit { // creating a playlist openCreatePlaylistDialog(): void { const dialogRef = this.dialog.open(CreatePlaylistComponent, { + data: { + create_mode: true + }, minWidth: '90vw', minHeight: '95vh' }); @@ -103,9 +106,10 @@ export class CustomPlaylistsComponent implements OnInit { editPlaylistDialog(args: { playlist: Playlist; index: number; }): void { const playlist = args.playlist; const index = args.index; - const dialogRef = this.dialog.open(ModifyPlaylistComponent, { + const dialogRef = this.dialog.open(CreatePlaylistComponent, { data: { - playlist_id: playlist.id + playlist_id: playlist.id, + create_mode: false }, minWidth: '85vw' }); @@ -113,7 +117,7 @@ export class CustomPlaylistsComponent implements OnInit { dialogRef.afterClosed().subscribe(() => { // updates playlist in file manager if it changed if (dialogRef.componentInstance.playlist_updated) { - this.playlists[index] = dialogRef.componentInstance.original_playlist; + this.playlists[index] = dialogRef.componentInstance.playlist; } }); } diff --git a/src/app/components/recent-videos/recent-videos.component.html b/src/app/components/recent-videos/recent-videos.component.html index 5603378..9741911 100644 --- a/src/app/components/recent-videos/recent-videos.component.html +++ b/src/app/components/recent-videos/recent-videos.component.html @@ -48,31 +48,53 @@
- - -
-
-
- {{(file.type === 'audio' || file.isAudio) ? 'audiotrack' : 'movie'}} - {{file.title}} -
-
{{file.registered | date:'shortDate'}}
-
+ + +
+ Normal order  + Reverse order  +
+ + + + +
{{file.title}}
+
- - +
+

No files selected!

+
- - - - - - - +
+ + + +
+
+
+ {{(file.type === 'audio' || file.isAudio) ? 'audiotrack' : 'movie'}} + {{file.title}} +
+
{{file.registered | date:'shortDate'}}
+
+
+ +
+
+ + + + + + + + +
+
-
+
File type diff --git a/src/app/components/recent-videos/recent-videos.component.scss b/src/app/components/recent-videos/recent-videos.component.scss index b9fd2fb..ab5e741 100644 --- a/src/app/components/recent-videos/recent-videos.component.scss +++ b/src/app/components/recent-videos/recent-videos.component.scss @@ -71,4 +71,42 @@ .audio-video-icon { position: relative; top: 6px; +} + +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.cdk-drag-placeholder { + opacity: 0; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.media-box:last-child { + border: none; +} + +.media-list.cdk-drop-list-dragging .media-box:not(.cdk-drag-placeholder) { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.remove-item-button { + right: 10px; + position: absolute; + top: 4px; +} + +.playlist-item-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 70%; + margin: 0 auto; } \ No newline at end of file diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 7437524..0463140 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -5,6 +5,7 @@ import { DatabaseFile, FileType, FileTypeFilter } from '../../../api-types'; import { MatPaginator } from '@angular/material/paginator'; import { Subject } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-recent-videos', @@ -14,11 +15,25 @@ import { distinctUntilChanged } from 'rxjs/operators'; export class RecentVideosComponent implements OnInit { @Input() usePaginator = true; + + // File selection + @Input() selectMode = false; + @Input() defaultSelected: DatabaseFile[] = []; @Input() sub_id = null; @Input() customHeader = null; + @Input() selectedIndex = 1; @Output() fileSelectionEmitter = new EventEmitter<{new_selection: string[], thumbnailURL: string}>(); + pageSize = 10; + paged_data: DatabaseFile[] = null; + + selected_data: string[] = []; + selected_data_objs: DatabaseFile[] = []; + reverse_order = false; + + // File listing (with cards) + cached_file_count = 0; loading_files = null; @@ -63,20 +78,32 @@ export class RecentVideosComponent implements OnInit { playlists = null; - pageSize = 10; - paged_data: DatabaseFile[] = null; - - selected_data: string[] = []; - @ViewChild('paginator') paginator: MatPaginator constructor(public postsService: PostsService, private router: Router) { // get cached file count if (localStorage.getItem('cached_file_count')) { this.cached_file_count = +localStorage.getItem('cached_file_count') <= 10 ? +localStorage.getItem('cached_file_count') : 10; - this.loading_files = Array(this.cached_file_count).fill(0); } + + // set filter property to cached value + const cached_filter_property = localStorage.getItem('filter_property'); + if (cached_filter_property && this.filterProperties[cached_filter_property]) { + this.filterProperty = this.filterProperties[cached_filter_property]; + } + + // set file type filter to cached value + const cached_file_type_filter = localStorage.getItem('file_type_filter'); + if (this.usePaginator && cached_file_type_filter) { + this.fileTypeFilter = cached_file_type_filter; + } + + const sort_order = localStorage.getItem('recent_videos_sort_order'); + + if (sort_order) { + this.descendingMode = sort_order === 'descending'; + } } ngOnInit(): void { @@ -104,23 +131,9 @@ export class RecentVideosComponent implements OnInit { } }); - // set filter property to cached value - const cached_filter_property = localStorage.getItem('filter_property'); - if (cached_filter_property && this.filterProperties[cached_filter_property]) { - this.filterProperty = this.filterProperties[cached_filter_property]; - } - - // set file type filter to cached value - const cached_file_type_filter = localStorage.getItem('file_type_filter'); - if (this.usePaginator && cached_file_type_filter) { - this.fileTypeFilter = cached_file_type_filter; - } - - const sort_order = localStorage.getItem('recent_videos_sort_order'); - - if (sort_order) { - this.descendingMode = sort_order === 'descending'; - } + + this.selected_data = this.defaultSelected.map(file => file.uid); + this.selected_data_objs = this.defaultSelected; this.searchChangedSubject .debounceTime(500) @@ -364,20 +377,41 @@ export class RecentVideosComponent implements OnInit { this.getAllFiles(); } - fileSelectionChanged(event): void { + fileSelectionChanged(event: { option: { _selected: boolean; value: DatabaseFile; } }): void { const adding = event.option._selected; const value = event.option.value; - if (adding) - this.selected_data.push(value); - else - this.selected_data = this.selected_data.filter(e => e !== value); - - let thumbnail_url = null; - if (this.selected_data.length) { - const file_obj = this.paged_data.find(file => file.uid === this.selected_data[0]); - if (file_obj) { thumbnail_url = file_obj['thumbnailURL'] } + if (adding) { + this.selected_data.push(value.uid); + this.selected_data_objs.push(value); + } else { + this.selected_data = this.selected_data.filter(e => e !== value.uid); + this.selected_data_objs = this.selected_data_objs.filter(e => e.uid !== value.uid); + } + + this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: this.selected_data_objs[0].thumbnailURL}); + } + + toggleSelectionOrder(): void { + this.reverse_order = !this.reverse_order; + localStorage.setItem('default_playlist_order_reversed', '' + this.reverse_order); + } + + drop(event: CdkDragDrop): void { + if (this.reverse_order) { + event.previousIndex = this.selected_data.length - 1 - event.previousIndex; + event.currentIndex = this.selected_data.length - 1 - event.currentIndex; } + moveItemInArray(this.selected_data, event.previousIndex, event.currentIndex); + moveItemInArray(this.selected_data_objs, event.previousIndex, event.currentIndex); + this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: this.selected_data_objs[0].thumbnailURL}); + } - this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: thumbnail_url}); + removeSelectedFile(index: number): void { + if (this.reverse_order) { + index = this.selected_data.length - 1 - index; + } + this.selected_data.splice(index, 1); + this.selected_data_objs.splice(index, 1); + this.fileSelectionEmitter.emit({new_selection: this.selected_data, thumbnailURL: this.selected_data_objs[0].thumbnailURL}); } } diff --git a/src/app/create-playlist/create-playlist.component.html b/src/app/create-playlist/create-playlist.component.html index d82c23e..cdc40ad 100644 --- a/src/app/create-playlist/create-playlist.component.html +++ b/src/app/create-playlist/create-playlist.component.html @@ -1,14 +1,29 @@ -

Create a playlist

-
-
-
- - - +
+

Create a playlist

+

Modify playlist

+ + + +
+
+ + + +
+
- -
- + + + +
-
- + +
+ + +
+
\ No newline at end of file diff --git a/src/app/create-playlist/create-playlist.component.scss b/src/app/create-playlist/create-playlist.component.scss index e69de29..82ffcce 100644 --- a/src/app/create-playlist/create-playlist.component.scss +++ b/src/app/create-playlist/create-playlist.component.scss @@ -0,0 +1,9 @@ +.fixActionRow { + height: 89vh; + display: flex; + flex-direction: column; +} + +.spacer { + flex-grow: 1; +} \ No newline at end of file diff --git a/src/app/create-playlist/create-playlist.component.ts b/src/app/create-playlist/create-playlist.component.ts index 6a5e308..7ec41b0 100644 --- a/src/app/create-playlist/create-playlist.component.ts +++ b/src/app/create-playlist/create-playlist.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormControl } from '@angular/forms'; import { PostsService } from 'app/posts.services'; +import { Playlist } from 'api-types'; @Component({ selector: 'app-create-playlist', @@ -20,9 +21,24 @@ export class CreatePlaylistComponent implements OnInit { cached_thumbnail_url = null; create_in_progress = false; + create_mode = false; - constructor(private postsService: PostsService, - public dialogRef: MatDialogRef) { } + // playlist modify mode + + playlist: Playlist = null; + playlist_id: string = null; + preselected_files = []; + playlist_updated = false; + + constructor(@Inject(MAT_DIALOG_DATA) public data: any, + private postsService: PostsService, + public dialogRef: MatDialogRef) { + if (this.data?.create_mode) this.create_mode = true; + if (this.data?.playlist_id) { + this.playlist_id = this.data.playlist_id; + this.getPlaylist(); + } + } ngOnInit(): void {} @@ -40,6 +56,17 @@ export class CreatePlaylistComponent implements OnInit { }); } + updatePlaylist(): void { + this.playlist['name'] = this.name; + this.playlist['uids'] = this.filesSelect.value; + this.playlist_updated = true; + this.postsService.updatePlaylist(this.playlist).subscribe(() => { + this.postsService.openSnackBar('Playlist updated successfully.'); + this.getPlaylist(); + this.postsService.playlists_changed.next(true); + }); + } + getThumbnailURL(): string { return this.cached_thumbnail_url; } @@ -49,4 +76,19 @@ export class CreatePlaylistComponent implements OnInit { if (new_selection.length) this.cached_thumbnail_url = thumbnailURL; else this.cached_thumbnail_url = null; } + + playlistChanged(): boolean { + return JSON.stringify(this.playlist.uids) !== JSON.stringify(this.filesSelect.value) || this.name !== this.playlist.name; + } + + getPlaylist(): void { + this.postsService.getPlaylist(this.playlist_id, null, true).subscribe(res => { + if (res['playlist']) { + this.filesSelect.setValue(res['file_objs'].map(file => file.uid)); + this.preselected_files = res['file_objs']; + this.playlist = res['playlist']; + this.name = this.playlist['name']; + } + }); + } } From 0951e445ac07d5995585f730aab6e5cc73697636 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 20 Jun 2022 16:05:37 -0400 Subject: [PATCH 08/40] Removed modify playlsit component --- src/app/app.module.ts | 2 - .../custom-playlists.component.ts | 1 - .../create-playlist.component.ts | 2 +- .../modify-playlist.component.html | 44 -------- .../modify-playlist.component.scss | 45 -------- .../modify-playlist.component.spec.ts | 25 ----- .../modify-playlist.component.ts | 103 ------------------ 7 files changed, 1 insertion(+), 221 deletions(-) delete mode 100644 src/app/dialogs/modify-playlist/modify-playlist.component.html delete mode 100644 src/app/dialogs/modify-playlist/modify-playlist.component.scss delete mode 100644 src/app/dialogs/modify-playlist/modify-playlist.component.spec.ts delete mode 100644 src/app/dialogs/modify-playlist/modify-playlist.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 238e9e8..ef53d84 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -73,7 +73,6 @@ import { ManageUserComponent } from './components/manage-user/manage-user.compon import { ManageRoleComponent } from './components/manage-role/manage-role.component'; import { CookiesUploaderDialogComponent } from './dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component'; import { LogsViewerComponent } from './components/logs-viewer/logs-viewer.component'; -import { ModifyPlaylistComponent } from './dialogs/modify-playlist/modify-playlist.component'; import { ConfirmDialogComponent } from './dialogs/confirm-dialog/confirm-dialog.component'; import { UnifiedFileCardComponent } from './components/unified-file-card/unified-file-card.component'; import { RecentVideosComponent } from './components/recent-videos/recent-videos.component'; @@ -121,7 +120,6 @@ registerLocaleData(es, 'es'); ManageRoleComponent, CookiesUploaderDialogComponent, LogsViewerComponent, - ModifyPlaylistComponent, ConfirmDialogComponent, UnifiedFileCardComponent, RecentVideosComponent, diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index 3dec80a..1a2faa3 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -3,7 +3,6 @@ import { PostsService } from 'app/posts.services'; import { Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.component'; -import { ModifyPlaylistComponent } from 'app/dialogs/modify-playlist/modify-playlist.component'; import { Playlist } from 'api-types'; @Component({ diff --git a/src/app/create-playlist/create-playlist.component.ts b/src/app/create-playlist/create-playlist.component.ts index 7ec41b0..1a5c40a 100644 --- a/src/app/create-playlist/create-playlist.component.ts +++ b/src/app/create-playlist/create-playlist.component.ts @@ -10,7 +10,7 @@ import { Playlist } from 'api-types'; styleUrls: ['./create-playlist.component.scss'] }) export class CreatePlaylistComponent implements OnInit { - // really "createPlaylistDialogComponent" + // really "createAndModifyPlaylistDialogComponent" filesToSelectFrom = null; type = null; diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.html b/src/app/dialogs/modify-playlist/modify-playlist.component.html deleted file mode 100644 index 6c234c3..0000000 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.html +++ /dev/null @@ -1,44 +0,0 @@ -

Modify playlist

- - -
- -
- - - -
- -
- Randomize order when playing -
- -
-
- Normal order  - Reverse order  - -
- -
- -
-
- - - - -
{{playlist_item.title}}
-
- - - - -
- -
- - - - - \ No newline at end of file diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.scss b/src/app/dialogs/modify-playlist/modify-playlist.component.scss deleted file mode 100644 index 0be9a78..0000000 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.scss +++ /dev/null @@ -1,45 +0,0 @@ -.media-list { - -} - -.media-box { - -} - -.cdk-drag-preview { -box-sizing: border-box; -border-radius: 4px; -box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), - 0 8px 10px 1px rgba(0, 0, 0, 0.14), - 0 3px 14px 2px rgba(0, 0, 0, 0.12); -} - -.cdk-drag-placeholder { -opacity: 0; -} - -.cdk-drag-animating { -transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); -} - -.media-box:last-child { -border: none; -} - -.media-list.cdk-drop-list-dragging .media-box:not(.cdk-drag-placeholder) { -transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); -} - -.remove-item-button { - right: 10px; - position: absolute; - top: 4px; -} - -.playlist-item-text { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 70%; - margin: 0 auto; -} \ No newline at end of file diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.spec.ts b/src/app/dialogs/modify-playlist/modify-playlist.component.spec.ts deleted file mode 100644 index b724b87..0000000 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { ModifyPlaylistComponent } from './modify-playlist.component'; - -describe('ModifyPlaylistComponent', () => { - let component: ModifyPlaylistComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ ModifyPlaylistComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ModifyPlaylistComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/dialogs/modify-playlist/modify-playlist.component.ts b/src/app/dialogs/modify-playlist/modify-playlist.component.ts deleted file mode 100644 index ae53c16..0000000 --- a/src/app/dialogs/modify-playlist/modify-playlist.component.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { PostsService } from 'app/posts.services'; -import { DatabaseFile, Playlist } from 'api-types'; - -@Component({ - selector: 'app-modify-playlist', - templateUrl: './modify-playlist.component.html', - styleUrls: ['./modify-playlist.component.scss'] -}) -export class ModifyPlaylistComponent implements OnInit { - - playlist_id: string = null; - - original_playlist: Playlist = null; - playlist: Playlist = null; - playlist_file_objs: DatabaseFile[] = null; - - available_files = []; - all_files = []; - playlist_updated = false; - reverse_order = false; - - constructor(@Inject(MAT_DIALOG_DATA) public data: any, - private postsService: PostsService, - public dialogRef: MatDialogRef) { } - - ngOnInit(): void { - if (this.data) { - this.playlist_id = this.data.playlist_id; - this.getPlaylist(); - } - - this.reverse_order = localStorage.getItem('default_playlist_order_reversed') === 'true'; - } - - getFiles(): void { - this.postsService.getAllFiles().subscribe(res => { - this.processFiles(res['files']); - }); - } - - processFiles(new_files: DatabaseFile[] = null): void { - if (new_files) { this.all_files = new_files; } - this.available_files = this.all_files.filter(e => !this.playlist_file_objs.includes(e)) - } - - updatePlaylist(): void { - this.playlist['uids'] = this.playlist_file_objs.map(playlist_file_obj => playlist_file_obj['uid']) - this.postsService.updatePlaylist(this.playlist).subscribe(res => { - this.playlist_updated = true; - this.postsService.openSnackBar('Playlist updated successfully.'); - this.getPlaylist(); - this.postsService.playlists_changed.next(true); - }); - } - - playlistChanged(): boolean { - return JSON.stringify(this.playlist) !== JSON.stringify(this.original_playlist); - } - - getPlaylist(): void { - this.postsService.getPlaylist(this.playlist_id, null, true).subscribe(res => { - if (res['playlist']) { - this.playlist = res['playlist']; - this.playlist_file_objs = res['file_objs']; - this.original_playlist = JSON.parse(JSON.stringify(this.playlist)); - this.getFiles(); - } - }); - } - - addContent(file: DatabaseFile): void { - this.playlist_file_objs.push(file); - this.playlist.uids.push(file.uid); - this.processFiles(); - } - - removeContent(index: number): void { - if (this.reverse_order) { - index = this.playlist_file_objs.length - 1 - index; - } - this.playlist_file_objs.splice(index, 1); - this.playlist.uids.splice(index, 1); - this.processFiles(); - } - - togglePlaylistOrder(): void { - this.reverse_order = !this.reverse_order; - localStorage.setItem('default_playlist_order_reversed', '' + this.reverse_order); - } - - drop(event: CdkDragDrop): void { - if (this.reverse_order) { - event.previousIndex = this.playlist_file_objs.length - 1 - event.previousIndex; - event.currentIndex = this.playlist_file_objs.length - 1 - event.currentIndex; - } - moveItemInArray(this.playlist_file_objs, event.previousIndex, event.currentIndex); - this.playlist.uids = this.playlist_file_objs.map(file => file.uid); - } - -} From 4c6c15d3a35bb85f2a0ca1a6cb47496c392cf14e Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 20 Jun 2022 16:05:54 -0400 Subject: [PATCH 09/40] Code cleanup --- src/app/main/main.component.ts | 1 - src/app/settings/settings.component.ts | 85 ++++++++++++-------------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 25719ad..372ed1b 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -594,7 +594,6 @@ export class MainComponent implements OnInit { if (simulated_args) { // hide password if needed const passwordIndex = simulated_args.indexOf('--password'); - console.log(passwordIndex); if (passwordIndex !== -1 && passwordIndex !== simulated_args.length - 1) { simulated_args[passwordIndex + 1] = simulated_args[passwordIndex + 1].replace(/./g, '*'); } diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 81854c9..ce0c3fd 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -13,6 +13,7 @@ import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop'; import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { EditCategoryDialogComponent } from 'app/dialogs/edit-category-dialog/edit-category-dialog.component'; import { ActivatedRoute, Router } from '@angular/router'; +import { Category } from 'api-types'; @Component({ selector: 'app-settings', @@ -62,7 +63,7 @@ export class SettingsComponent implements OnInit { Object.keys(this.INDEX_TO_TAB).forEach(key => { this.TAB_TO_INDEX[this.INDEX_TO_TAB[key]] = key; }); } - ngOnInit() { + ngOnInit(): void { if (this.postsService.initialized) { this.getConfig(); this.getDBInfo(); @@ -90,16 +91,16 @@ export class SettingsComponent implements OnInit { }); } - getConfig() { + getConfig(): void { this.initial_config = this.postsService.config; this.new_config = JSON.parse(JSON.stringify(this.initial_config)); } - settingsSame() { + settingsSame(): boolean { return JSON.stringify(this.new_config) === JSON.stringify(this.initial_config); } - saveSettings() { + saveSettings(): void { const settingsToSave = {'YoutubeDLMaterial': this.new_config}; this.postsService.setConfig(settingsToSave).subscribe(res => { if (res['success']) { @@ -111,31 +112,31 @@ export class SettingsComponent implements OnInit { this.initial_config = JSON.parse(JSON.stringify(this.new_config)); this.postsService.reload_config.next(true); } - }, err => { + }, () => { console.error('Failed to save config!'); }) } - cancelSettings() { + cancelSettings(): void { this.new_config = JSON.parse(JSON.stringify(this.initial_config)); } - tabChanged(event) { + tabChanged(event): void { const index = event['index']; this.router.navigate(['/settings', {tab: this.INDEX_TO_TAB[index]}]); } - dropCategory(event: CdkDragDrop) { + dropCategory(event: CdkDragDrop): void { moveItemInArray(this.postsService.categories, event.previousIndex, event.currentIndex); this.postsService.updateCategories(this.postsService.categories).subscribe(res => { - }, err => { + }, () => { this.postsService.openSnackBar('Failed to update categories!'); }); } - openAddCategoryDialog() { - const done = new EventEmitter(); + openAddCategoryDialog(): void { + const done = new EventEmitter(); const dialogRef = this.dialog.open(InputDialogComponent, { width: '300px', data: { @@ -162,7 +163,7 @@ export class SettingsComponent implements OnInit { }); } - deleteCategory(category) { + deleteCategory(category: Category): void { const dialogRef = this.dialog.open(ConfirmDialogComponent, { data: { dialogTitle: 'Delete category', @@ -178,14 +179,14 @@ export class SettingsComponent implements OnInit { this.postsService.openSnackBar(`Successfully deleted ${category['name']}!`); this.postsService.reloadCategories(); } - }, err => { + }, () => { this.postsService.openSnackBar(`Failed to delete ${category['name']}!`); }); } }); } - openEditCategoryDialog(category) { + openEditCategoryDialog(category: Category): void { this.dialog.open(EditCategoryDialogComponent, { data: { category: category @@ -193,7 +194,7 @@ export class SettingsComponent implements OnInit { }); } - generateAPIKey() { + generateAPIKey(): void { this.postsService.generateNewAPIKey().subscribe(res => { if (res['new_api_key']) { this.initial_config.API.API_key = res['new_api_key']; @@ -202,16 +203,16 @@ export class SettingsComponent implements OnInit { }); } - localeSelectChanged(new_val) { + localeSelectChanged(new_val: string): void { localStorage.setItem('locale', new_val); - this.openSnackBar('Language successfully changed! Reload to update the page.') + this.postsService.openSnackBar('Language successfully changed! Reload to update the page.') } - generateBookmarklet() { + generateBookmarklet(): void { this.bookmarksite('YTDL-Material', this.generated_bookmarklet_code); } - generateBookmarkletCode() { + generateBookmarkletCode(): string { const currentURL = window.location.href.split('#')[0]; const homePageWithArgsURL = currentURL + '#/home;url='; const audioOnly = this.bookmarkletAudioOnly; @@ -226,13 +227,13 @@ export class SettingsComponent implements OnInit { } // not currently functioning on most platforms. hence not in use - bookmarksite(title, url) { + bookmarksite(title: string, url: string): void { // Internet Explorer if (document.all) { window['external']['AddFavorite'](url, title); } else if (window['chrome']) { // Google Chrome - this.openSnackBar('Chrome users must drag the \'Alternate URL\' link to your bookmarks.'); + this.postsService.openSnackBar('Chrome users must drag the \'Alternate URL\' link to your bookmarks.'); } else if (window['sidebar']) { // Firefox window['sidebar'].addPanel(title, url, ''); @@ -246,7 +247,7 @@ export class SettingsComponent implements OnInit { } } - openArgsModifierDialog() { + openArgsModifierDialog(): void { const dialogRef = this.dialog.open(ArgModifierDialogComponent, { data: { initial_args: this.new_config['Downloader']['custom_args'] @@ -259,20 +260,20 @@ export class SettingsComponent implements OnInit { }); } - getLatestGithubRelease() { + getLatestGithubRelease(): void { this.postsService.getLatestGithubRelease().subscribe(res => { this.latestGithubRelease = res; }); } - openCookiesUploaderDialog() { + openCookiesUploaderDialog(): void { this.dialog.open(CookiesUploaderDialogComponent, { width: '65vw' }); } - killAllDownloads() { - const done = new EventEmitter(); + killAllDownloads(): void { + const done = new EventEmitter(); const dialogRef = this.dialog.open(ConfirmDialogComponent, { data: { dialogTitle: 'Kill downloads', @@ -292,7 +293,7 @@ export class SettingsComponent implements OnInit { dialogRef.close(); this.postsService.openSnackBar('Failed to kill all downloads! Check logs for details.'); } - }, err => { + }, () => { dialogRef.close(); this.postsService.openSnackBar('Failed to kill all downloads! Check logs for details.'); }); @@ -300,21 +301,21 @@ export class SettingsComponent implements OnInit { }); } - restartServer() { - this.postsService.restartServer().subscribe(res => { + restartServer(): void { + this.postsService.restartServer().subscribe(() => { this.postsService.openSnackBar('Restarting!'); - }, err => { + }, () => { this.postsService.openSnackBar('Failed to restart the server.'); }); } - getDBInfo() { + getDBInfo(): void { this.postsService.getDBInfo().subscribe(res => { this.db_info = res['db_info']; }); } - transferDB() { + transferDB(): void { const dialogRef = this.dialog.open(ConfirmDialogComponent, { data: { dialogTitle: 'Transfer DB', @@ -329,25 +330,25 @@ export class SettingsComponent implements OnInit { }); } - _transferDB() { + _transferDB(): void { this.db_transferring = true; this.postsService.transferDB(this.db_info['using_local_db']).subscribe(res => { this.db_transferring = false; const success = res['success']; if (success) { - this.openSnackBar('Successfully transfered DB! Reloading info...'); + this.postsService.openSnackBar('Successfully transfered DB! Reloading info...'); this.getDBInfo(); } else { - this.openSnackBar('Failed to transfer DB -- transfer was aborted. Error: ' + res['error']); + this.postsService.openSnackBar('Failed to transfer DB -- transfer was aborted. Error: ' + res['error']); } }, err => { this.db_transferring = false; - this.openSnackBar('Failed to transfer DB -- API call failed. See browser logs for details.'); + this.postsService.openSnackBar('Failed to transfer DB -- API call failed. See browser logs for details.'); console.error(err); }); } - testConnectionString(connection_string) { + testConnectionString(connection_string: string): void { this.testing_connection_string = true; this.postsService.testConnectionString(connection_string).subscribe(res => { this.testing_connection_string = false; @@ -356,17 +357,9 @@ export class SettingsComponent implements OnInit { } else { this.postsService.openSnackBar('Connection failed! Error: ' + res['error']); } - }, err => { + }, () => { this.testing_connection_string = false; this.postsService.openSnackBar('Connection failed! Error: Server error. See logs for more info.'); }); } - - // snackbar helper - public openSnackBar(message: string, action: string = '') { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - } From cbdd1a6253df654237bf0fdedb19c60ea083b9a8 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 20 Jun 2022 16:25:55 -0400 Subject: [PATCH 10/40] Improved snackbar translations support --- .../custom-playlists.component.ts | 6 ++-- .../downloads/downloads.component.ts | 30 ++++++++----------- .../logs-viewer/logs-viewer.component.ts | 12 ++++---- .../recent-videos/recent-videos.component.ts | 6 ++-- .../twitch-chat/twitch-chat.component.ts | 6 ++-- .../create-playlist.component.ts | 11 ++++++- .../cookies-uploader-dialog.component.ts | 2 +- .../restore-db-dialog.component.ts | 6 ++-- src/app/main/main.component.ts | 4 +-- src/app/player/player.component.ts | 6 ++-- src/app/settings/settings.component.ts | 28 ++++++++--------- 11 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/app/components/custom-playlists/custom-playlists.component.ts b/src/app/components/custom-playlists/custom-playlists.component.ts index 1a2faa3..c2905c9 100644 --- a/src/app/components/custom-playlists/custom-playlists.component.ts +++ b/src/app/components/custom-playlists/custom-playlists.component.ts @@ -53,9 +53,9 @@ export class CustomPlaylistsComponent implements OnInit { dialogRef.afterClosed().subscribe(result => { if (result) { this.getAllPlaylists(); - this.postsService.openSnackBar('Successfully created playlist!', ''); + this.postsService.openSnackBar($localize`Successfully created playlist!', '`); } else if (result === false) { - this.postsService.openSnackBar('ERROR: failed to create playlist!', ''); + this.postsService.openSnackBar($localize`ERROR: failed to create playlist!', '`); } }); } @@ -96,7 +96,7 @@ export class CustomPlaylistsComponent implements OnInit { this.postsService.removePlaylist(playlistID).subscribe(res => { if (res['success']) { this.playlists.splice(index, 1); - this.postsService.openSnackBar('Playlist successfully removed.', ''); + this.postsService.openSnackBar($localize`Playlist successfully removed.', '`); } this.getAllPlaylists(); }); diff --git a/src/app/components/downloads/downloads.component.ts b/src/app/components/downloads/downloads.component.ts index 81cdd8c..45f1e7a 100644 --- a/src/app/components/downloads/downloads.component.ts +++ b/src/app/components/downloads/downloads.component.ts @@ -8,6 +8,7 @@ import { MatDialog } from '@angular/material/dialog'; import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialog.component'; import { MatSort } from '@angular/material/sort'; import { Clipboard } from '@angular/cdk/clipboard'; +import { Download } from 'api-types'; @Component({ selector: 'app-downloads', @@ -68,7 +69,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; - sort_downloads = (a, b) => { + sort_downloads = (a: Download, b: Download): number => { const result = b.timestamp_start - a.timestamp_start; return result; } @@ -166,7 +167,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { pauseDownload(download_uid: string): void { this.postsService.pauseDownload(download_uid).subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to pause download! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to pause download! See server logs for more info.`); } }); } @@ -174,7 +175,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { pauseAllDownloads(): void { this.postsService.pauseAllDownloads().subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to pause all downloads! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to pause all downloads! See server logs for more info.`); } }); } @@ -182,7 +183,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { resumeDownload(download_uid: string): void { this.postsService.resumeDownload(download_uid).subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to resume download! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to resume download! See server logs for more info.`); } }); } @@ -190,7 +191,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { resumeAllDownloads(): void { this.postsService.resumeAllDownloads().subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to resume all downloads! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to resume all downloads! See server logs for more info.`); } }); } @@ -198,7 +199,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { restartDownload(download_uid: string): void { this.postsService.restartDownload(download_uid).subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to restart download! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to restart download! See server logs for more info.`); } }); } @@ -206,7 +207,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { cancelDownload(download_uid: string): void { this.postsService.cancelDownload(download_uid).subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to cancel download! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to cancel download! See server logs for more info.`); } }); } @@ -214,12 +215,12 @@ export class DownloadsComponent implements OnInit, OnDestroy { clearDownload(download_uid: string): void { this.postsService.clearDownload(download_uid).subscribe(res => { if (!res['success']) { - this.postsService.openSnackBar('Failed to pause download! See server logs for more info.'); + this.postsService.openSnackBar($localize`Failed to pause download! See server logs for more info.`); } }); } - watchContent(download): void { + watchContent(download: Download): void { const container = download['container']; localStorage.setItem('player_navigator', this.router.url.split(';')[0]); const is_playlist = container['uids']; // hacky, TODO: fix @@ -230,7 +231,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { } } - combineDownloads(downloads_old, downloads_new) { + combineDownloads(downloads_old: Download[], downloads_new: Download[]): Download[] { // only keeps downloads that exist in the new set downloads_old = downloads_old.filter(download_old => downloads_new.some(download_new => download_new.uid === download_old.uid)); @@ -251,7 +252,7 @@ export class DownloadsComponent implements OnInit, OnDestroy { return downloads_old; } - showError(download) { + showError(download: Download): void { const copyToClipboardEmitter = new EventEmitter(); this.dialog.open(ConfirmDialogComponent, { data: { @@ -272,10 +273,3 @@ export class DownloadsComponent implements OnInit, OnDestroy { }); } } - -export interface Download { - timestamp_start: number; - title: string; - step_index: number; - progress: string; -} \ No newline at end of file diff --git a/src/app/components/logs-viewer/logs-viewer.component.ts b/src/app/components/logs-viewer/logs-viewer.component.ts index 5a218cc..c05c9f9 100644 --- a/src/app/components/logs-viewer/logs-viewer.component.ts +++ b/src/app/components/logs-viewer/logs-viewer.component.ts @@ -43,17 +43,17 @@ export class LogsViewerComponent implements OnInit { }) }); } else { - this.postsService.openSnackBar('Failed to retrieve logs!'); + this.postsService.openSnackBar($localize`Failed to retrieve logs!`); } }, err => { this.logs_loading = false; console.error(err); - this.postsService.openSnackBar('Failed to retrieve logs!'); + this.postsService.openSnackBar($localize`Failed to retrieve logs!`); }); } copiedLogsToClipboard() { - this.postsService.openSnackBar('Logs copied to clipboard!'); + this.postsService.openSnackBar($localize`Logs copied to clipboard!`); } clearLogs() { @@ -72,12 +72,12 @@ export class LogsViewerComponent implements OnInit { this.logs = []; this.logs_text = ''; this.getLogs(); - this.postsService.openSnackBar('Logs successfully cleared!'); + this.postsService.openSnackBar($localize`Logs successfully cleared!`); } else { - this.postsService.openSnackBar('Failed to clear logs!'); + this.postsService.openSnackBar($localize`Failed to clear logs!`); } }, err => { - this.postsService.openSnackBar('Failed to clear logs!'); + this.postsService.openSnackBar($localize`Failed to clear logs!`); }); } }); diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 0463140..b930379 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -297,13 +297,13 @@ export class RecentVideosComponent implements OnInit { deleteNormalFile(file, blacklistMode = false) { this.postsService.deleteFile(file.uid, blacklistMode).subscribe(result => { if (result) { - this.postsService.openSnackBar('Delete success!', 'OK.'); + this.postsService.openSnackBar($localize`Delete success!', 'OK.`); this.removeFileCard(file); } else { - this.postsService.openSnackBar('Delete failed!', 'OK.'); + this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); } }, err => { - this.postsService.openSnackBar('Delete failed!', 'OK.'); + this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); }); } diff --git a/src/app/components/twitch-chat/twitch-chat.component.ts b/src/app/components/twitch-chat/twitch-chat.component.ts index bbeb7de..8208b84 100644 --- a/src/app/components/twitch-chat/twitch-chat.component.ts +++ b/src/app/components/twitch-chat/twitch-chat.component.ts @@ -96,18 +96,18 @@ export class TwitchChatComponent implements OnInit, OnDestroy { let vodId = this.db_file.url.split('videos/').length > 1 && this.db_file.url.split('videos/')[1]; vodId = vodId.split('?')[0]; if (!vodId) { - this.postsService.openSnackBar('VOD url for this video is not supported. VOD ID must be after "twitch.tv/videos/"'); + this.postsService.openSnackBar($localize`VOD url for this video is not supported. VOD ID must be after "twitch.tv/videos/"`); } this.postsService.downloadTwitchChat(this.db_file.id, this.db_file.isAudio ? 'audio' : 'video', vodId, null, this.sub).subscribe(res => { if (res['chat']) { this.initializeChatCheck(res['chat']); } else { this.downloading_chat = false; - this.postsService.openSnackBar('Download failed.') + this.postsService.openSnackBar($localize`Download failed.`) } }, err => { this.downloading_chat = false; - this.postsService.openSnackBar('Chat could not be downloaded.') + this.postsService.openSnackBar($localize`Chat could not be downloaded.`) }); } diff --git a/src/app/create-playlist/create-playlist.component.ts b/src/app/create-playlist/create-playlist.component.ts index 1a5c40a..8688166 100644 --- a/src/app/create-playlist/create-playlist.component.ts +++ b/src/app/create-playlist/create-playlist.component.ts @@ -53,17 +53,26 @@ export class CreatePlaylistComponent implements OnInit { } else { this.dialogRef.close(false); } + }, err => { + this.create_in_progress = false; + console.error(err); }); } updatePlaylist(): void { + this.create_in_progress = true; this.playlist['name'] = this.name; this.playlist['uids'] = this.filesSelect.value; this.playlist_updated = true; this.postsService.updatePlaylist(this.playlist).subscribe(() => { - this.postsService.openSnackBar('Playlist updated successfully.'); + this.create_in_progress = false; + this.postsService.openSnackBar($localize`Playlist updated successfully.`); this.getPlaylist(); this.postsService.playlists_changed.next(true); + }, err => { + this.create_in_progress = false; + console.error(err) + this.postsService.openSnackBar($localize`Playlist updated successfully.`); }); } diff --git a/src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts b/src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts index 327a870..fc17a64 100644 --- a/src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts +++ b/src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts @@ -39,7 +39,7 @@ export class CookiesUploaderDialogComponent implements OnInit { this.uploading = false; if (res['success']) { this.uploaded = true; - this.postsService.openSnackBar('Cookies successfully uploaded!'); + this.postsService.openSnackBar($localize`Cookies successfully uploaded!`); } }, err => { this.uploading = false; diff --git a/src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts b/src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts index 204a8c1..6c14543 100644 --- a/src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts +++ b/src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts @@ -36,14 +36,14 @@ export class RestoreDbDialogComponent implements OnInit { this.postsService.restoreDBBackup(this.selected_backup[0]).subscribe(res => { this.restoring = false; if (res['success']) { - this.postsService.openSnackBar('Database successfully restored!'); + this.postsService.openSnackBar($localize`Database successfully restored!`); this.dialogRef.close(); } else { - this.postsService.openSnackBar('Failed to restore database! See logs for more info.'); + this.postsService.openSnackBar($localize`Failed to restore database! See logs for more info.`); } }, err => { this.restoring = false; - this.postsService.openSnackBar('Failed to restore database! See browser console for more info.'); + this.postsService.openSnackBar($localize`Failed to restore database! See browser console for more info.`); console.error(err); }); } diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 372ed1b..baac9b4 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -390,7 +390,7 @@ export class MainComponent implements OnInit { }, () => { // can't access server this.downloadingfile = false; this.current_download = null; - this.postsService.openSnackBar('Download failed!', 'OK.'); + this.postsService.openSnackBar($localize`Download failed!', 'OK.`); }); if (!this.autoplay && urls.length === 1) { @@ -776,7 +776,7 @@ export class MainComponent implements OnInit { } else if (this.current_download['finished'] && this.current_download['error']) { this.downloadingfile = false; this.current_download = null; - this.postsService.openSnackBar('Download failed!', 'OK.'); + this.postsService.openSnackBar($localize`Download failed!', 'OK.`); } } else { // console.log('failed to get new download'); diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index a0676e4..4dbaa8c 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -147,7 +147,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { this.postsService.getFile(this.uid, this.uuid).subscribe(res => { this.db_file = res['file']; if (!this.db_file) { - this.postsService.openSnackBar('Failed to get file information from the server.', 'Dismiss'); + this.postsService.openSnackBar($localize`Failed to get file information from the server.', 'Dismiss`); return; } this.postsService.incrementViewCount(this.db_file['uid'], null, this.uuid).subscribe(() => undefined, err => { @@ -183,10 +183,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { this.show_player = true; this.parseFileNames(); } else { - this.postsService.openSnackBar('Failed to load playlist!', ''); + this.postsService.openSnackBar($localize`Failed to load playlist!', '`); } }, () => { - this.postsService.openSnackBar('Failed to load playlist!', ''); + this.postsService.openSnackBar($localize`Failed to load playlist!', '`); }); } diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index ce0c3fd..f46709f 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -131,7 +131,7 @@ export class SettingsComponent implements OnInit { this.postsService.updateCategories(this.postsService.categories).subscribe(res => { }, () => { - this.postsService.openSnackBar('Failed to update categories!'); + this.postsService.openSnackBar($localize`Failed to update categories!`); }); } @@ -205,7 +205,7 @@ export class SettingsComponent implements OnInit { localeSelectChanged(new_val: string): void { localStorage.setItem('locale', new_val); - this.postsService.openSnackBar('Language successfully changed! Reload to update the page.') + this.postsService.openSnackBar($localize`Language successfully changed! Reload to update the page.`) } generateBookmarklet(): void { @@ -233,7 +233,7 @@ export class SettingsComponent implements OnInit { window['external']['AddFavorite'](url, title); } else if (window['chrome']) { // Google Chrome - this.postsService.openSnackBar('Chrome users must drag the \'Alternate URL\' link to your bookmarks.'); + this.postsService.openSnackBar($localize`Chrome users must drag the \'Alternate URL\' link to your bookmarks.`); } else if (window['sidebar']) { // Firefox window['sidebar'].addPanel(title, url, ''); @@ -288,14 +288,14 @@ export class SettingsComponent implements OnInit { this.postsService.killAllDownloads().subscribe(res => { if (res['success']) { dialogRef.close(); - this.postsService.openSnackBar('Successfully killed all downloads!'); + this.postsService.openSnackBar($localize`Successfully killed all downloads!`); } else { dialogRef.close(); - this.postsService.openSnackBar('Failed to kill all downloads! Check logs for details.'); + this.postsService.openSnackBar($localize`Failed to kill all downloads! Check logs for details.`); } }, () => { dialogRef.close(); - this.postsService.openSnackBar('Failed to kill all downloads! Check logs for details.'); + this.postsService.openSnackBar($localize`Failed to kill all downloads! Check logs for details.`); }); } }); @@ -303,9 +303,9 @@ export class SettingsComponent implements OnInit { restartServer(): void { this.postsService.restartServer().subscribe(() => { - this.postsService.openSnackBar('Restarting!'); + this.postsService.openSnackBar($localize`Restarting!`); }, () => { - this.postsService.openSnackBar('Failed to restart the server.'); + this.postsService.openSnackBar($localize`Failed to restart the server.`); }); } @@ -336,14 +336,14 @@ export class SettingsComponent implements OnInit { this.db_transferring = false; const success = res['success']; if (success) { - this.postsService.openSnackBar('Successfully transfered DB! Reloading info...'); + this.postsService.openSnackBar($localize`Successfully transfered DB! Reloading info...`); this.getDBInfo(); } else { - this.postsService.openSnackBar('Failed to transfer DB -- transfer was aborted. Error: ' + res['error']); + this.postsService.openSnackBar($localize`Failed to transfer DB -- transfer was aborted. Error: ` + res['error']); } }, err => { this.db_transferring = false; - this.postsService.openSnackBar('Failed to transfer DB -- API call failed. See browser logs for details.'); + this.postsService.openSnackBar($localize`Failed to transfer DB -- API call failed. See browser logs for details.`); console.error(err); }); } @@ -353,13 +353,13 @@ export class SettingsComponent implements OnInit { this.postsService.testConnectionString(connection_string).subscribe(res => { this.testing_connection_string = false; if (res['success']) { - this.postsService.openSnackBar('Connection successful!'); + this.postsService.openSnackBar($localize`Connection successful!`); } else { - this.postsService.openSnackBar('Connection failed! Error: ' + res['error']); + this.postsService.openSnackBar($localize`Connection failed! Error: ` + res['error']); } }, () => { this.testing_connection_string = false; - this.postsService.openSnackBar('Connection failed! Error: Server error. See logs for more info.'); + this.postsService.openSnackBar($localize`Connection failed! Error: Server error. See logs for more info.`); }); } } From 7bfb2976fe774ab23e606cab882c7ed258032054 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:22:58 -0400 Subject: [PATCH 11/40] Fixed twitch chat downloads, tcd is now required and client secret must be supplied --- backend/appdata/default.json | 3 +- backend/config.js | 3 +- backend/consts.js | 10 +- backend/test/tests.js | 19 +++- backend/twitch.js | 136 ++++++++++------------- src/app/settings/settings.component.html | 11 +- 6 files changed, 92 insertions(+), 90 deletions(-) diff --git a/backend/appdata/default.json b/backend/appdata/default.json index 44f2f02..639e269 100644 --- a/backend/appdata/default.json +++ b/backend/appdata/default.json @@ -31,7 +31,8 @@ "use_youtube_API": false, "youtube_API_key": "", "use_twitch_API": false, - "twitch_API_key": "", + "twitch_client_ID": "", + "twitch_client_secret": "", "twitch_auto_download_chat": false, "use_sponsorblock_API": false, "generate_NFO_files": false diff --git a/backend/config.js b/backend/config.js index 4e208fb..27faab8 100644 --- a/backend/config.js +++ b/backend/config.js @@ -206,7 +206,8 @@ const DEFAULT_CONFIG = { "use_youtube_API": false, "youtube_API_key": "", "use_twitch_API": false, - "twitch_API_key": "", + "twitch_client_ID": "", + "twitch_client_secret": "", "twitch_auto_download_chat": false, "use_sponsorblock_API": false, "generate_NFO_files": false diff --git a/backend/consts.js b/backend/consts.js index 222d6bc..f473e07 100644 --- a/backend/consts.js +++ b/backend/consts.js @@ -102,9 +102,13 @@ exports.CONFIG_ITEMS = { 'key': 'ytdl_use_twitch_api', 'path': 'YoutubeDLMaterial.API.use_twitch_API' }, - 'ytdl_twitch_api_key': { - 'key': 'ytdl_twitch_api_key', - 'path': 'YoutubeDLMaterial.API.twitch_API_key' + 'ytdl_twitch_client_id': { + 'key': 'ytdl_twitch_client_id', + 'path': 'YoutubeDLMaterial.API.twitch_client_ID' + }, + 'ytdl_twitch_client_secret': { + 'key': 'ytdl_twitch_client_secret', + 'path': 'YoutubeDLMaterial.API.twitch_client_secret' }, 'ytdl_twitch_auto_download_chat': { 'key': 'ytdl_twitch_auto_download_chat', diff --git a/backend/test/tests.js b/backend/test/tests.js index c87045a..9236a42 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -1,6 +1,7 @@ -var assert = require('assert'); +const assert = require('assert'); const low = require('lowdb') -var winston = require('winston'); +const winston = require('winston'); +const path = require('path'); process.chdir('./backend') @@ -465,6 +466,20 @@ describe('Downloader', function() { console.log(updated_args2); assert(JSON.stringify(updated_args2), JSON.stringify(expected_args2)); }); + describe('Twitch', async function () { + const twitch_api = require('../twitch'); + const example_vod = '1493770675'; + it('Download VOD', async function() { + const sample_path = path.join('test', 'sample.twitch_chat.json'); + if (fs.existsSync(sample_path)) fs.unlinkSync(sample_path); + this.timeout(300000); + await twitch_api.downloadTwitchChatByVODID(example_vod, 'sample', null, null, null, './test'); + assert(fs.existsSync(sample_path)); + + // cleanup + if (fs.existsSync(sample_path)) fs.unlinkSync(sample_path); + }); + }); }); describe('Tasks', function() { diff --git a/backend/twitch.js b/backend/twitch.js index 2a231e9..151e177 100644 --- a/backend/twitch.js +++ b/backend/twitch.js @@ -1,90 +1,53 @@ -var moment = require('moment'); -var Axios = require('axios'); -var fs = require('fs-extra') -var path = require('path'); const config_api = require('./config'); - -async function getCommentsForVOD(clientID, vodId) { - let url = `https://api.twitch.tv/v5/videos/${vodId}/comments?content_offset_seconds=0`, - batch, - cursor; - - let comments = null; - - try { - do { - batch = (await Axios.get(url, { - headers: { - 'Client-ID': clientID, - Accept: 'application/vnd.twitchtv.v5+json; charset=UTF-8', - 'Content-Type': 'application/json; charset=UTF-8', - } - })).data; - - const str = batch.comments.map(c => { - let { - created_at: msgCreated, - content_offset_seconds: timestamp, - commenter: { - name, - _id, - created_at: acctCreated - }, - message: { - body: msg, - user_color: user_color - } - } = c; - - const timestamp_str = moment.duration(timestamp, 'seconds') - .toISOString() - .replace(/P.*?T(?:(\d+?)H)?(?:(\d+?)M)?(?:(\d+).*?S)?/, - (_, ...ms) => { - const seg = v => v ? v.padStart(2, '0') : '00'; - return `${seg(ms[0])}:${seg(ms[1])}:${seg(ms[2])}`; - }); - - acctCreated = moment(acctCreated).utc(); - msgCreated = moment(msgCreated).utc(); - - if (!comments) comments = []; - - comments.push({ - timestamp: timestamp, - timestamp_str: timestamp_str, - name: name, - message: msg, - user_color: user_color - }); - // let line = `${timestamp},${msgCreated.format(tsFormat)},${name},${_id},"${msg.replace(/"/g, '""')}",${acctCreated.format(tsFormat)}`; - // return line; - }).join('\n'); - - cursor = batch._next; - url = `https://api.twitch.tv/v5/videos/${vodId}/comments?cursor=${cursor}`; - await new Promise(res => setTimeout(res, 300)); - } while (cursor); - } catch (err) { - console.error(err); +const logger = require('./logger'); + +const moment = require('moment'); +const fs = require('fs-extra') +const path = require('path'); + +async function getCommentsForVOD(clientID, clientSecret, vodId) { + const { promisify } = require('util'); + const child_process = require('child_process'); + const exec = promisify(child_process.exec); + const result = await exec(`tcd --video ${vodId} --client-id ${clientID} --client-secret ${clientSecret} --format json -o appdata`, {stdio:[0,1,2]}); + + if (result['stderr']) { + logger.error(`Failed to download twitch comments for ${vodId}`); + logger.error(result['stderr']); + return null; } - return comments; + const raw_json = fs.readJSONSync(path.join('appdata', `${vodId}.json`)); + const new_json = raw_json.comments.map(comment_obj => { + return { + timestamp: comment_obj.content_offset_seconds, + timestamp_str: convertTimestamp(comment_obj.content_offset_seconds), + name: comment_obj.commenter.name, + message: comment_obj.message.body, + user_color: comment_obj.message.user_color + } + }); + + return new_json; } async function getTwitchChatByFileID(id, type, user_uid, uuid, sub) { + const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); + const subscriptionsFileFolder = config_api.getConfigItem('ytdl_subscriptions_base_path'); let file_path = null; if (user_uid) { if (sub) { - file_path = path.join('users', user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json'); + file_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`); } else { - file_path = path.join('users', user_uid, type, id + '.twitch_chat.json'); + file_path = path.join(usersFileFolder, user_uid, type, `${id}.twitch_chat.json`); } } else { if (sub) { - file_path = path.join('subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json'); + file_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`); } else { - file_path = path.join(type, id + '.twitch_chat.json'); + const typeFolder = config_api.getConfigItem(`ytdl_${type}_folder_path`); + file_path = path.join(typeFolder, `${id}.twitch_chat.json`); } } @@ -96,23 +59,28 @@ async function getTwitchChatByFileID(id, type, user_uid, uuid, sub) { return chat_file; } -async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub) { - const twitch_api_key = config_api.getConfigItem('ytdl_twitch_api_key'); - const chat = await getCommentsForVOD(twitch_api_key, vodId); +async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub, customFileFolderPath = null) { + const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); + const subscriptionsFileFolder = config_api.getConfigItem('ytdl_subscriptions_base_path'); + const twitch_client_id = config_api.getConfigItem('ytdl_twitch_client_id'); + const twitch_client_secret = config_api.getConfigItem('ytdl_twitch_client_secret'); + const chat = await getCommentsForVOD(twitch_client_id, twitch_client_secret, vodId); // save file if needed params are included let file_path = null; - if (user_uid) { + if (customFileFolderPath) { + file_path = path.join(customFileFolderPath, `${id}.twitch_chat.json`) + } else if (user_uid) { if (sub) { - file_path = path.join('users', user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json'); + file_path = path.join(usersFileFolder, user_uid, 'subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`); } else { - file_path = path.join('users', user_uid, type, id + '.twitch_chat.json'); + file_path = path.join(usersFileFolder, user_uid, type, `${id}.twitch_chat.json`); } } else { if (sub) { - file_path = path.join('subscriptions', sub.isPlaylist ? 'playlists' : 'channels', sub.name, id + '.twitch_chat.json'); + file_path = path.join(subscriptionsFileFolder, sub.isPlaylist ? 'playlists' : 'channels', sub.name, `${id}.twitch_chat.json`); } else { - file_path = path.join(type, id + '.twitch_chat.json'); + file_path = path.join(type, `${id}.twitch_chat.json`); } } @@ -121,6 +89,14 @@ async function downloadTwitchChatByVODID(vodId, id, type, user_uid, sub) { return chat; } +const convertTimestamp = (timestamp) => moment.duration(timestamp, 'seconds') + .toISOString() + .replace(/P.*?T(?:(\d+?)H)?(?:(\d+?)M)?(?:(\d+).*?S)?/, + (_, ...ms) => { + const seg = v => v ? v.padStart(2, '0') : '00'; + return `${seg(ms[0])}:${seg(ms[1])}:${seg(ms[2])}`; +}); + module.exports = { getCommentsForVOD: getCommentsForVOD, getTwitchChatByFileID: getTwitchChatByFileID, diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index aa453a4..24c1c0c 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -263,11 +263,16 @@
- - Also known as a Client ID. Generating a key is easy! + + Generating an ID/secret is easy!
-
+
+ + + +
+
Use SponsorBlock API
From 901e87a681d24480c353549b68eaef126b564833 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:23:28 -0400 Subject: [PATCH 12/40] Fixed loading spinner in create playlist dialog --- src/app/create-playlist/create-playlist.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/create-playlist/create-playlist.component.html b/src/app/create-playlist/create-playlist.component.html index cdc40ad..c196efd 100644 --- a/src/app/create-playlist/create-playlist.component.html +++ b/src/app/create-playlist/create-playlist.component.html @@ -18,12 +18,12 @@
-
+
\ No newline at end of file From da8c23d3ef09747f596b7ade65c2fa93eec5e73e Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:40:42 -0400 Subject: [PATCH 13/40] Updated dockerfile to include tcd installation --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 258e7e9..c4b4a30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,6 +52,7 @@ RUN npm install -g pm2 && \ apt install -y --no-install-recommends gosu python3-minimal python-is-python3 atomicparsley && \ apt clean && \ rm -rf /var/lib/apt/lists/* +RUN pip install tcd WORKDIR /app # User 1000 already exist from base image COPY --chown=$UID:$GID --from=ffmpeg [ "/usr/local/bin/ffmpeg", "/usr/local/bin/ffmpeg" ] From 9d1624d5694730a398622eb362a38f44c8f0cffc Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:42:15 -0400 Subject: [PATCH 14/40] Removed recommendation of nightly, updated language in README, and updated SECURITY.md --- Dockerfile.heroku | 2 +- README.md | 19 +++++++------------ SECURITY.md | 18 +++++++++--------- docker-compose.yml | 2 +- 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Dockerfile.heroku b/Dockerfile.heroku index b2ff8b3..16e451c 100644 --- a/Dockerfile.heroku +++ b/Dockerfile.heroku @@ -1,2 +1,2 @@ -FROM tzahi12345/youtubedl-material:nightly +FROM tzahi12345/youtubedl-material:latest CMD [ "pm2-runtime", "pm2.config.js" ] \ No newline at end of file diff --git a/README.md b/README.md index bf4c4f2..d983581 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,6 @@ Now with [Docker](#Docker) support!
-### USAGE OF THE NIGHTLY BUILDS IS HIGHLY RECOMMENDED. - -For much better scaling with large datasets please run your YTDL-M instance with a MongoDB backend rather than the json file-based default. -It will fix a lot of performance problems (especially with datasets in the tens of thousands videos/audios)! -The (closed) issues as well as the project's Wiki will give you good starting points for your journey! - -For MongoDB specifically there is [this little guide](https://github.com/Tzahi12345/YoutubeDL-Material/wiki/Setting-a-MongoDB-backend-to-use-as-database-provider-for-YTDL-M). - -
- ## Getting Started Check out the prerequisites, and go to the installation section. Easy as pie! @@ -58,6 +48,7 @@ sudo yum install nodejs youtube-dl ffmpeg ffmpeg-devel Optional dependencies: * AtomicParsley (for embedding thumbnails, package name `atomicparsley`) +* [tcd](https://github.com/PetterKraabol/Twitch-Chat-Downloader) (for downloading Twitch VOD chats) ### Installing @@ -102,8 +93,6 @@ If you are looking to setup YoutubeDL-Material with Docker, this section is for 3. Run `docker-compose up` to start it up. If successful, it should say "HTTP(S): Started on port 17443" or something similar. This tells you the *container-internal* port of the application. Please check your `docker-compose.yml` file for the *external* port. If you downloaded the file as described above, it defaults to **8998**. 4. Make sure you can connect to the specified URL + *external* port, and if so, you are done! -NOTE: It is currently recommended that you use the `nightly` tag on Docker. To do so, simply update the docker-compose.yml `image` field so that it points to `tzahi12345/youtubedl-material:nightly`. - ### Custom UID/GID By default, the Docker container runs as non-root with UID=1000 and GID=1000. To set this to your own UID/GID, simply update the `environment` section in your `docker-compose.yml` like so: @@ -114,6 +103,12 @@ environment: GID: YOUR_GID ``` +## MongoDB + +For much better scaling with large datasets please run your YoutubeDL-Material instance with MongoDB backend rather than the json file-based default. It will fix a lot of performance problems (especially with datasets in the tens of thousands videos/audios)! + +[Tutorial](https://github.com/Tzahi12345/YoutubeDL-Material/wiki/Setting-a-MongoDB-backend-to-use-as-database-provider-for-YTDL-M). + ## API [API Docs](https://youtubedl-material.stoplight.io/docs/youtubedl-material/Public%20API%20v1.yaml) diff --git a/SECURITY.md b/SECURITY.md index 18daadb..0601c7a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,16 +2,16 @@ ## Supported Versions -Currently all work on this project goes into the nightly builds. -4.2's RELEASE build is now quite old and should be considered legacy. -We urge users to use the nightly releases, because the project -constantly sees fixes. +If you would like to see the latest updates, use the `nightly` tag on Docker. -| Version | Supported | -| ------------- | ------------------ | -| 4.2 Nightlies | :white_check_mark: | -| 4.2 Release | :x: | -| < 4.2 | :x: | +If you'd like to stick with more stable releases, use the `latest` tag on Docker or download the [latest release here](https://github.com/Tzahi12345/YoutubeDL-Material/releases/latest). + +| Version | Supported | +| -------------------- | ------------------ | +| 4.3 Docker Nightlies | :white_check_mark: | +| 4.3 Release | :white_check_mark: | +| 4.2 Release | :x: | +| < 4.2 | :x: | ## Reporting a Vulnerability diff --git a/docker-compose.yml b/docker-compose.yml index 60aef89..c1cc36e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - ./users:/app/users ports: - "8998:17442" - image: tzahi12345/youtubedl-material:nightly + image: tzahi12345/youtubedl-material:latest ytdl-mongo-db: image: mongo ports: From cddd2802067c7892afed28c1290ab5f8ac3d78d0 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:51:32 -0400 Subject: [PATCH 15/40] Fixed issue where pip was missing in Docker Temp twitch chat files now get auto removed --- Dockerfile | 2 +- backend/twitch.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c4b4a30..b237235 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,7 +49,7 @@ RUN npm config set strict-ssl false && \ FROM base RUN npm install -g pm2 && \ apt update && \ - apt install -y --no-install-recommends gosu python3-minimal python-is-python3 atomicparsley && \ + apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python-pip atomicparsley && \ apt clean && \ rm -rf /var/lib/apt/lists/* RUN pip install tcd diff --git a/backend/twitch.js b/backend/twitch.js index 151e177..6a70c89 100644 --- a/backend/twitch.js +++ b/backend/twitch.js @@ -17,7 +17,9 @@ async function getCommentsForVOD(clientID, clientSecret, vodId) { return null; } - const raw_json = fs.readJSONSync(path.join('appdata', `${vodId}.json`)); + const temp_chat_path = path.join('appdata', `${vodId}.json`); + + const raw_json = fs.readJSONSync(temp_chat_path); const new_json = raw_json.comments.map(comment_obj => { return { timestamp: comment_obj.content_offset_seconds, @@ -28,6 +30,8 @@ async function getCommentsForVOD(clientID, clientSecret, vodId) { } }); + fs.unlinkSync(temp_chat_path); + return new_json; } From b6de6d08fa5a0b11d2b3e7c8301be220d38ec447 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 01:58:35 -0400 Subject: [PATCH 16/40] Fixed potential command injection vulnerability --- backend/twitch.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/twitch.js b/backend/twitch.js index 6a70c89..c713676 100644 --- a/backend/twitch.js +++ b/backend/twitch.js @@ -9,6 +9,13 @@ async function getCommentsForVOD(clientID, clientSecret, vodId) { const { promisify } = require('util'); const child_process = require('child_process'); const exec = promisify(child_process.exec); + + // Reject invalid params to prevent command injection attack + if (!clientID.match(/^[0-9a-z]+$/) || !clientSecret.match(/^[0-9a-z]+$/) || !vodId.match(/^[0-9a-z]+$/)) { + logger.error('Client ID, client secret, and VOD ID must be purely alphanumeric. Twitch chat download failed!'); + return null; + } + const result = await exec(`tcd --video ${vodId} --client-id ${clientID} --client-secret ${clientSecret} --format json -o appdata`, {stdio:[0,1,2]}); if (result['stderr']) { From 6fe0cd5649fd3d42a8240584db880ad6b6ee869b Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 02:01:36 -0400 Subject: [PATCH 17/40] Updated pip target --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b237235..915aa79 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,7 +49,7 @@ RUN npm config set strict-ssl false && \ FROM base RUN npm install -g pm2 && \ apt update && \ - apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python-pip atomicparsley && \ + apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley && \ apt clean && \ rm -rf /var/lib/apt/lists/* RUN pip install tcd From 39abc3efcf0d656a7d73ee949230c17f1f8344d4 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 02:21:04 -0400 Subject: [PATCH 18/40] Utilize yt-dlp filesize approximations if available --- backend/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/utils.js b/backend/utils.js index 805d591..1d44681 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -173,8 +173,8 @@ function getExpectedFileSize(input_info_jsons) { let individual_expected_filesize = 0; formats.forEach(format_id => { info_json.formats.forEach(available_format => { - if (available_format.format_id === format_id && available_format.filesize) { - individual_expected_filesize += available_format.filesize; + if (available_format.format_id === format_id && (available_format.filesize || available_format.filesize_approx)) { + individual_expected_filesize += (available_format.filesize ? available_format.filesize : available_format.filesize_approx); } }); }); From 636f7b16a8ecd05957589bb1003f139da9c585df Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 02:23:35 -0400 Subject: [PATCH 19/40] Changed default downloader to yt-dlp --- backend/appdata/default.json | 2 +- backend/config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/appdata/default.json b/backend/appdata/default.json index 639e269..d0425c6 100644 --- a/backend/appdata/default.json +++ b/backend/appdata/default.json @@ -64,7 +64,7 @@ "mongodb_connection_string": "mongodb://127.0.0.1:27017/?compressors=zlib" }, "Advanced": { - "default_downloader": "youtube-dl", + "default_downloader": "yt-dlp", "use_default_downloading_agent": true, "custom_downloading_agent": "", "multi_user_mode": false, diff --git a/backend/config.js b/backend/config.js index 27faab8..7b1908e 100644 --- a/backend/config.js +++ b/backend/config.js @@ -239,7 +239,7 @@ const DEFAULT_CONFIG = { "mongodb_connection_string": "mongodb://127.0.0.1:27017/?compressors=zlib" }, "Advanced": { - "default_downloader": "youtube-dl", + "default_downloader": "yt-dlp", "use_default_downloading_agent": true, "custom_downloading_agent": "", "multi_user_mode": false, From bec158f65d8ea8a17eeb6f236928a64a4c8dd654 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 21:07:34 -0400 Subject: [PATCH 20/40] Fixed an issue where if file manager and download only mode were enabled, files would download twice --- src/app/main/main.component.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index baac9b4..4bfee05 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -50,8 +50,6 @@ export class MainComponent implements OnInit { allowQualitySelect = false; downloadOnlyMode = false; allowAutoplay = false; - audioFolderPath; - videoFolderPath; use_youtubedl_archive = false; globalCustomArgs = null; allowAdvancedDownload = false; @@ -69,8 +67,6 @@ export class MainComponent implements OnInit { results_showing = true; results = []; - mp3s: any[] = []; - mp4s: any[] = []; playlists = {'audio': [], 'video': []}; playlist_thumbnails = {}; downloads: Download[] = []; @@ -200,8 +196,6 @@ export class MainComponent implements OnInit { && this.postsService.hasPermission('filemanager'); this.downloadOnlyMode = this.postsService.config['Extra']['download_only_mode']; this.allowAutoplay = this.postsService.config['Extra']['allow_autoplay']; - this.audioFolderPath = this.postsService.config['Downloader']['path-audio']; - this.videoFolderPath = this.postsService.config['Downloader']['path-video']; this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive']; this.globalCustomArgs = this.postsService.config['Downloader']['custom_args']; this.youtubeSearchEnabled = this.postsService.config['API'] && this.postsService.config['API']['use_youtube_API'] && @@ -308,7 +302,7 @@ export class MainComponent implements OnInit { } // download helpers - downloadHelper(container, type: string, is_playlist = false, force_view = false, navigate_mode = false): void { + downloadHelper(container: DatabaseFile | Playlist, type: string, is_playlist = false, force_view = false, navigate_mode = false): void { this.downloadingfile = false; if (!this.autoplay && !this.downloadOnlyMode && !navigate_mode) { // do nothing @@ -319,7 +313,7 @@ export class MainComponent implements OnInit { if (is_playlist) { this.downloadPlaylist(container['uid']); } else { - this.downloadFileFromServer(container, type); + this.downloadFileFromServer(container as DatabaseFile, type); } this.reloadRecentVideos(); } else { @@ -438,7 +432,7 @@ export class MainComponent implements OnInit { return null; } - getDownloadByUID(uid: string) { + getDownloadByUID(uid: string): Download { const index = this.downloads.findIndex(download => download.uid === uid); if (index !== -1) { return this.downloads[index]; @@ -447,7 +441,7 @@ export class MainComponent implements OnInit { } } - removeDownloadFromCurrentDownloads(download_to_remove): boolean { + removeDownloadFromCurrentDownloads(download_to_remove: Download): boolean { if (this.current_download === download_to_remove) { this.current_download = null; } @@ -770,9 +764,10 @@ export class MainComponent implements OnInit { if (this.current_download['finished'] && !this.current_download['error']) { const container = this.current_download['container']; - const is_playlist = this.current_download['file_uids'].length > 1; - this.downloadHelper(container, this.current_download['type'], is_playlist, false); - this.current_download = null; + const is_playlist = this.current_download['file_uids'].length > 1; + const type = this.current_download['type']; + this.current_download = null; + this.downloadHelper(container, type, is_playlist, false); } else if (this.current_download['finished'] && this.current_download['error']) { this.downloadingfile = false; this.current_download = null; From 6d3f5e6c9496237be9994caa1de592d77a343fdb Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 21:38:58 -0400 Subject: [PATCH 21/40] Improved UI for download only mode Updated API model for DatabaseFile --- Public API v1.yaml | 4 ++ src/api-types/models/DatabaseFile.ts | 2 + .../recent-videos.component.html | 5 +- .../recent-videos.component.scss | 9 +++ .../recent-videos/recent-videos.component.ts | 71 +++++++------------ 5 files changed, 43 insertions(+), 48 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 464871f..860c054 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -2421,6 +2421,10 @@ components: type: number local_view_count: type: number + sub_id: + type: string + registered: + type: number Playlist: required: - uids diff --git a/src/api-types/models/DatabaseFile.ts b/src/api-types/models/DatabaseFile.ts index 796c2fe..bfe8cb4 100644 --- a/src/api-types/models/DatabaseFile.ts +++ b/src/api-types/models/DatabaseFile.ts @@ -30,4 +30,6 @@ export type DatabaseFile = { category?: Category; view_count?: number; local_view_count?: number; + sub_id?: string; + registered?: number; }; \ No newline at end of file diff --git a/src/app/components/recent-videos/recent-videos.component.html b/src/app/components/recent-videos/recent-videos.component.html index 9741911..83c5368 100644 --- a/src/app/components/recent-videos/recent-videos.component.html +++ b/src/app/components/recent-videos/recent-videos.component.html @@ -32,8 +32,9 @@
-
- +
+ +
No files found. diff --git a/src/app/components/recent-videos/recent-videos.component.scss b/src/app/components/recent-videos/recent-videos.component.scss index ab5e741..221a0b3 100644 --- a/src/app/components/recent-videos/recent-videos.component.scss +++ b/src/app/components/recent-videos/recent-videos.component.scss @@ -109,4 +109,13 @@ text-overflow: ellipsis; max-width: 70%; margin: 0 auto; +} + +.blurred { + filter: blur(2px); +} + +.downloading-spinner { + align-self: center; + position: absolute; } \ No newline at end of file diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index b930379..e6eb7d0 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -41,7 +41,7 @@ export class RecentVideosComponent implements OnInit { subscription_files_received = false; file_count = 10; searchChangedSubject: Subject = new Subject(); - downloading_content = {'video': {}, 'audio': {}}; + downloading_content = {}; search_mode = false; search_text = ''; searchIsFocused = false; @@ -148,7 +148,7 @@ export class RecentVideosComponent implements OnInit { }); } - getAllPlaylists() { + getAllPlaylists(): void { this.postsService.getPlaylists().subscribe(res => { this.playlists = res['playlists']; }); @@ -156,22 +156,22 @@ export class RecentVideosComponent implements OnInit { // search - onSearchInputChanged(newvalue) { + onSearchInputChanged(newvalue: string): void { this.normal_files_received = false; this.searchChangedSubject.next(newvalue); } - filterOptionChanged(value) { + filterOptionChanged(value: string): void { localStorage.setItem('filter_property', value['key']); this.getAllFiles(); } - fileTypeFilterChanged(value) { + fileTypeFilterChanged(value: string): void { localStorage.setItem('file_type_filter', value); this.getAllFiles(); } - toggleModeChange() { + toggleModeChange(): void { this.descendingMode = !this.descendingMode; localStorage.setItem('recent_videos_sort_order', this.descendingMode ? 'descending' : 'ascending'); this.getAllFiles(); @@ -179,7 +179,7 @@ export class RecentVideosComponent implements OnInit { // get files - getAllFiles(cache_mode = false) { + getAllFiles(cache_mode = false): void { this.normal_files_received = cache_mode; const current_file_index = (this.paginator?.pageIndex ? this.paginator.pageIndex : 0)*this.pageSize; const sort = {by: this.filterProperty['property'], order: this.descendingMode ? -1 : 1}; @@ -212,7 +212,7 @@ export class RecentVideosComponent implements OnInit { } } - navigateToFile(file, new_tab) { + navigateToFile(file: DatabaseFile, new_tab: boolean): void { localStorage.setItem('player_navigator', this.router.url); if (file.sub_id) { const sub = this.postsService.getSubscriptionByID(file.sub_id); @@ -234,46 +234,26 @@ export class RecentVideosComponent implements OnInit { } } - goToSubscription(file) { + goToSubscription(file: DatabaseFile): void { this.router.navigate(['/subscription', {id: file.sub_id}]); } // downloading - downloadFile(file) { - if (file.sub_id) { - this.downloadSubscriptionFile(file); - } else { - this.downloadNormalFile(file); - } - } - - downloadSubscriptionFile(file) { - const type = (file.isAudio ? 'audio' : 'video') as FileType; - const ext = type === 'audio' ? '.mp3' : '.mp4' - const sub = this.postsService.getSubscriptionByID(file.sub_id); - this.postsService.downloadFileFromServer(file.uid).subscribe(res => { - const blob: Blob = res; - saveAs(blob, file.id + ext); - }, err => { - console.log(err); - }); - } - - downloadNormalFile(file) { + downloadFile(file: DatabaseFile): void { const type = (file.isAudio ? 'audio' : 'video') as FileType; const ext = type === 'audio' ? '.mp3' : '.mp4' const name = file.id; - this.downloading_content[type][name] = true; + this.downloading_content[file.uid] = true; this.postsService.downloadFileFromServer(file.uid).subscribe(res => { - this.downloading_content[type][name] = false; + this.downloading_content[file.uid] = false; const blob: Blob = res; saveAs(blob, decodeURIComponent(name) + ext); - if (!this.postsService.config.Extra.file_manager_enabled) { + if (!this.postsService.config.Extra.file_manager_enabled && !file.sub_id) { // tell server to delete the file once downloaded - this.postsService.deleteFile(file.uid).subscribe(delRes => { - // reload mp4s + this.postsService.deleteFile(file.uid).subscribe(() => { + // reload files this.getAllFiles(); }); } @@ -284,7 +264,6 @@ export class RecentVideosComponent implements OnInit { deleteFile(args) { const file = args.file; - const index = args.index; const blacklistMode = args.blacklistMode; if (file.sub_id) { @@ -294,7 +273,7 @@ export class RecentVideosComponent implements OnInit { } } - deleteNormalFile(file, blacklistMode = false) { + deleteNormalFile(file: DatabaseFile, blacklistMode = false): void { this.postsService.deleteFile(file.uid, blacklistMode).subscribe(result => { if (result) { this.postsService.openSnackBar($localize`Delete success!', 'OK.`); @@ -302,12 +281,12 @@ export class RecentVideosComponent implements OnInit { } else { this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); } - }, err => { + }, () => { this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); }); } - deleteSubscriptionFile(file, blacklistMode = false) { + deleteSubscriptionFile(file: DatabaseFile, blacklistMode = false): void { if (blacklistMode) { this.deleteForever(file); } else { @@ -315,23 +294,23 @@ export class RecentVideosComponent implements OnInit { } } - deleteAndRedownload(file) { + deleteAndRedownload(file: DatabaseFile): void { const sub = this.postsService.getSubscriptionByID(file.sub_id); - this.postsService.deleteSubscriptionFile(sub, file.id, false, file.uid).subscribe(res => { + this.postsService.deleteSubscriptionFile(sub, file.id, false, file.uid).subscribe(() => { this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`); this.removeFileCard(file); }); } - deleteForever(file) { + deleteForever(file: DatabaseFile): void { const sub = this.postsService.getSubscriptionByID(file.sub_id); - this.postsService.deleteSubscriptionFile(sub, file.id, true, file.uid).subscribe(res => { + this.postsService.deleteSubscriptionFile(sub, file.id, true, file.uid).subscribe(() => { this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`); this.removeFileCard(file); }); } - removeFileCard(file_to_remove) { + removeFileCard(file_to_remove: DatabaseFile): void { const index = this.paged_data.map(e => e.uid).indexOf(file_to_remove.uid); this.paged_data.splice(index, 1); this.getAllFiles(true); @@ -356,13 +335,13 @@ export class RecentVideosComponent implements OnInit { // sorting and filtering - sortFiles(a, b) { + sortFiles(a: DatabaseFile, b: DatabaseFile): number { // uses the 'registered' flag as the timestamp const result = b.registered - a.registered; return result; } - durationStringToNumber(dur_str) { + durationStringToNumber(dur_str: string): number { let num_sum = 0; const dur_str_parts = dur_str.split(':'); for (let i = dur_str_parts.length - 1; i >= 0; i--) { From 55d4f746c352bf1cbe78c3e8725177b866bb6898 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 22:28:30 -0400 Subject: [PATCH 22/40] Fixed issue where mongodb attempted to connect even when using local DB --- backend/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/app.js b/backend/app.js index 322bb44..44194bd 100644 --- a/backend/app.js +++ b/backend/app.js @@ -500,7 +500,8 @@ async function loadConfig() { loadConfigValues(); // connect to DB - await db_api.connectToDB(); + if (!config_api.getConfigItem('ytdl_use_local_db')) + await db_api.connectToDB(); db_api.database_initialized = true; db_api.database_initialized_bs.next(true); From adbe7f95d50b469aff63dd74895b2bf833704cd7 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 22:45:38 -0400 Subject: [PATCH 23/40] Fixed issue where reopening file info dialog after editing metadata would show stale data --- .../unified-file-card/unified-file-card.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/components/unified-file-card/unified-file-card.component.ts b/src/app/components/unified-file-card/unified-file-card.component.ts index a9cc7c9..6c4f658 100644 --- a/src/app/components/unified-file-card/unified-file-card.component.ts +++ b/src/app/components/unified-file-card/unified-file-card.component.ts @@ -9,7 +9,6 @@ import localeES from '@angular/common/locales/es'; import localeDE from '@angular/common/locales/de'; import localeZH from '@angular/common/locales/zh'; import localeNB from '@angular/common/locales/nb'; -import { DatabaseFile, Playlist } from 'api-types'; registerLocaleData(localeGB); registerLocaleData(localeFR); @@ -105,12 +104,16 @@ export class UnifiedFileCardComponent implements OnInit { } openFileInfoDialog() { - this.dialog.open(VideoInfoDialogComponent, { + const dialogRef = this.dialog.open(VideoInfoDialogComponent, { data: { file: this.file_obj, }, minWidth: '50vw' - }) + }); + + dialogRef.afterClosed().subscribe(() => { + this.file_obj = dialogRef.componentInstance.file; + }); } emitEditPlaylist() { From fb6cf8548d985c8e0176e6786e88124702a28f3d Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 22:45:54 -0400 Subject: [PATCH 24/40] Added spinner during db connection test --- src/app/settings/settings.component.html | 1 + src/app/settings/settings.component.scss | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 24c1c0c..3d4128a 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -328,6 +328,7 @@
+
diff --git a/src/app/settings/settings.component.scss b/src/app/settings/settings.component.scss index b59df98..1276950 100644 --- a/src/app/settings/settings.component.scss +++ b/src/app/settings/settings.component.scss @@ -105,4 +105,11 @@ .action-buttons { position: absolute; bottom: 15px; +} + +.test-connection-spinner { + display: inline-block; + position: relative; + top: 6px; + margin-left: 10px; } \ No newline at end of file From 314a5047d6fba5779f13bd0e09d31d63d2f6a955 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Jun 2022 22:48:03 -0400 Subject: [PATCH 25/40] Updated translations source file --- src/assets/i18n/messages.en.xlf | 571 ++++++++++++++++++++++++-------- 1 file changed, 440 insertions(+), 131 deletions(-) diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index a24d0b6..98e3fcb 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -94,6 +94,27 @@ Settings menu label + + Successfully created playlist!', ' + + src/app/components/custom-playlists/custom-playlists.component.ts + 56 + + + + ERROR: failed to create playlist!', ' + + src/app/components/custom-playlists/custom-playlists.component.ts + 58 + + + + Playlist successfully removed.', ' + + src/app/components/custom-playlists/custom-playlists.component.ts + 99 + + Date @@ -202,7 +223,7 @@ src/app/settings/settings.component.html - 492 + 498 Cancel @@ -274,112 +295,158 @@ Creating download src/app/components/downloads/downloads.component.ts - 58 + 59 Getting info src/app/components/downloads/downloads.component.ts - 59 + 60 Downloading file src/app/components/downloads/downloads.component.ts - 60 + 61 Complete src/app/components/downloads/downloads.component.ts - 61 + 62 Clear downloads src/app/components/downloads/downloads.component.ts - 130 + 131 Select downloads to clear src/app/components/downloads/downloads.component.ts - 131 + 132 Clear src/app/components/downloads/downloads.component.ts - 132 + 133 Finished downloads src/app/components/downloads/downloads.component.ts - 137 + 138 Paused downloads src/app/components/downloads/downloads.component.ts - 141 + 142 Errored downloads src/app/components/downloads/downloads.component.ts - 145 + 146 Failed to clear finished downloads! src/app/components/downloads/downloads.component.ts - 156 + 157 Cleared downloads! src/app/components/downloads/downloads.component.ts - 158 + 159 + + + + Failed to pause download! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 170 + + + src/app/components/downloads/downloads.component.ts + 218 + + + + Failed to pause all downloads! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 178 + + + + Failed to resume download! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 186 + + + + Failed to resume all downloads! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 194 + + + + Failed to restart download! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 202 + + + + Failed to cancel download! See server logs for more info. + + src/app/components/downloads/downloads.component.ts + 210 Error for src/app/components/downloads/downloads.component.ts - 258 + 259 Copy to clipboard src/app/components/downloads/downloads.component.ts - 260 + 261 Close src/app/components/downloads/downloads.component.ts - 261 + 262 Copied to clipboard! src/app/components/downloads/downloads.component.ts - 269 + 270 @@ -462,6 +529,42 @@ Clear logs button + + Failed to retrieve logs! + + src/app/components/logs-viewer/logs-viewer.component.ts + 46 + + + src/app/components/logs-viewer/logs-viewer.component.ts + 51 + + + + Logs copied to clipboard! + + src/app/components/logs-viewer/logs-viewer.component.ts + 56 + + + + Logs successfully cleared! + + src/app/components/logs-viewer/logs-viewer.component.ts + 75 + + + + Failed to clear logs! + + src/app/components/logs-viewer/logs-viewer.component.ts + 77 + + + src/app/components/logs-viewer/logs-viewer.component.ts + 80 + + Manage role @@ -624,6 +727,10 @@ src/app/components/modify-users/modify-users.component.html 58 + + src/app/create-playlist/create-playlist.component.html + 25 + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html 56 @@ -632,17 +739,13 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html 68 - - src/app/dialogs/modify-playlist/modify-playlist.component.html - 43 - src/app/dialogs/video-info-dialog/video-info-dialog.component.html 63 src/app/settings/settings.component.html - 489 + 495 save user edit action button tooltip @@ -690,15 +793,47 @@ No files found. src/app/components/recent-videos/recent-videos.component.html - 39 + 40 No files found + + Order + + src/app/components/recent-videos/recent-videos.component.html + 53 + + Order + + + Normal order  + + src/app/components/recent-videos/recent-videos.component.html + 55 + + Normal order + + + Reverse order  + + src/app/components/recent-videos/recent-videos.component.html + 56 + + Reverse order + + + Select files + + src/app/components/recent-videos/recent-videos.component.html + 71 + + Select files + File type src/app/components/recent-videos/recent-videos.component.html - 78 + 101 File type @@ -706,7 +841,7 @@ Both src/app/components/recent-videos/recent-videos.component.html - 80 + 103 Both @@ -714,7 +849,7 @@ Video only src/app/components/recent-videos/recent-videos.component.html - 81 + 104 Video only @@ -722,10 +857,28 @@ Audio only src/app/components/recent-videos/recent-videos.component.html - 82 + 105 Audio only + + Delete success!', 'OK. + + src/app/components/recent-videos/recent-videos.component.ts + 279 + + + + Delete failed!', 'OK. + + src/app/components/recent-videos/recent-videos.component.ts + 282 + + + src/app/components/recent-videos/recent-videos.component.ts + 285 + + See more. @@ -961,6 +1114,27 @@ Download Twitch Chat button + + VOD url for this video is not supported. VOD ID must be after "twitch.tv/videos/" + + src/app/components/twitch-chat/twitch-chat.component.ts + 99 + + + + Download failed. + + src/app/components/twitch-chat/twitch-chat.component.ts + 106 + + + + Chat could not be downloaded. + + src/app/components/twitch-chat/twitch-chat.component.ts + 110 + + Auto-generated @@ -1057,30 +1231,57 @@ Create a playlist src/app/create-playlist/create-playlist.component.html - 1 + 2 Create a playlist dialog title + + Modify playlist + + src/app/create-playlist/create-playlist.component.html + 3 + + Modify playlist dialog title + Name src/app/create-playlist/create-playlist.component.html - 6 + 10 src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html 5 - - src/app/dialogs/modify-playlist/modify-playlist.component.html - 8 - src/app/dialogs/video-info-dialog/video-info-dialog.component.html 8 Playlist name placeholder + + Create + + src/app/create-playlist/create-playlist.component.html + 22 + + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 17 + + Create button + + + Playlist updated successfully. + + src/app/create-playlist/create-playlist.component.ts + 69 + + + src/app/create-playlist/create-playlist.component.ts + 75 + + About YoutubeDL-Material @@ -1185,11 +1386,11 @@ src/app/settings/settings.component.html - 283 + 288 src/app/settings/settings.component.html - 289 + 294 About bug click here @@ -1304,6 +1505,13 @@ Cookies upload warning + + Cookies successfully uploaded! + + src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.ts + 42 + + Editing category @@ -1516,53 +1724,34 @@ Custom args hint - - Modify playlist - - src/app/dialogs/modify-playlist/modify-playlist.component.html - 1 - - Modify playlist dialog title - - - Randomize order when playing - - src/app/dialogs/modify-playlist/modify-playlist.component.html - 13 - - Randomize order when playing checkbox label - - - Normal order  + + Restore - src/app/dialogs/modify-playlist/modify-playlist.component.html - 18 + src/app/dialogs/restore-db-dialog/restore-db-dialog.component.html + 25 - Normal order + Restore button - - Reverse order  + + Database successfully restored! - src/app/dialogs/modify-playlist/modify-playlist.component.html - 19 + src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts + 39 - Reverse order - - Add content + + Failed to restore database! See logs for more info. - src/app/dialogs/modify-playlist/modify-playlist.component.html - 24 + src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts + 42 - Add content - - Restore + + Failed to restore database! See browser console for more info. - src/app/dialogs/restore-db-dialog/restore-db-dialog.component.html - 25 + src/app/dialogs/restore-db-dialog/restore-db-dialog.component.ts + 46 - Restore button Create admin account @@ -1580,14 +1769,6 @@ No default admin detected explanation - - Create - - src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html - 17 - - Create - Share playlist @@ -2052,11 +2233,22 @@ Crop to placeholder + + Download failed!', 'OK. + + src/app/main/main.component.ts + 387 + + + src/app/main/main.component.ts + 774 + + Download for has been queued! src/app/main/main.component.ts - 397 + 391 @@ -2067,6 +2259,24 @@ View count label + + Failed to get file information from the server.', 'Dismiss + + src/app/player/player.component.ts + 150 + + + + Failed to load playlist!', ' + + src/app/player/player.component.ts + 186 + + + src/app/player/player.component.ts + 189 + + Main @@ -2489,10 +2699,6 @@ src/app/settings/settings.component.html 255 - - src/app/settings/settings.component.html - 267 - Youtube API Key setting hint @@ -2511,27 +2717,35 @@ Auto download Twitch Chat setting - - Twitch API Key + + Twitch Client ID src/app/settings/settings.component.html 266 - Twitch API Key setting placeholder + Twitch Client ID setting placeholder - - Also known as a Client ID. + + Generating an ID/secret is easy! src/app/settings/settings.component.html 267 - Twitch API Key setting hint AKA preamble + Twitch Client ID setting hint + + + Twitch Client Secret + + src/app/settings/settings.component.html + 272 + + Twitch Client Secret setting placeholder Enables a button to skip ads when viewing supported videos. src/app/settings/settings.component.html - 271 + 276 SponsorBlock API tooltip @@ -2539,7 +2753,7 @@ Use SponsorBlock API src/app/settings/settings.component.html - 271 + 276 Use SponsorBlock API setting @@ -2547,7 +2761,7 @@ Generates NFO files with every download, primarily used by Kodi. src/app/settings/settings.component.html - 274 + 279 Generate NFO files tooltip @@ -2555,7 +2769,7 @@ Generate NFO files src/app/settings/settings.component.html - 274 + 279 Generate NFO files setting @@ -2563,7 +2777,7 @@ to download the official YoutubeDL-Material Chrome extension manually. src/app/settings/settings.component.html - 283 + 288 Chrome click here suffix @@ -2571,7 +2785,7 @@ You must manually load the extension and modify the extension's settings to set the frontend URL. src/app/settings/settings.component.html - 284 + 289 Chrome setup suffix @@ -2579,7 +2793,7 @@ to install the official YoutubeDL-Material Firefox extension right off the Firefox extensions page. src/app/settings/settings.component.html - 289 + 294 Firefox click here suffix @@ -2587,7 +2801,7 @@ Detailed setup instructions. src/app/settings/settings.component.html - 290 + 295 Firefox setup prefix link @@ -2595,7 +2809,7 @@ Not much is required other than changing the extension's settings to set the frontend URL. src/app/settings/settings.component.html - 290 + 295 Firefox setup suffix @@ -2603,7 +2817,7 @@ Drag the link below to your bookmarks, and you're good to go! Just navigate to the YouTube video you'd like to download, and click the bookmark. src/app/settings/settings.component.html - 295 + 300 Bookmarklet instructions @@ -2611,7 +2825,7 @@ Generate 'audio only' bookmarklet src/app/settings/settings.component.html - 296 + 301 Generate audio only bookmarklet checkbox @@ -2619,7 +2833,7 @@ Database src/app/settings/settings.component.html - 305 + 310 Database settings label @@ -2627,7 +2841,7 @@ Database location: src/app/settings/settings.component.html - 311 + 316 Database location label @@ -2635,7 +2849,7 @@ Records per table src/app/settings/settings.component.html - 312 + 317 Records per table label @@ -2643,7 +2857,7 @@ MongoDB Connection String src/app/settings/settings.component.html - 320 + 325 MongoDB Connection String @@ -2651,7 +2865,7 @@ Example: src/app/settings/settings.component.html - 321 + 326 MongoDB Connection String setting hint AKA preamble @@ -2659,7 +2873,7 @@ Test connection string src/app/settings/settings.component.html - 325 + 330 Test connection string button @@ -2667,7 +2881,7 @@ Transfer DB to src/app/settings/settings.component.html - 329 + 335 Transfer DB button @@ -2675,7 +2889,7 @@ Database information could not be retrieved. Check the server logs for more information. src/app/settings/settings.component.html - 333 + 339 Database info not retrieved error message @@ -2683,7 +2897,7 @@ Advanced src/app/settings/settings.component.html - 341 + 347 Host settings label @@ -2691,7 +2905,7 @@ Select a downloader src/app/settings/settings.component.html - 347 + 353 Default downloader select label @@ -2699,7 +2913,7 @@ Use default downloading agent src/app/settings/settings.component.html - 356 + 362 Use default downloading agent setting @@ -2707,7 +2921,7 @@ Select a download agent src/app/settings/settings.component.html - 360 + 366 Custom downloader select label @@ -2715,7 +2929,7 @@ Log Level src/app/settings/settings.component.html - 374 + 380 Log Level label @@ -2723,7 +2937,7 @@ Login expiration src/app/settings/settings.component.html - 386 + 392 Login expiration select label @@ -2731,7 +2945,7 @@ Allow advanced download src/app/settings/settings.component.html - 397 + 403 Allow advanced downloading setting @@ -2739,7 +2953,7 @@ Use Cookies src/app/settings/settings.component.html - 405 + 411 Use cookies setting @@ -2747,7 +2961,7 @@ Set Cookies src/app/settings/settings.component.html - 406 + 412 Set cookies button @@ -2755,7 +2969,7 @@ Restart server src/app/settings/settings.component.html - 418 + 424 Restart server button @@ -2763,7 +2977,7 @@ Users src/app/settings/settings.component.html - 427 + 433 Users settings label @@ -2771,7 +2985,7 @@ Allow user registration src/app/settings/settings.component.html - 433 + 439 Allow registration setting @@ -2779,7 +2993,7 @@ Auth method src/app/settings/settings.component.html - 437 + 443 Auth method select @@ -2787,7 +3001,7 @@ Internal src/app/settings/settings.component.html - 439 + 445 Internal auth method @@ -2795,7 +3009,7 @@ LDAP src/app/settings/settings.component.html - 442 + 448 LDAP auth method @@ -2803,7 +3017,7 @@ LDAP URL src/app/settings/settings.component.html - 449 + 455 LDAP URL @@ -2811,7 +3025,7 @@ Bind DN src/app/settings/settings.component.html - 454 + 460 Bind DN @@ -2819,7 +3033,7 @@ Bind Credentials src/app/settings/settings.component.html - 459 + 465 Bind Credentials @@ -2827,7 +3041,7 @@ Search Base src/app/settings/settings.component.html - 464 + 470 Search Base @@ -2835,7 +3049,7 @@ Search Filter src/app/settings/settings.component.html - 469 + 475 Search Filter @@ -2843,7 +3057,7 @@ Logs src/app/settings/settings.component.html - 478 + 484 Logs settings label @@ -2851,7 +3065,102 @@ You must enable multi-user mode to access this tab. src/app/settings/settings.component.ts - 48 + 49 + + + + Failed to update categories! + + src/app/settings/settings.component.ts + 134 + + + + Language successfully changed! Reload to update the page. + + src/app/settings/settings.component.ts + 208 + + + + Chrome users must drag the 'Alternate URL' link to your bookmarks. + + src/app/settings/settings.component.ts + 236 + + + + Successfully killed all downloads! + + src/app/settings/settings.component.ts + 291 + + + + Failed to kill all downloads! Check logs for details. + + src/app/settings/settings.component.ts + 294 + + + src/app/settings/settings.component.ts + 298 + + + + Restarting! + + src/app/settings/settings.component.ts + 306 + + + + Failed to restart the server. + + src/app/settings/settings.component.ts + 308 + + + + Successfully transfered DB! Reloading info... + + src/app/settings/settings.component.ts + 339 + + + + Failed to transfer DB -- transfer was aborted. Error: + + src/app/settings/settings.component.ts + 342 + + + + Failed to transfer DB -- API call failed. See browser logs for details. + + src/app/settings/settings.component.ts + 346 + + + + Connection successful! + + src/app/settings/settings.component.ts + 356 + + + + Connection failed! Error: + + src/app/settings/settings.component.ts + 358 + + + + Connection failed! Error: Server error. See logs for more info. + + src/app/settings/settings.component.ts + 362 From 2ba1dc63334d1123844d048a3b179d244cc94337 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 01:10:18 -0400 Subject: [PATCH 26/40] Archive refactor to improve reliability and consistency --- backend/app.js | 3 +- backend/db.js | 25 +-------- backend/downloader.js | 17 ++++-- backend/subscriptions.js | 13 ++--- backend/test/tests.js | 28 ++++++++++ backend/utils.js | 55 +++++++++++++++++-- .../unified-file-card.component.html | 2 +- 7 files changed, 102 insertions(+), 41 deletions(-) diff --git a/backend/app.js b/backend/app.js index 44194bd..249224d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -101,7 +101,6 @@ let backendPort = null; let useDefaultDownloadingAgent = null; let customDownloadingAgent = null; let allowSubscriptions = null; -let archivePath = path.join(__dirname, 'appdata', 'archives'); // other needed values let url_domain = null; @@ -506,7 +505,7 @@ async function loadConfig() { db_api.database_initialized_bs.next(true); // creates archive path if missing - await fs.ensureDir(archivePath); + await fs.ensureDir(utils.getArchiveFolder()); // check migrations await checkMigrations(); diff --git a/backend/db.js b/backend/db.js index e35ddca..99d781d 100644 --- a/backend/db.js +++ b/backend/db.js @@ -494,8 +494,7 @@ exports.deleteFile = async (uid, uuid = null, blacklistMode = false) => { let useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive'); if (useYoutubeDLArchive) { - const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path'); - const archive_path = uuid ? path.join(usersFileFolder, uuid, 'archives', `archive_${type}.txt`) : path.join('appdata', 'archives', `archive_${type}.txt`); + const archive_path = utils.getArchiveFolder(type, uuid); // get ID from JSON @@ -503,14 +502,8 @@ exports.deleteFile = async (uid, uuid = null, blacklistMode = false) => { let id = null; if (jsonobj) id = jsonobj.id; - // use subscriptions API to remove video from the archive file, and write it to the blacklist - if (await fs.pathExists(archive_path)) { - const line = id ? await utils.removeIDFromArchive(archive_path, id) : null; - if (blacklistMode && line) await writeToBlacklist(type, line); - } else { - logger.info('Could not find archive file for audio files. Creating...'); - await fs.close(await fs.open(archive_path, 'w')); - } + // Remove file ID from the archive file, and write it to the blacklist (if enabled) + await utils.deleteFileFromArchive(uid, type, archive_path, id, blacklistMode); } if (jsonExists) await fs.unlink(jsonPath); @@ -1110,15 +1103,3 @@ exports.applyFilterLocalDB = (db_path, filter_obj, operation) => { }); return return_val; } - -// archive helper functions - -async function writeToBlacklist(type, line) { - const archivePath = path.join(__dirname, 'appdata', 'archives'); - let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt'); - // adds newline to the beginning of the line - line.replace('\n', ''); - line.replace('\r', ''); - line = '\n' + line; - await fs.appendFile(blacklistPath, line); -} diff --git a/backend/downloader.js b/backend/downloader.js index aef46f1..91fcb98 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -18,8 +18,6 @@ const db_api = require('./db'); const mutex = new Mutex(); let should_check_downloads = true; -const archivePath = path.join(__dirname, 'appdata', 'archives'); - if (db_api.database_initialized) { setupDownloads(); } else { @@ -242,6 +240,7 @@ async function downloadQueuedFile(download_uid) { return new Promise(async resolve => { const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path'); const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path'); + const usersFolderPath = config_api.getConfigItem('ytdl_users_base_path'); await db_api.updateRecord('download_queue', {uid: download_uid}, {step_index: 2, finished_step: false, running: true}); const url = download['url']; @@ -249,9 +248,11 @@ async function downloadQueuedFile(download_uid) { const options = download['options']; const args = download['args']; const category = download['category']; - let fileFolderPath = type === 'audio' ? audioFolderPath : videoFolderPath; // TODO: fix + let fileFolderPath = type === 'audio' ? audioFolderPath : videoFolderPath; if (options.customFileFolderPath) { fileFolderPath = options.customFileFolderPath; + } else if (download['user_uid']) { + fileFolderPath = path.join(usersFolderPath, download['user_uid'], type); } fs.ensureDirSync(fileFolderPath); @@ -375,13 +376,19 @@ async function downloadQueuedFile(download_uid) { exports.generateArgs = async (url, type, options, user_uid = null, simulated = false) => { const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path'); const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path'); + const usersFolderPath = config_api.getConfigItem('ytdl_users_base_path'); const videopath = config_api.getConfigItem('ytdl_default_file_output') ? config_api.getConfigItem('ytdl_default_file_output') : '%(title)s'; const globalArgs = config_api.getConfigItem('ytdl_custom_args'); const useCookies = config_api.getConfigItem('ytdl_use_cookies'); const is_audio = type === 'audio'; - let fileFolderPath = is_audio ? audioFolderPath : videoFolderPath; + let fileFolderPath = type === 'audio' ? audioFolderPath : videoFolderPath; // TODO: fix + if (options.customFileFolderPath) { + fileFolderPath = options.customFileFolderPath; + } else if (user_uid) { + fileFolderPath = path.join(usersFolderPath, user_uid, fileFolderPath); + } if (options.customFileFolderPath) fileFolderPath = options.customFileFolderPath; @@ -628,6 +635,6 @@ function getArchiveFolder(fileFolderPath, options, user_uid) { } else if (user_uid) { return path.join(fileFolderPath, 'archives'); } else { - return path.join(archivePath); + return path.join('appdata', 'archives'); } } \ No newline at end of file diff --git a/backend/subscriptions.js b/backend/subscriptions.js index bad2064..8164eb0 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -196,12 +196,11 @@ async function deleteSubscriptionFile(sub, file, deleteForever, file_uid = null, return false; } else { // check if the user wants the video to be redownloaded (deleteForever === false) - if (!deleteForever && useArchive && sub.archive && retrievedID) { - const archive_path = path.join(sub.archive, 'archive.txt') - // if archive exists, remove line with video ID - if (await fs.pathExists(archive_path)) { - utils.removeIDFromArchive(archive_path, retrievedID); - } + if (useArchive && retrievedID) { + const archive_path = utils.getArchiveFolder(sub.type, user_uid, sub); + + // Remove file ID from the archive file, and write it to the blacklist (if enabled) + await utils.deleteFileFromArchive(file_uid, sub.type, archive_path, retrievedID, deleteForever); } return true; } @@ -322,7 +321,7 @@ function generateOptionsForSubscriptionDownload(sub, user_uid) { selectedHeight: sub.maxQuality && sub.maxQuality !== 'best' ? sub.maxQuality : null, customFileFolderPath: getAppendedBasePath(sub, basePath), customOutput: sub.custom_output ? `${sub.custom_output}` : `${default_output}`, - customArchivePath: path.join(__dirname, basePath, 'archives', sub.name), + customArchivePath: path.join(basePath, 'archives', sub.name), additionalArgs: sub.custom_args } diff --git a/backend/test/tests.js b/backend/test/tests.js index 9236a42..15856c9 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -590,4 +590,32 @@ describe('Tasks', function() { const dummy_task_obj = await db_api.getRecord('tasks', {key: 'dummy_task'}); assert(dummy_task_obj['data']); }); +}); + +describe('Archive', async function() { + const archive_path = path.join('test', 'archives'); + fs.ensureDirSync(archive_path); + const archive_file_path = path.join(archive_path, 'archive_video.txt'); + const blacklist_file_path = path.join(archive_path, 'blacklist_video.txt'); + beforeEach(async function() { + if (fs.existsSync(archive_file_path)) fs.unlinkSync(archive_file_path); + fs.writeFileSync(archive_file_path, 'youtube testing1\nyoutube testing2\nyoutube testing3\n'); + + if (fs.existsSync(blacklist_file_path)) fs.unlinkSync(blacklist_file_path); + fs.writeFileSync(blacklist_file_path, ''); + }); + + it('Delete from archive', async function() { + await utils.deleteFileFromArchive('N/A', 'video', archive_path, 'testing2', false); + const new_archive = fs.readFileSync(archive_file_path); + assert(!new_archive.includes('testing2')); + }); + + it('Delete from archive - blacklist', async function() { + await utils.deleteFileFromArchive('N/A', 'video', archive_path, 'testing2', true); + const new_archive = fs.readFileSync(archive_file_path); + const new_blacklist = fs.readFileSync(blacklist_file_path); + assert(!new_archive.includes('testing2')); + assert(new_blacklist.includes('testing2')); + }); }); \ No newline at end of file diff --git a/backend/utils.js b/backend/utils.js index 1d44681..2ccd00b 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -218,8 +218,11 @@ function deleteJSONFile(file_path, type) { if (fs.existsSync(alternate_json_path)) fs.unlinkSync(alternate_json_path); } -async function removeIDFromArchive(archive_path, id) { - let data = await fs.readFile(archive_path, {encoding: 'utf-8'}); +// archive helper functions + +async function removeIDFromArchive(archive_path, type, id) { + const archive_file = path.join(archive_path, `archive_${type}.txt`); + const data = await fs.readFile(archive_file, {encoding: 'utf-8'}); if (!data) { logger.error('Archive could not be found.'); return; @@ -236,12 +239,34 @@ async function removeIDFromArchive(archive_path, id) { } } + if (lastIndex === -1) return null; + const line = dataArray.splice(lastIndex, 1); // remove the keyword id from the data Array // UPDATE FILE WITH NEW DATA const updatedData = dataArray.join('\n'); - await fs.writeFile(archive_path, updatedData); - if (line) return line; + await fs.writeFile(archive_file, updatedData); + if (line) return Array.isArray(line) && line.length === 1 ? line[0] : line; +} + +async function writeToBlacklist(archive_folder, type, line) { + let blacklistPath = path.join(archive_folder, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt'); + // adds newline to the beginning of the line + line.replace('\n', ''); + line.replace('\r', ''); + line = '\n' + line; + await fs.appendFile(blacklistPath, line); +} + +async function deleteFileFromArchive(uid, type, archive_path, id, blacklistMode) { + const archive_file = path.join(archive_path, `archive_${type}.txt`); + if (await fs.pathExists(archive_path)) { + const line = id ? await removeIDFromArchive(archive_path, type, id) : null; + if (blacklistMode && line) await writeToBlacklist(archive_path, type, line); + } else { + logger.info(`Could not find archive file for file ${uid}. Creating...`); + await fs.close(await fs.open(archive_file, 'w')); + } } function durationStringToNumber(dur_str) { @@ -471,6 +496,25 @@ const searchObjectByString = function(o, s) { return o; } +function getArchiveFolder(type, user_uid = null, sub = null) { + const usersFolderPath = config_api.getConfigItem('ytdl_users_base_path'); + const subsFolderPath = config_api.getConfigItem('ytdl_subscriptions_base_path'); + + if (user_uid) { + if (sub) { + return path.join(usersFolderPath, user_uid, 'subscriptions', 'archives', sub.name); + } else { + return path.join(usersFolderPath, user_uid, type, 'archives'); + } + } else { + if (sub) { + return path.join(subsFolderPath, 'archives', sub.name); + } else { + return path.join('appdata', 'archives'); + } + } +} + // objects function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description, view_count, height, abr) { @@ -500,6 +544,8 @@ module.exports = { fixVideoMetadataPerms: fixVideoMetadataPerms, deleteJSONFile: deleteJSONFile, removeIDFromArchive: removeIDFromArchive, + writeToBlacklist: writeToBlacklist, + deleteFileFromArchive: deleteFileFromArchive, getDownloadedFilesByType: getDownloadedFilesByType, createContainerZipFile: createContainerZipFile, durationStringToNumber: durationStringToNumber, @@ -516,5 +562,6 @@ module.exports = { restartServer: restartServer, injectArgs: injectArgs, searchObjectByString: searchObjectByString, + getArchiveFolder: getArchiveFolder, File: File } diff --git a/src/app/components/unified-file-card/unified-file-card.component.html b/src/app/components/unified-file-card/unified-file-card.component.html index b14f9cd..e26e830 100644 --- a/src/app/components/unified-file-card/unified-file-card.component.html +++ b/src/app/components/unified-file-card/unified-file-card.component.html @@ -34,7 +34,7 @@ restoreDelete and redownload From a2d1b154a311813b61763256fdfc12aa2fc5ce39 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 01:22:22 -0400 Subject: [PATCH 27/40] Fixed broken translations for snackbars --- .../recent-videos/recent-videos.component.ts | 11 +- .../share-media-dialog.component.ts | 14 +- .../subscribe-dialog.component.ts | 13 +- .../update-progress-dialog.component.ts | 12 +- src/app/main/main.component.ts | 4 +- src/app/player/player.component.ts | 12 +- src/app/settings/settings.component.ts | 3 +- src/assets/i18n/messages.en.xlf | 159 ++++++++++++++---- 8 files changed, 151 insertions(+), 77 deletions(-) diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index e6eb7d0..0c2f9f8 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -276,13 +276,13 @@ export class RecentVideosComponent implements OnInit { deleteNormalFile(file: DatabaseFile, blacklistMode = false): void { this.postsService.deleteFile(file.uid, blacklistMode).subscribe(result => { if (result) { - this.postsService.openSnackBar($localize`Delete success!', 'OK.`); + this.postsService.openSnackBar($localize`Delete success!`, $localize`OK.`); this.removeFileCard(file); } else { - this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); + this.postsService.openSnackBar($localize`Delete failed!`, $localize`OK.`); } }, () => { - this.postsService.openSnackBar($localize`Delete failed!', 'OK.`); + this.postsService.openSnackBar($localize`Delete failed!`, $localize`OK.`); }); } @@ -297,7 +297,7 @@ export class RecentVideosComponent implements OnInit { deleteAndRedownload(file: DatabaseFile): void { const sub = this.postsService.getSubscriptionByID(file.sub_id); this.postsService.deleteSubscriptionFile(sub, file.id, false, file.uid).subscribe(() => { - this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`); + this.postsService.openSnackBar($localize`Successfully deleted file: ` + file.id); this.removeFileCard(file); }); } @@ -305,7 +305,7 @@ export class RecentVideosComponent implements OnInit { deleteForever(file: DatabaseFile): void { const sub = this.postsService.getSubscriptionByID(file.sub_id); this.postsService.deleteSubscriptionFile(sub, file.id, true, file.uid).subscribe(() => { - this.postsService.openSnackBar(`Successfully deleted file: '${file.id}'`); + this.postsService.openSnackBar($localize`Successfully deleted file: ` + file.id); this.removeFileCard(file); }); } @@ -316,6 +316,7 @@ export class RecentVideosComponent implements OnInit { this.getAllFiles(true); } + // TODO: Add translation support for these snackbars addFileToPlaylist(info_obj) { const file = info_obj['file']; const playlist_id = info_obj['playlist_id']; diff --git a/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts index 332a461..e4f2912 100644 --- a/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts +++ b/src/app/dialogs/share-media-dialog/share-media-dialog.component.ts @@ -58,31 +58,31 @@ export class ShareMediaDialogComponent implements OnInit { } copiedToClipboard() { - this.openSnackBar('Copied to clipboard!'); + this.postsService.openSnackBar($localize`Copied to clipboard!`); } sharingChanged(event) { if (event.checked) { this.postsService.enableSharing(this.uid, this.is_playlist).subscribe(res => { if (res['success']) { - this.openSnackBar('Sharing enabled.'); + this.postsService.openSnackBar($localize`Sharing enabled.`); this.sharing_enabled = true; } else { - this.openSnackBar('Failed to enable sharing.'); + this.postsService.openSnackBar($localize`Failed to enable sharing.`); } }, err => { - this.openSnackBar('Failed to enable sharing - server error.'); + this.postsService.openSnackBar($localize`Failed to enable sharing - server error.`); }); } else { this.postsService.disableSharing(this.uid, this.is_playlist).subscribe(res => { if (res['success']) { - this.openSnackBar('Sharing disabled.'); + this.postsService.openSnackBar($localize`Sharing disabled.`); this.sharing_enabled = false; } else { - this.openSnackBar('Failed to disable sharing.'); + this.postsService.openSnackBar($localize`Failed to disable sharing.`); } }, err => { - this.openSnackBar('Failed to disable sharing - server error.'); + this.postsService.openSnackBar($localize`Failed to disable sharing - server error.`); }); } } diff --git a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts index 916b5f9..3e7640a 100644 --- a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts +++ b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { MatDialogRef, MatDialog } from '@angular/material/dialog'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { PostsService } from 'app/posts.services'; import { ArgModifierDialogComponent } from '../arg-modifier-dialog/arg-modifier-dialog.component'; @@ -70,7 +69,6 @@ export class SubscribeDialogComponent implements OnInit { ]; constructor(private postsService: PostsService, - private snackBar: MatSnackBar, private dialog: MatDialog, public dialogRef: MatDialogRef) { } @@ -81,7 +79,7 @@ export class SubscribeDialogComponent implements OnInit { if (this.url && this.url !== '') { // timerange must be specified if download_all is false if (!this.download_all && !this.timerange_amount) { - this.openSnackBar('You must specify an amount of time'); + this.postsService.openSnackBar($localize`You must specify an amount of time`); return; } this.subscribing = true; @@ -97,7 +95,7 @@ export class SubscribeDialogComponent implements OnInit { this.dialogRef.close(res['new_sub']); } else { if (res['error']) { - this.openSnackBar('ERROR: ' + res['error']); + this.postsService.openSnackBar($localize`ERROR: ` + res['error']); } this.dialogRef.close(); } @@ -118,11 +116,4 @@ export class SubscribeDialogComponent implements OnInit { } }); } - - public openSnackBar(message: string, action = '') { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - } diff --git a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts index 4862839..888aa85 100644 --- a/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts +++ b/src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { PostsService } from 'app/posts.services'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { UpdaterStatus } from '../../../api-types'; @Component({ @@ -14,7 +13,7 @@ export class UpdateProgressDialogComponent implements OnInit { updateInterval = 250; errored = false; - constructor(private postsService: PostsService, private snackBar: MatSnackBar) { } + constructor(private postsService: PostsService) { } ngOnInit(): void { this.getUpdateProgress(); @@ -28,16 +27,9 @@ export class UpdateProgressDialogComponent implements OnInit { if (res) { this.updateStatus = res; if (this.updateStatus && this.updateStatus['error']) { - this.openSnackBar('Update failed. Check logs for more details.'); + this.postsService.openSnackBar($localize`Update failed. Check logs for more details.`); } } }); } - - public openSnackBar(message: string, action: string = '') { - this.snackBar.open(message, action, { - duration: 2000, - }); - } - } diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 4bfee05..26078ff 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -384,7 +384,7 @@ export class MainComponent implements OnInit { }, () => { // can't access server this.downloadingfile = false; this.current_download = null; - this.postsService.openSnackBar($localize`Download failed!', 'OK.`); + this.postsService.openSnackBar($localize`Download failed!`, 'OK.'); }); if (!this.autoplay && urls.length === 1) { @@ -771,7 +771,7 @@ export class MainComponent implements OnInit { } else if (this.current_download['finished'] && this.current_download['error']) { this.downloadingfile = false; this.current_download = null; - this.postsService.openSnackBar($localize`Download failed!', 'OK.`); + this.postsService.openSnackBar($localize`Download failed!`, 'OK.'); } } else { // console.log('failed to get new download'); diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index 4dbaa8c..053e931 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -3,7 +3,6 @@ import { VgApiService } from '@videogular/ngx-videogular/core'; import { PostsService } from 'app/posts.services'; import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-media-dialog.component'; import { FileType } from '../../api-types'; @@ -109,7 +108,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { } constructor(public postsService: PostsService, private route: ActivatedRoute, private dialog: MatDialog, private router: Router, - public snackBar: MatSnackBar, private cdr: ChangeDetectorRef) { + private cdr: ChangeDetectorRef) { } processConfig(): void { @@ -147,7 +146,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { this.postsService.getFile(this.uid, this.uuid).subscribe(res => { this.db_file = res['file']; if (!this.db_file) { - this.postsService.openSnackBar($localize`Failed to get file information from the server.', 'Dismiss`); + this.postsService.openSnackBar($localize`Failed to get file information from the server.`, 'Dismiss'); return; } this.postsService.incrementViewCount(this.db_file['uid'], null, this.uuid).subscribe(() => undefined, err => { @@ -169,6 +168,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { this.uids = this.subscription.videos.map(video => video['uid']); this.parseFileNames(); }, () => { + // TODO: Make translatable this.postsService.openSnackBar(`Failed to find subscription ${this.sub_id}`, 'Dismiss'); }); } @@ -183,10 +183,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { this.show_player = true; this.parseFileNames(); } else { - this.postsService.openSnackBar($localize`Failed to load playlist!', '`); + this.postsService.openSnackBar($localize`Failed to load playlist!`); } }, () => { - this.postsService.openSnackBar($localize`Failed to load playlist!', '`); + this.postsService.openSnackBar($localize`Failed to load playlist!`); }); } @@ -351,7 +351,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { width: '60vw' }); - dialogRef.afterClosed().subscribe(res => { + dialogRef.afterClosed().subscribe(() => { if (!this.playlist_id) { this.getFile(); } else { diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index f46709f..1771e66 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -176,6 +176,7 @@ export class SettingsComponent implements OnInit { if (confirmed) { this.postsService.deleteCategory(category['uid']).subscribe(res => { if (res['success']) { + // TODO: Make translatable this.postsService.openSnackBar(`Successfully deleted ${category['name']}!`); this.postsService.reloadCategories(); } @@ -233,7 +234,7 @@ export class SettingsComponent implements OnInit { window['external']['AddFavorite'](url, title); } else if (window['chrome']) { // Google Chrome - this.postsService.openSnackBar($localize`Chrome users must drag the \'Alternate URL\' link to your bookmarks.`); + this.postsService.openSnackBar($localize`Chrome users must drag the 'Alternate URL' link to your bookmarks.`); } else if (window['sidebar']) { // Firefox window['sidebar'].addPanel(title, url, ''); diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 98e3fcb..5c40019 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -448,6 +448,10 @@ src/app/components/downloads/downloads.component.ts 270 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 61 + User name @@ -861,15 +865,30 @@ Audio only - - Delete success!', 'OK. + + Delete success! + + src/app/components/recent-videos/recent-videos.component.ts + 279 + + + + OK. src/app/components/recent-videos/recent-videos.component.ts 279 + + src/app/components/recent-videos/recent-videos.component.ts + 282 + + + src/app/components/recent-videos/recent-videos.component.ts + 285 + - - Delete failed!', 'OK. + + Delete failed! src/app/components/recent-videos/recent-videos.component.ts 282 @@ -879,6 +898,17 @@ 285 + + Successfully deleted file: + + src/app/components/recent-videos/recent-videos.component.ts + 300 + + + src/app/components/recent-videos/recent-videos.component.ts + 308 + + See more. @@ -1191,12 +1221,16 @@ Delete and redownload subscription video button - - Delete forever + + Delete and blacklist src/app/components/unified-file-card/unified-file-card.component.html 37 + + src/app/components/unified-file-card/unified-file-card.component.html + 40 + Delete forever subscription video button @@ -1211,14 +1245,6 @@ Delete video button - - Delete and blacklist - - src/app/components/unified-file-card/unified-file-card.component.html - 40 - - Delete and blacklist video button - Edit @@ -1817,6 +1843,48 @@ Copy to clipboard button + + Sharing enabled. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 68 + + + + Failed to enable sharing. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 71 + + + + Failed to enable sharing - server error. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 74 + + + + Sharing disabled. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 79 + + + + Failed to disable sharing. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 82 + + + + Failed to disable sharing - server error. + + src/app/dialogs/share-media-dialog/share-media-dialog.component.ts + 85 + + Subscribe to playlist or channel @@ -1865,6 +1933,20 @@ Subscribe button + + You must specify an amount of time + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts + 82 + + + + ERROR: + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts + 98 + + Type: @@ -1921,6 +2003,13 @@ Update progress dialog title + + Update failed. Check logs for more details. + + src/app/dialogs/update-progress-dialog/update-progress-dialog.component.ts + 30 + + Update task schedule @@ -2233,8 +2322,8 @@ Crop to placeholder - - Download failed!', 'OK. + + Download failed! src/app/main/main.component.ts 387 @@ -2259,22 +2348,22 @@ View count label - - Failed to get file information from the server.', 'Dismiss + + Failed to get file information from the server. src/app/player/player.component.ts 150 - - Failed to load playlist!', ' + + Failed to load playlist! src/app/player/player.component.ts - 186 + 187 src/app/player/player.component.ts - 189 + 190 @@ -3079,88 +3168,88 @@ Language successfully changed! Reload to update the page. src/app/settings/settings.component.ts - 208 + 209 Chrome users must drag the 'Alternate URL' link to your bookmarks. src/app/settings/settings.component.ts - 236 + 237 Successfully killed all downloads! src/app/settings/settings.component.ts - 291 + 292 Failed to kill all downloads! Check logs for details. src/app/settings/settings.component.ts - 294 + 295 src/app/settings/settings.component.ts - 298 + 299 Restarting! src/app/settings/settings.component.ts - 306 + 307 Failed to restart the server. src/app/settings/settings.component.ts - 308 + 309 Successfully transfered DB! Reloading info... src/app/settings/settings.component.ts - 339 + 340 Failed to transfer DB -- transfer was aborted. Error: src/app/settings/settings.component.ts - 342 + 343 Failed to transfer DB -- API call failed. See browser logs for details. src/app/settings/settings.component.ts - 346 + 347 Connection successful! src/app/settings/settings.component.ts - 356 + 357 Connection failed! Error: src/app/settings/settings.component.ts - 358 + 359 Connection failed! Error: Server error. See logs for more info. src/app/settings/settings.component.ts - 362 + 363 From b1448d95e5cf8c6509e3e1356debe4b3db291527 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 01:44:19 -0400 Subject: [PATCH 28/40] Added resolution and audio bitrate to video info dialog --- Public API v1.yaml | 6 ++++ src/api-types/models/DatabaseFile.ts | 8 +++++ .../video-info-dialog.component.html | 8 +++++ src/app/player/player.component.ts | 8 +++-- src/assets/i18n/messages.en.xlf | 30 ++++++++++++++----- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 860c054..37b9f1f 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -2425,6 +2425,12 @@ components: type: string registered: type: number + height: + type: number + description: In pixels, only for videos + abr: + type: number + description: In Kbps Playlist: required: - uids diff --git a/src/api-types/models/DatabaseFile.ts b/src/api-types/models/DatabaseFile.ts index bfe8cb4..f36af9e 100644 --- a/src/api-types/models/DatabaseFile.ts +++ b/src/api-types/models/DatabaseFile.ts @@ -32,4 +32,12 @@ export type DatabaseFile = { local_view_count?: number; sub_id?: string; registered?: number; + /** + * In pixels, only for videos + */ + height?: number; + /** + * In Kbps + */ + abr?: number; }; \ No newline at end of file diff --git a/src/app/dialogs/video-info-dialog/video-info-dialog.component.html b/src/app/dialogs/video-info-dialog/video-info-dialog.component.html index 8f99be9..d30862c 100644 --- a/src/app/dialogs/video-info-dialog/video-info-dialog.component.html +++ b/src/app/dialogs/video-info-dialog/video-info-dialog.component.html @@ -47,6 +47,14 @@ +
+
Resolution: 
+
{{new_file.height ? new_file.height + 'p' : 'N/A'}}
+
+
+
Audio bitrate: 
+
{{new_file.abr ? new_file.abr + ' Kbps' : 'N/A'}}
+
File size: 
{{new_file.size ? filesize(new_file.size) : 'N/A'}}
diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index 053e931..4cba6c5 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -361,12 +361,16 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { } openFileInfoDialog(): void { - this.dialog.open(VideoInfoDialogComponent, { + const dialogRef = this.dialog.open(VideoInfoDialogComponent, { data: { file: this.db_file, }, minWidth: '50vw' - }) + }); + + dialogRef.afterClosed().subscribe(() => { + this.db_file = dialogRef.componentInstance.file; + }); } setPlaybackTimestamp(time: number): void { diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 5c40019..a7ea877 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -641,7 +641,7 @@ src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 62 + 70 Close @@ -745,7 +745,7 @@ src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 63 + 71 src/app/settings/settings.component.html @@ -2154,11 +2154,27 @@ Local view count + + Resolution: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 51 + + Video resolution property + + + Audio bitrate: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 55 + + Video audio bitrate property + File size: src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 51 + 59 Video file size property @@ -2166,7 +2182,7 @@ Path: src/app/dialogs/video-info-dialog/video-info-dialog.component.html - 55 + 63 Video path property @@ -2352,18 +2368,18 @@ Failed to get file information from the server. src/app/player/player.component.ts - 150 + 149 Failed to load playlist! src/app/player/player.component.ts - 187 + 186 src/app/player/player.component.ts - 190 + 189 From 64b4b5a2b48b3a02cae1df72e0bb4d9127e214ad Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 01:44:55 -0400 Subject: [PATCH 29/40] Fixed #600, where selecting a max resolution for subscriptions was broken --- backend/downloader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/downloader.js b/backend/downloader.js index 91fcb98..1ac0260 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -421,7 +421,7 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f if (customQualityConfiguration) { qualityPath = ['-f', customQualityConfiguration, '--merge-output-format', 'mp4']; } else if (selectedHeight && selectedHeight !== '' && !is_audio) { - qualityPath = ['-f', `'(mp4)[height=${selectedHeight}'`]; + qualityPath = ['-f', `'(mp4)[height=${selectedHeight}]`]; } else if (is_audio) { qualityPath = ['--audio-quality', maxBitrate ? maxBitrate : '0'] } From a0c36bf1a160040783af43f6afd975370d842595 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 18:37:54 -0400 Subject: [PATCH 30/40] Subscription videos now skip the collect info step during download process, reducing calls to video host --- Public API v1.yaml | 2 ++ backend/downloader.js | 11 ++++++----- backend/subscriptions.js | 5 ++++- backend/test/tests.js | 8 ++++++++ backend/utils.js | 22 ++++++++++++++++++++++ src/api-types/models/Download.ts | 1 + 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 37b9f1f..1d9e0dd 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -2512,6 +2512,8 @@ components: type: string sub_name: type: string + prefetched_info: + type: object Task: required: - key diff --git a/backend/downloader.js b/backend/downloader.js index 1ac0260..a0ae6e6 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -26,7 +26,7 @@ if (db_api.database_initialized) { }); } -exports.createDownload = async (url, type, options, user_uid = null, sub_id = null, sub_name = null) => { +exports.createDownload = async (url, type, options, user_uid = null, sub_id = null, sub_name = null, prefetched_info = null) => { return await mutex.runExclusive(async () => { const download = { url: url, @@ -35,6 +35,7 @@ exports.createDownload = async (url, type, options, user_uid = null, sub_id = nu user_uid: user_uid, sub_id: sub_id, sub_name: sub_name, + prefetched_info: prefetched_info, options: options, uid: uuid(), step_index: 0, @@ -185,7 +186,7 @@ async function collectInfo(download_uid) { let args = await exports.generateArgs(url, type, options, download['user_uid']); // get video info prior to download - let info = await exports.getVideoInfoByURL(url, args, download_uid); + let info = download['prefetched_info'] ? download['prefetched_info'] : await exports.getVideoInfoByURL(url, args, download_uid); if (!info) { // info failed, error presumably already recorded @@ -227,7 +228,8 @@ async function collectInfo(download_uid) { options: options, files_to_check_for_progress: files_to_check_for_progress, expected_file_size: expected_file_size, - title: playlist_title ? playlist_title : info['title'] + title: playlist_title ? playlist_title : info['title'], + prefetched_info: null }); } @@ -572,8 +574,7 @@ exports.getVideoInfoByURL = async (url, args = [], download_uid = null) => { function filterArgs(args, isAudio) { const video_only_args = ['--add-metadata', '--embed-subs', '--xattrs']; const audio_only_args = ['-x', '--extract-audio', '--embed-thumbnail']; - const args_to_remove = isAudio ? video_only_args : audio_only_args; - return args.filter(x => !args_to_remove.includes(x)); + return utils.filterArgs(args, isAudio ? video_only_args : audio_only_args); } async function checkDownloadPercent(download_uid) { diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 8164eb0..295818b 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -296,7 +296,8 @@ async function getVideosForSub(sub, user_uid = null) { for (let j = 0; j < files_to_download.length; j++) { const file_to_download = files_to_download[j]; - await downloader_api.createDownload(file_to_download['webpage_url'], sub.type || 'video', base_download_options, user_uid, sub.id, sub.name); + file_to_download['formats'] = utils.stripPropertiesFromObject(file_to_download['formats'], ['format_id', 'filesize', 'filesize_approx']); // prevent download object from blowing up in size + await downloader_api.createDownload(file_to_download['webpage_url'], sub.type || 'video', base_download_options, user_uid, sub.id, sub.name, file_to_download); } resolve(files_to_download); @@ -420,6 +421,8 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de downloadConfig.push('--no-clean-infojson'); } + downloadConfig = utils.filterArgs(downloadConfig, ['--write-comments']); + return downloadConfig; } diff --git a/backend/test/tests.js b/backend/test/tests.js index 15856c9..b640687 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -618,4 +618,12 @@ describe('Archive', async function() { assert(!new_archive.includes('testing2')); assert(new_blacklist.includes('testing2')); }); +}); + +describe('Utils', async function() { + it('Strip properties', async function() { + const test_obj = {test1: 'test1', test2: 'test2', test3: 'test3'}; + const stripped_obj = utils.stripPropertiesFromObject(test_obj, ['test1', 'test3']); + assert(!stripped_obj['test1'] && stripped_obj['test2'] && !stripped_obj['test3']) + }); }); \ No newline at end of file diff --git a/backend/utils.js b/backend/utils.js index 2ccd00b..4c36230 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -481,6 +481,10 @@ function injectArgs(original_args, new_args) { return updated_args; } +function filterArgs(args, args_to_remove) { + return args.filter(x => !args_to_remove.includes(x)); +} + const searchObjectByString = function(o, s) { s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties s = s.replace(/^\./, ''); // strip a leading dot @@ -496,6 +500,22 @@ const searchObjectByString = function(o, s) { return o; } +function stripPropertiesFromObject(obj, properties, whitelist = false) { + if (!whitelist) { + const new_obj = JSON.parse(JSON.stringify(obj)); + for (let field of properties) { + delete new_obj[field]; + } + return new_obj; + } + + const new_obj = {}; + for (let field of properties) { + new_obj[field] = obj[field]; + } + return new_obj; +} + function getArchiveFolder(type, user_uid = null, sub = null) { const usersFolderPath = config_api.getConfigItem('ytdl_users_base_path'); const subsFolderPath = config_api.getConfigItem('ytdl_subscriptions_base_path'); @@ -561,7 +581,9 @@ module.exports = { fetchFile: fetchFile, restartServer: restartServer, injectArgs: injectArgs, + filterArgs: filterArgs, searchObjectByString: searchObjectByString, + stripPropertiesFromObject: stripPropertiesFromObject, getArchiveFolder: getArchiveFolder, File: File } diff --git a/src/api-types/models/Download.ts b/src/api-types/models/Download.ts index 6556da0..db447c3 100644 --- a/src/api-types/models/Download.ts +++ b/src/api-types/models/Download.ts @@ -22,4 +22,5 @@ export type Download = { user_uid?: string; sub_id?: string; sub_name?: string; + prefetched_info?: any; }; \ No newline at end of file From 0ffd7022d0a44ac7e361652e97069e681aa4429c Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Thu, 23 Jun 2022 19:49:48 -0400 Subject: [PATCH 31/40] Removed nodemon Fixed several dependency vulnerabilities --- backend/package-lock.json | 716 +------------------------------------- backend/package.json | 16 +- backend/utils.js | 2 +- package-lock.json | 82 +++-- package.json | 2 +- 5 files changed, 61 insertions(+), 757 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 421898c..8340118 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -51,19 +51,6 @@ "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==" }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, "@types/ldapjs": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.2.tgz", @@ -82,11 +69,6 @@ "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -112,26 +94,6 @@ "uri-js": "^4.2.2" } }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -182,7 +144,7 @@ "append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, "archiver": { "version": "5.3.1", @@ -384,21 +346,6 @@ } } }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -446,9 +393,9 @@ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "buffer-indexof-polyfill": { "version": "1.0.1", @@ -461,35 +408,11 @@ "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" }, "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } + "streamsearch": "^1.1.0" } }, "bytes": { @@ -497,27 +420,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -527,11 +429,6 @@ "get-intrinsic": "^1.0.2" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -545,60 +442,11 @@ "traverse": ">=0.3.0 <0.4" } }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -619,14 +467,6 @@ } } }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, "color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -748,19 +588,6 @@ "json5": "^2.1.1" } }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, "connected-domain": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/connected-domain/-/connected-domain-1.0.0.tgz", @@ -839,11 +666,6 @@ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -865,24 +687,6 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -912,51 +716,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "requires": { - "is-obj": "^2.0.0" - } - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -981,11 +745,6 @@ } } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1008,11 +767,6 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, "enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -1036,11 +790,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1359,42 +1108,6 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "requires": { - "ini": "1.3.7" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - } - } - }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -1427,11 +1140,6 @@ "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, "has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -1445,11 +1153,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" - }, "hashish": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz", @@ -1471,11 +1174,6 @@ "zero-fill": "^2.2.3" } }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, "http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -1516,21 +1214,6 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1545,11 +1228,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1573,24 +1251,11 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -1599,15 +1264,6 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, "is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -1617,26 +1273,11 @@ "define-properties": "^1.1.3" } }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -1662,11 +1303,6 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1695,11 +1331,6 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1788,27 +1419,11 @@ "safe-buffer": "^5.0.1" } }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "requires": { - "package-json": "^6.3.0" - } - }, "lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -2009,11 +1624,6 @@ "steno": "^0.4.1" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2027,21 +1637,6 @@ "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "md5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", @@ -2109,11 +1704,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2314,16 +1904,15 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", "requires": { "append-field": "^1.0.0", - "busboy": "^0.2.11", + "busboy": "^1.0.0", "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.4", "object-assign": "^4.1.1", - "on-finished": "^2.3.0", "type-is": "^1.6.4", "xtend": "^4.0.0" } @@ -2399,56 +1988,11 @@ "sorted-array-functions": "^1.3.0" } }, - "nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "requires": { - "abbrev": "1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -2517,11 +2061,6 @@ "require-at": "^1.0.6" } }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, "p-finally": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", @@ -2543,24 +2082,6 @@ "p-limit": "^3.0.2" } }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2683,11 +2204,6 @@ "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2720,11 +2236,6 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2739,14 +2250,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "requires": { - "escape-goat": "^2.0.0" - } - }, "qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", @@ -2783,17 +2286,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "read-last-lines": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/read-last-lines/-/read-last-lines-1.7.2.tgz", @@ -2820,30 +2312,6 @@ "minimatch": "^3.0.4" } }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "requires": { - "rc": "^1.2.8" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -2893,14 +2361,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -2953,21 +2413,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, "send": { "version": "0.17.2", "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", @@ -3120,9 +2565,9 @@ } }, "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" }, "string-width": { "version": "4.2.2", @@ -3167,39 +2612,11 @@ "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" - } - } - }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, "table-parser": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/table-parser/-/table-parser-0.1.3.tgz", @@ -3220,11 +2637,6 @@ "readable-stream": "^3.1.1" } }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" - }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -3246,11 +2658,6 @@ "thenify": ">= 3.1.0 < 4" } }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3264,14 +2671,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "requires": { - "nopt": "~1.0.10" - } - }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -3314,11 +2713,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3331,31 +2725,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "requires": { - "debug": "^2.2.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "universalify": { "version": "1.0.0", @@ -3400,26 +2770,6 @@ } } }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -3428,14 +2778,6 @@ "punycode": "^2.1.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3504,14 +2846,6 @@ "isexe": "^2.0.0" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "requires": { - "string-width": "^4.0.0" - } - }, "winston": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", @@ -3569,22 +2903,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" - }, "xmlbuilder2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index bf926a1..855fc74 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,20 +5,9 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "nodemon app.js", + "start": "pm2-runtime --raw pm2.config.js", "debug": "set YTDL_MODE=debug && node app.js" }, - "nodemonConfig": { - "ignore": [ - "*.js", - "appdata/*", - "public/*" - ], - "watch": [ - "restart_update.json", - "restart_general.json" - ] - }, "repository": { "type": "git", "url": "" @@ -47,11 +36,10 @@ "mocha": "^9.2.2", "moment": "^2.29.2", "mongodb": "^3.6.9", - "multer": "^1.4.2", + "multer": "1.4.5-lts.1", "node-fetch": "^2.6.7", "node-id3": "^0.1.14", "node-schedule": "^2.1.0", - "nodemon": "^2.0.7", "passport": "^0.4.1", "passport-http": "^0.3.0", "passport-jwt": "^4.0.0", diff --git a/backend/utils.js b/backend/utils.js index 4c36230..534cbcf 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -443,7 +443,7 @@ async function fetchFile(url, path, file_label) { async function restartServer(is_update = false) { logger.info(`${is_update ? 'Update complete! ' : ''}Restarting server...`); - // the following line restarts the server through nodemon + // the following line restarts the server through pm2 fs.writeFileSync(`restart${is_update ? '_update' : '_general'}.json`, 'internal use only'); process.exit(1); } diff --git a/package-lock.json b/package-lock.json index eab9318..037aee5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "youtube-dl-material", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3675,7 +3675,7 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { @@ -3785,12 +3785,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true } } }, @@ -3945,7 +3939,7 @@ "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -4666,7 +4660,7 @@ "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -4956,7 +4950,7 @@ "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "integrity": "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==", "dev": true }, "ecc-jsbn": { @@ -4976,20 +4970,20 @@ "dev": true }, "electron": { - "version": "13.6.6", - "resolved": "https://registry.npmjs.org/electron/-/electron-13.6.6.tgz", - "integrity": "sha512-TP2Bl1nTxaH1yRmlYiF7imzvKE/NASE0cl6wOYA3AaP/UrBGc4L3NwJfn5Z55o+1t4TH8vCRxENufESyb32HhA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-19.0.6.tgz", + "integrity": "sha512-S9Yud32nKhB0iWC0lGl2JXz4FQnCiLCnP5Vehm1/CqyeICcQGmgQaZl2HYpCJ2pesKIsYL9nsgmku/10cxm/gg==", "dev": true, "requires": { - "@electron/get": "^1.0.1", - "@types/node": "^14.6.2", + "@electron/get": "^1.14.1", + "@types/node": "^16.11.26", "extract-zip": "^1.0.3" }, "dependencies": { "@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", + "version": "16.11.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.41.tgz", + "integrity": "sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ==", "dev": true } } @@ -5956,7 +5950,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -6024,7 +6018,7 @@ "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" @@ -6412,9 +6406,9 @@ }, "dependencies": { "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "optional": true, "requires": { @@ -6442,9 +6436,9 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "optional": true, "requires": { @@ -7455,7 +7449,7 @@ "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", "dev": true }, "json-parse-better-errors": { @@ -7511,7 +7505,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "requires": { "graceful-fs": "^4.1.6" @@ -8561,6 +8555,12 @@ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true + }, "npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -8584,7 +8584,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "optional": true } @@ -9370,7 +9370,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "performance-now": { @@ -9817,7 +9817,7 @@ "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", "dev": true }, "pretty-bytes": { @@ -9865,7 +9865,7 @@ "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true, "optional": true }, @@ -9951,8 +9951,7 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "resolved": "", "dev": true }, "strip-ansi": { @@ -10167,8 +10166,7 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "resolved": "", "dev": true }, "ansi-styles": { @@ -10576,7 +10574,7 @@ "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dev": true, "requires": { "lowercase-keys": "^1.0.0" @@ -10826,7 +10824,7 @@ "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, "optional": true }, @@ -11937,7 +11935,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, "typescript": { @@ -12027,7 +12025,7 @@ "url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "dev": true, "requires": { "prepend-http": "^2.0.0" @@ -12576,7 +12574,7 @@ "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", diff --git a/package.json b/package.json index 07d79e9..a746ff5 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@typescript-eslint/eslint-plugin": "^4.29.0", "@typescript-eslint/parser": "^4.29.0", "codelyzer": "^6.0.0", - "electron": "^13.6.6", + "electron": "^19.0.6", "eslint": "^7.32.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", From c6b7e7bc4c14ce4cfad2c1f39b39ff8eb8661287 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 00:13:36 -0400 Subject: [PATCH 32/40] Added rpi 4 as a "special case" in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d983581..6dc0c46 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Alternatively, you can port forward the port specified in the config (defaults t ### Host-specific instructions -If you're on a Synology NAS, unRAID or any other possible special case you can check if there's known issues or instructions both in the issue tracker and in the [Wiki!](https://github.com/Tzahi12345/YoutubeDL-Material/wiki#environment-specific-guideshelp) +If you're on a Synology NAS, unRAID, Raspberry Pi 4 or any other possible special case you can check if there's known issues or instructions both in the issue tracker and in the [Wiki!](https://github.com/Tzahi12345/YoutubeDL-Material/wiki#environment-specific-guideshelp) ### Setup From fecefde3ad32c69aa43ee774b649d49d90452aca Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 13:14:28 -0400 Subject: [PATCH 33/40] Removed pm2 global install in favor of local node_modules (#662) --- Dockerfile | 5 +- Dockerfile.heroku | 2 +- backend/entrypoint.sh | 2 +- backend/package-lock.json | 1251 ++++++++++++++++++++++++++++++++++++- backend/package.json | 1 + package-lock.json | 6 +- 6 files changed, 1249 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index cb82c64..b05dbb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,8 +47,7 @@ RUN npm config set strict-ssl false && \ # Final image FROM base -RUN npm install -g pm2 && \ - apt update && \ +RUN apt update && \ apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley && \ apt clean && \ rm -rf /var/lib/apt/lists/* @@ -65,4 +64,4 @@ RUN chmod +x /app/fix-scripts/*.sh EXPOSE 17442 ENTRYPOINT [ "/app/entrypoint.sh" ] -CMD [ "pm2-runtime","--raw","pm2.config.js" ] +CMD [ "npm","start" ] diff --git a/Dockerfile.heroku b/Dockerfile.heroku index 16e451c..977a446 100644 --- a/Dockerfile.heroku +++ b/Dockerfile.heroku @@ -1,2 +1,2 @@ FROM tzahi12345/youtubedl-material:latest -CMD [ "pm2-runtime", "pm2.config.js" ] \ No newline at end of file +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index 59ffdb6..8be147f 100755 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh set -eu -CMD="pm2-runtime pm2.config.js" +CMD="npm start" # 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 8340118..6c5c9cf 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -51,6 +51,216 @@ "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==" }, + "@opencensus/core": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz", + "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "@opencensus/propagation-b3": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz", + "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==", + "requires": { + "@opencensus/core": "^0.0.8", + "uuid": "^3.2.1" + }, + "dependencies": { + "@opencensus/core": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz", + "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "@pm2/agent": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.1.tgz", + "integrity": "sha512-QKHMm6yexcvdDfcNE7PL9D6uEjoQPGRi+8dh+rc4Hwtbpsbh5IAvZbz3BVGjcd4HaX6pt2xGpOohG7/Y2L4QLw==", + "requires": { + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.0.0-1", + "fclone": "~1.0.11", + "nssocket": "0.6.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~5.0.0", + "semver": "~7.2.0", + "ws": "~7.4.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.3.tgz", + "integrity": "sha512-utbW9Z7ZxVvwiIWkdOMLOR9G/NFXh2aRucghkVrEMJWuC++r3lCkBC3LwqBinyHzGMAJxY5tn6VakZGHObq5ig==" + } + } + }, + "@pm2/io": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.0.tgz", + "integrity": "sha512-3rToDVJaRoob5Lq8+7Q2TZFruoEkdORxwzFpZaqF4bmH6Bkd7kAbdPrI/z8X6k1Meq5rTtScM7MmDgppH6aLlw==", + "requires": { + "@opencensus/core": "0.0.9", + "@opencensus/propagation-b3": "0.0.8", + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "6.3.0", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "eventemitter2": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", + "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + } + } + }, + "@pm2/js-api": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.6.7.tgz", + "integrity": "sha512-jiJUhbdsK+5C4zhPZNnyA3wRI01dEc6a2GhcQ9qI38DyIk+S+C8iC3fGjcjUbt/viLYKPjlAaE+hcT2/JMQPXw==", + "requires": { + "async": "^2.6.3", + "axios": "^0.21.0", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "ws": "^7.0.0" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "eventemitter2": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.5.tgz", + "integrity": "sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "requires": { + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, "@types/ldapjs": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.2.tgz", @@ -83,6 +293,39 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -94,6 +337,19 @@ "uri-js": "^4.2.2" } }, + "amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==" + }, + "amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "requires": { + "amp": "0.3.1" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -216,11 +472,28 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "requires": { + "tslib": "^2.0.1" + } + }, "async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, + "async-listener": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "requires": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + } + }, "async-mutex": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.1.tgz", @@ -317,11 +590,21 @@ "readable-stream": "^3.4.0" } }, + "blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==" + }, "bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" }, + "bodec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", + "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==" + }, "body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", @@ -442,11 +725,48 @@ "traverse": ">=0.3.0 <0.4" } }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" }, + "charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cli-tableau": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", + "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "requires": { + "chalk": "3.0.0" + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -515,6 +835,11 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, "compress-commons": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", @@ -613,6 +938,15 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, + "continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "requires": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } + }, "cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -651,6 +985,11 @@ "luxon": "^1.26.0" } }, + "croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==" + }, "cross-spawn": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", @@ -666,6 +1005,11 @@ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, + "culvert": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", + "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -674,6 +1018,16 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" + }, + "dayjs": { + "version": "1.8.36", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", + "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -687,6 +1041,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -696,6 +1055,17 @@ "object-keys": "^1.1.1" } }, + "degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "requires": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -767,6 +1137,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emitter-listener": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "requires": { + "shimmer": "^1.2.0" + } + }, "enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -785,6 +1163,14 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "requires": { + "ansi-colors": "^4.1.1" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -800,16 +1186,43 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==" + }, "execa": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz", @@ -913,16 +1326,36 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, + "fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==" + }, "fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1051,6 +1484,38 @@ "rimraf": "2" } }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1079,6 +1544,57 @@ "pump": "^3.0.0" } }, + "get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "requires": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1087,6 +1603,16 @@ "assert-plus": "^1.0.0" } }, + "git-node-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", + "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==" + }, + "git-sha1": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", + "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==" + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1140,6 +1666,11 @@ "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, "has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -1186,6 +1717,31 @@ "toidentifier": "1.0.1" } }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -1196,18 +1752,42 @@ "sshpk": "^1.7.0" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } }, "ieee754": { "version": "1.2.1", @@ -1228,6 +1808,16 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1251,6 +1841,14 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1318,6 +1916,17 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-git": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", + "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==", + "requires": { + "bodec": "^0.1.0", + "culvert": "^0.1.2", + "git-sha1": "^0.1.2", + "pako": "^0.2.5" + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1424,6 +2033,11 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==" + }, "lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -1482,6 +2096,15 @@ "verror": "^1.8.1" } }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -1555,6 +2178,11 @@ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -1855,6 +2483,11 @@ } } }, + "module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, "moment": { "version": "2.29.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", @@ -1942,6 +2575,11 @@ } } }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -1957,11 +2595,41 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" }, + "needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -2001,6 +2669,22 @@ "path-key": "^3.0.0" } }, + "nssocket": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz", + "integrity": "sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w==", + "requires": { + "eventemitter2": "~0.4.14", + "lazy": "~1.0.11" + }, + "dependencies": { + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==" + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -2061,6 +2745,19 @@ "require-at": "^1.0.6" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "p-finally": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", @@ -2082,6 +2779,52 @@ "p-limit": "^3.0.2" } }, + "pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pac-resolver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", + "requires": { + "degenerator": "^3.0.2", + "ip": "^1.1.5", + "netmask": "^2.0.2" + } + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2174,6 +2917,11 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -2194,16 +2942,217 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, + "pidusage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.0.tgz", + "integrity": "sha512-8VJLToXhj+RYZGNVw8oxc7dS54iCQXUJ+MDFHezQ/fwF5B8W4OWodAMboc1wb08S/4LiHwAmkT4ohf/d3YPPsw==", + "requires": { + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, + "pm2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.2.0.tgz", + "integrity": "sha512-PO5hMVhQ85cTszFM++6v07Me9hPJMkFbHjkFigtMMk+La8ty2wCi2dlBTeZYJDhPUSjK8Ccltpq2buNRcyMOTw==", + "requires": { + "@pm2/agent": "~2.0.0", + "@pm2/io": "~5.0.0", + "@pm2/js-api": "~0.6.7", + "@pm2/pm2-version-check": "^1.0.4", + "async": "~3.2.0", + "blessed": "0.1.81", + "chalk": "3.0.0", + "chokidar": "^3.5.1", + "cli-tableau": "^2.0.0", + "commander": "2.15.1", + "croner": "~4.1.92", + "dayjs": "~1.8.25", + "debug": "^4.3.1", + "enquirer": "2.3.6", + "eventemitter2": "5.0.1", + "fclone": "1.0.11", + "mkdirp": "1.0.4", + "needle": "2.4.0", + "pidusage": "~3.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.1", + "pm2-deploy": "~1.0.2", + "pm2-multimeter": "^0.1.2", + "pm2-sysmonit": "^1.2.8", + "promptly": "^2", + "semver": "^7.2", + "source-map-support": "0.5.19", + "sprintf-js": "1.1.2", + "vizion": "~2.2.1", + "yamljs": "0.3.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, + "pm2-axon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz", + "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==", + "requires": { + "amp": "~0.3.1", + "amp-message": "~0.1.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pm2-axon-rpc": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz", + "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==", + "requires": { + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pm2-deploy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz", + "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==", + "requires": { + "run-series": "^1.1.8", + "tv4": "^1.3.0" + } + }, + "pm2-multimeter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", + "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==", + "requires": { + "charm": "~0.1.1" + } + }, + "pm2-sysmonit": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz", + "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==", + "optional": true, + "requires": { + "async": "^3.2.0", + "debug": "^4.3.1", + "pidusage": "^2.0.21", + "systeminformation": "^5.7", + "tx2": "~1.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "optional": true, + "requires": { + "safe-buffer": "^5.2.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true + } + } + }, "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2214,6 +3163,14 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, + "promptly": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", + "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==", + "requires": { + "read": "^1.0.4" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2223,6 +3180,54 @@ "ipaddr.js": "1.9.1" } }, + "proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "requires": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "ps-node": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ps-node/-/ps-node-0.1.6.tgz", @@ -2286,6 +3291,14 @@ } } }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "requires": { + "mute-stream": "~0.0.4" + } + }, "read-last-lines": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/read-last-lines/-/read-last-lines-1.7.2.tgz", @@ -2312,6 +3325,14 @@ "minimatch": "^3.0.4" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -2361,6 +3382,41 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "require-in-the-middle": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz", + "integrity": "sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -2369,6 +3425,11 @@ "glob": "^7.1.3" } }, + "run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==" + }, "rxjs": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz", @@ -2408,6 +3469,11 @@ "sparse-bitfield": "^3.0.3" } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2482,6 +3548,11 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, "shortid": { "version": "2.2.15", "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.15.tgz", @@ -2503,11 +3574,64 @@ "is-arrayish": "^0.3.1" } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "sorted-array-functions": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2617,6 +3741,25 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "systeminformation": { + "version": "5.11.21", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.11.21.tgz", + "integrity": "sha512-aYuoelPUEItkeFi9d2EgGYZur6CgGaPAOUv9K5h1rJn5EyAYIXtonxJN3Dn58zQ3BFbj9FggaxaVBGg/pNRngA==", + "optional": true + }, "table-parser": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/table-parser/-/table-parser-0.1.3.tgz", @@ -2708,11 +3851,33 @@ "safe-buffer": "^5.0.1" } }, + "tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==" + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "tx2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", + "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==", + "optional": true, + "requires": { + "json-stringify-safe": "^5.0.1" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2824,6 +3989,36 @@ "extsprintf": "^1.2.0" } }, + "vizion": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz", + "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==", + "requires": { + "async": "^2.6.3", + "git-node-fs": "^1.0.0", + "ini": "^1.3.5", + "js-git": "^0.7.8" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + } + } + }, + "vm2": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.9.tgz", + "integrity": "sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw==", + "requires": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -2873,6 +4068,11 @@ "triple-beam": "^1.3.0" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, "workerpool": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", @@ -2903,6 +4103,11 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + }, "xmlbuilder2": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz", @@ -2934,6 +4139,11 @@ } } }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -2949,6 +4159,25 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "requires": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + } + } + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/backend/package.json b/backend/package.json index 855fc74..4c116d2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -45,6 +45,7 @@ "passport-jwt": "^4.0.0", "passport-ldapauth": "^3.0.1", "passport-local": "^1.0.0", + "pm2": "^5.2.0", "progress": "^2.0.3", "ps-node": "^0.1.6", "read-last-lines": "^1.7.2", diff --git a/package-lock.json b/package-lock.json index 037aee5..db6f9f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9951,7 +9951,8 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "strip-ansi": { @@ -10166,7 +10167,8 @@ "dependencies": { "ansi-regex": { "version": "5.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "ansi-styles": { From 573cca0b2f8b538abfff3cee94123ea04cbb86ff Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 17:29:06 -0400 Subject: [PATCH 34/40] Completed deprecation of streamingOnly mode for subscriptions --- Public API v1.yaml | 4 ---- backend/app.js | 15 +-------------- backend/subscriptions.js | 5 ----- src/api-types/models/Subscription.ts | 1 - .../recent-videos/recent-videos.component.ts | 11 +---------- .../edit-subscription-dialog.component.html | 5 ----- .../subscribe-dialog.component.html | 5 ----- .../subscribe-dialog.component.ts | 3 --- 8 files changed, 2 insertions(+), 47 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index 1d9e0dd..f9879e8 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -1826,7 +1826,6 @@ components: required: - name - url - - streamingOnly type: object properties: name: @@ -2608,7 +2607,6 @@ components: - url - type - user_uid - - streamingOnly - isPlaylist - videos type: object @@ -2624,8 +2622,6 @@ components: user_uid: type: string nullable: true - streamingOnly: - type: boolean isPlaylist: type: boolean archive: diff --git a/backend/app.js b/backend/app.js index 249224d..514d82b 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1272,7 +1272,7 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => { subscription = JSON.parse(JSON.stringify(subscription)); // get sub videos - if (subscription.name && !subscription.streamingOnly) { + if (subscription.name) { var parsed_files = await db_api.getRecords('files', {sub_id: subscription.id}); // subscription.videos; subscription['videos'] = parsed_files; // loop through files for extra processing @@ -1282,19 +1282,6 @@ app.post('/api/getSubscription', optionalJwt, async (req, res) => { if (file && file['url'].includes('twitch.tv')) file['chat_exists'] = fs.existsSync(file['path'].substring(0, file['path'].length - 4) + '.twitch_chat.json'); } - res.send({ - subscription: subscription, - files: parsed_files - }); - } else if (subscription.name && subscription.streamingOnly) { - // return list of videos - let parsed_files = []; - if (subscription.videos) { - for (let i = 0; i < subscription.videos.length; i++) { - const video = subscription.videos[i]; - parsed_files.push(new utils.File(video.title, video.title, video.thumbnail, false, video.duration, video.url, video.uploader, video.size, null, null, video.upload_date, video.view_count, video.height, video.abr)); - } - } res.send({ subscription: subscription, files: parsed_files diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 295818b..d424444 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -389,11 +389,6 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de downloadConfig.push('--download-archive', archive_path); } - // if streaming only mode, just get the list of videos - if (sub.streamingOnly) { - downloadConfig = ['-f', 'best', '--dump-json']; - } - if (sub.timerange && !redownload) { downloadConfig.push('--dateafter', sub.timerange); } diff --git a/src/api-types/models/Subscription.ts b/src/api-types/models/Subscription.ts index 5c87886..2e280d8 100644 --- a/src/api-types/models/Subscription.ts +++ b/src/api-types/models/Subscription.ts @@ -10,7 +10,6 @@ export type Subscription = { id: string; type: FileType; user_uid: string | null; - streamingOnly: boolean; isPlaylist: boolean; archive?: string; timerange?: string; diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 0c2f9f8..90522ff 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -215,18 +215,9 @@ export class RecentVideosComponent implements OnInit { navigateToFile(file: DatabaseFile, new_tab: boolean): void { localStorage.setItem('player_navigator', this.router.url); if (file.sub_id) { - const sub = this.postsService.getSubscriptionByID(file.sub_id); - if (sub.streamingOnly) { - // streaming only mode subscriptions - // !new_tab ? this.router.navigate(['/player', {name: file.id, - // url: file.requested_formats ? file.requested_formats[0].url : file.url}]) - // : window.open(`/#/player;name=${file.id};url=${file.requested_formats ? file.requested_formats[0].url : file.url}`); - } else { - // normal subscriptions !new_tab ? this.router.navigate(['/player', {uid: file.uid, - type: file.isAudio ? 'audio' : 'video'}]) + type: file.isAudio ? 'audio' : 'video'}]) : window.open(`/#/player;uid=${file.uid};type=${file.isAudio ? 'audio' : 'video'}`); - } } else { // normal files !new_tab ? this.router.navigate(['/player', {type: file.isAudio ? 'audio' : 'video', uid: file.uid}]) diff --git a/src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html b/src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html index f76af9f..51ed8ee 100644 --- a/src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html +++ b/src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html @@ -34,11 +34,6 @@
-
-
- Streaming-only mode -
-
diff --git a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html index 7b53c01..fe53377 100644 --- a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html +++ b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html @@ -47,11 +47,6 @@ Audio-only mode
-
-
- Streaming-only mode -
-
diff --git a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts index 3e7640a..507dc86 100644 --- a/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts +++ b/src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts @@ -21,9 +21,6 @@ export class SubscribeDialogComponent implements OnInit { // state subscribing = false; - // no videos actually downloaded, just streamed - streamingOnlyMode = false; - // audio only mode audioOnlyMode = false; From a4a004547531617bc797584068f82020d41690c0 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 17:33:10 -0400 Subject: [PATCH 35/40] Fixed error where sub with no videos would crash if none existed and redownload fresh uploads was enabled (#480) --- backend/subscriptions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/subscriptions.js b/backend/subscriptions.js index d424444..854579f 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -476,6 +476,7 @@ async function updateSubscriptionProperty(sub, assignment_obj) { async function setFreshUploads(sub) { const sub_files = await db_api.getRecords('files', {sub_id: sub.id}); + if (!sub_files) return; const current_date = new Date().toISOString().split('T')[0].replace(/-/g, ''); sub_files.forEach(async file => { if (current_date === file['upload_date'].replace(/-/g, '')) { From d225e84a0380c25bfa8ca902ffea78b32728aeaa Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 19:26:34 -0400 Subject: [PATCH 36/40] Fixed issue where errored single videos in playlist/sub downloads in yt-dlp would cause the entire sub check to fail (#493) --- backend/subscriptions.js | 94 +++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 854579f..78ccbd2 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -241,65 +241,22 @@ async function getVideosForSub(sub, user_uid = null) { logger.verbose('Subscription: finished check for ' + sub.name); if (err && !output) { logger.error(err.stderr ? err.stderr : err.message); - if (err.stderr.includes('This video is unavailable')) { + if (err.stderr.includes('This video is unavailable') || err.stderr.includes('Private video')) { logger.info('An error was encountered with at least one video, backup method will be used.') try { - // TODO: reimplement - - // const outputs = err.stdout.split(/\r\n|\r|\n/); - // for (let i = 0; i < outputs.length; i++) { - // const output = JSON.parse(outputs[i]); - // await handleOutputJSON(sub, output, i === 0, multiUserMode) - // if (err.stderr.includes(output['id']) && archive_path) { - // // we found a video that errored! add it to the archive to prevent future errors - // if (sub.archive) { - // archive_dir = sub.archive; - // archive_path = path.join(archive_dir, 'archive.txt') - // fs.appendFileSync(archive_path, output['id']); - // } - // } - // } + const outputs = err.stdout.split(/\r\n|\r|\n/); // .map(jsonStr => JSON.parse(jsonStr)); + const files_to_download = await handleOutputJSON(outputs, sub, user_uid); + resolve(files_to_download); } catch(e) { logger.error('Backup method failed. See error below:'); logger.error(e); } + } else { + logger.error('Subscription check failed!'); } resolve(false); } else if (output) { - if (config_api.getConfigItem('ytdl_subscriptions_redownload_fresh_uploads')) { - await setFreshUploads(sub, user_uid); - checkVideosForFreshUploads(sub, user_uid); - } - - if (output.length === 0 || (output.length === 1 && output[0] === '')) { - logger.verbose('No additional videos to download for ' + sub.name); - resolve(true); - return; - } - - const output_jsons = []; - for (let i = 0; i < output.length; i++) { - let output_json = null; - try { - output_json = JSON.parse(output[i]); - output_jsons.push(output_json); - } catch(e) { - output_json = null; - } - if (!output_json) { - continue; - } - } - - const files_to_download = await getFilesToDownload(sub, output_jsons); - const base_download_options = generateOptionsForSubscriptionDownload(sub, user_uid); - - for (let j = 0; j < files_to_download.length; j++) { - const file_to_download = files_to_download[j]; - file_to_download['formats'] = utils.stripPropertiesFromObject(file_to_download['formats'], ['format_id', 'filesize', 'filesize_approx']); // prevent download object from blowing up in size - await downloader_api.createDownload(file_to_download['webpage_url'], sub.type || 'video', base_download_options, user_uid, sub.id, sub.name, file_to_download); - } - + const files_to_download = await handleOutputJSON(output, sub, user_uid); resolve(files_to_download); } }); @@ -309,6 +266,43 @@ async function getVideosForSub(sub, user_uid = null) { }); } +async function handleOutputJSON(output, sub, user_uid) { + if (config_api.getConfigItem('ytdl_subscriptions_redownload_fresh_uploads')) { + await setFreshUploads(sub, user_uid); + checkVideosForFreshUploads(sub, user_uid); + } + + if (output.length === 0 || (output.length === 1 && output[0] === '')) { + logger.verbose('No additional videos to download for ' + sub.name); + return []; + } + + const output_jsons = []; + for (let i = 0; i < output.length; i++) { + let output_json = null; + try { + output_json = JSON.parse(output[i]); + output_jsons.push(output_json); + } catch(e) { + output_json = null; + } + if (!output_json) { + continue; + } + } + + const files_to_download = await getFilesToDownload(sub, output_jsons); + const base_download_options = generateOptionsForSubscriptionDownload(sub, user_uid); + + for (let j = 0; j < files_to_download.length; j++) { + const file_to_download = files_to_download[j]; + file_to_download['formats'] = utils.stripPropertiesFromObject(file_to_download['formats'], ['format_id', 'filesize', 'filesize_approx']); // prevent download object from blowing up in size + await downloader_api.createDownload(file_to_download['webpage_url'], sub.type || 'video', base_download_options, user_uid, sub.id, sub.name, file_to_download); + } + + return files_to_download; +} + function generateOptionsForSubscriptionDownload(sub, user_uid) { let basePath = null; if (user_uid) From 19a3ffc1180f522787c7f775347eda062200b2ec Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Fri, 24 Jun 2022 19:27:46 -0400 Subject: [PATCH 37/40] Minor code cleanup --- backend/subscriptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 78ccbd2..6e9e454 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -244,7 +244,7 @@ async function getVideosForSub(sub, user_uid = null) { if (err.stderr.includes('This video is unavailable') || err.stderr.includes('Private video')) { logger.info('An error was encountered with at least one video, backup method will be used.') try { - const outputs = err.stdout.split(/\r\n|\r|\n/); // .map(jsonStr => JSON.parse(jsonStr)); + const outputs = err.stdout.split(/\r\n|\r|\n/); const files_to_download = await handleOutputJSON(outputs, sub, user_uid); resolve(files_to_download); } catch(e) { From 86cbfea08fc6d1826313b3ad0ea26e9fdf98678b Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sat, 25 Jun 2022 17:22:08 -0400 Subject: [PATCH 38/40] UI & logs now use proper fork name rather than just youtube-dl --- Public API v1.yaml | 2 ++ backend/downloader.js | 7 +++---- src/api-types/models/Task.ts | 1 + src/app/components/tasks/tasks.component.ts | 20 ++++++++------------ src/app/main/main.component.ts | 3 ++- src/app/posts.services.ts | 5 +++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Public API v1.yaml b/Public API v1.yaml index f9879e8..9f335f8 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -2527,6 +2527,8 @@ components: properties: key: type: string + title: + type: string last_ran: type: number last_confirmed: diff --git a/backend/downloader.js b/backend/downloader.js index a0ae6e6..ce4223c 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -376,6 +376,8 @@ async function downloadQueuedFile(download_uid) { // helper functions exports.generateArgs = async (url, type, options, user_uid = null, simulated = false) => { + const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader'); + const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path'); const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path'); const usersFolderPath = config_api.getConfigItem('ytdl_users_base_path'); @@ -413,8 +415,6 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f if (!is_audio && !is_youtube) { // tiktok videos fail when using the default format qualityPath = null; - } else if (!is_audio && !is_youtube && (url.includes('reddit') || url.includes('pornhub'))) { - qualityPath = ['-f', 'bestvideo+bestaudio'] } if (customArgs) { @@ -505,7 +505,6 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f downloadConfig.push('-r', rate_limit); } - const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader'); if (default_downloader === 'yt-dlp') { downloadConfig.push('--no-clean-infojson'); } @@ -515,7 +514,7 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f // filter out incompatible args downloadConfig = filterArgs(downloadConfig, is_audio); - if (!simulated) logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`); + if (!simulated) logger.verbose(`${default_downloader} args being used: ${downloadConfig.join(',')}`); return downloadConfig; } diff --git a/src/api-types/models/Task.ts b/src/api-types/models/Task.ts index 2533c98..e6f68be 100644 --- a/src/api-types/models/Task.ts +++ b/src/api-types/models/Task.ts @@ -4,6 +4,7 @@ export type Task = { key: string; + title?: string; last_ran: number; last_confirmed: number; running: boolean; diff --git a/src/app/components/tasks/tasks.component.ts b/src/app/components/tasks/tasks.component.ts index 8fec372..c4dd2a1 100644 --- a/src/app/components/tasks/tasks.component.ts +++ b/src/app/components/tasks/tasks.component.ts @@ -7,6 +7,7 @@ import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialo import { RestoreDbDialogComponent } from 'app/dialogs/restore-db-dialog/restore-db-dialog.component'; import { UpdateTaskScheduleDialogComponent } from 'app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component'; import { PostsService } from 'app/posts.services'; +import { Task } from 'api-types'; @Component({ selector: 'app-tasks', @@ -17,7 +18,7 @@ export class TasksComponent implements OnInit { interval_id = null; tasks_check_interval = 1500; - tasks = null; + tasks: Task[] = null; tasks_retrieved = false; displayedColumns: string[] = ['title', 'last_ran', 'last_confirmed', 'status', 'actions']; @@ -55,6 +56,11 @@ export class TasksComponent implements OnInit { getTasks(): void { this.postsService.getTasks().subscribe(res => { + for (const task of res['tasks']) { + if (task.title.includes('youtube-dl')) { + task.title = task.title.replace('youtube-dl', this.postsService.config.Advanced.default_downloader); + } + } if (this.tasks) { if (JSON.stringify(this.tasks) === JSON.stringify(res['tasks'])) return; for (const task of res['tasks']) { @@ -94,7 +100,7 @@ export class TasksComponent implements OnInit { }); } - scheduleTask(task: any): void { + scheduleTask(task: Task): void { // open dialog const dialogRef = this.dialog.open(UpdateTaskScheduleDialogComponent, { data: { @@ -152,13 +158,3 @@ export class TasksComponent implements OnInit { } } - -export interface Task { - key: string; - title: string; - last_ran: number; - last_confirmed: number; - running: boolean; - confirming: boolean; - data: unknown; -} \ No newline at end of file diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 26078ff..54db31d 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -591,7 +591,8 @@ export class MainComponent implements OnInit { if (passwordIndex !== -1 && passwordIndex !== simulated_args.length - 1) { simulated_args[passwordIndex + 1] = simulated_args[passwordIndex + 1].replace(/./g, '*'); } - this.simulatedOutput = `youtube-dl ${this.url} ${simulated_args.join(' ')}`; + const downloader = this.postsService.config.Advanced.default_downloader; + this.simulatedOutput = `${downloader} ${this.url} ${simulated_args.join(' ')}`; } }); } diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 6417f5c..9115437 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -100,7 +100,8 @@ import { UpdateFileRequest, Sort, FileTypeFilter, - GetAllFilesRequest + GetAllFilesRequest, + GetAllTasksResponse } from '../api-types'; import { isoLangs } from './settings/locales_list'; import { Title } from '@angular/platform-browser'; @@ -596,7 +597,7 @@ export class PostsService implements CanActivate { } getTasks() { - return this.http.post(this.path + 'getTasks', {}, this.httpOptions); + return this.http.post(this.path + 'getTasks', {}, this.httpOptions); } resetTasks() { From 9a250b5c58471b941e2d23e0b4ee8951b10809f4 Mon Sep 17 00:00:00 2001 From: Glassed Silver Date: Mon, 27 Jun 2022 02:06:13 +0200 Subject: [PATCH 39/40] Update unified-file-card.component.html --- .../unified-file-card/unified-file-card.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/unified-file-card/unified-file-card.component.html b/src/app/components/unified-file-card/unified-file-card.component.html index e26e830..6290adf 100644 --- a/src/app/components/unified-file-card/unified-file-card.component.html +++ b/src/app/components/unified-file-card/unified-file-card.component.html @@ -34,10 +34,10 @@ restoreDelete and redownload - + From 742129bf6af89610d59f88f2abb7ddb1f737cc34 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 26 Jun 2022 20:58:59 -0400 Subject: [PATCH 40/40] Updated source translation file --- src/assets/i18n/messages.en.xlf | 90 ++++++++++++++------------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index a7ea877..1085678 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -207,7 +207,7 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 66 + 61 src/app/dialogs/restore-db-dialog/restore-db-dialog.component.html @@ -215,7 +215,7 @@ src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 79 + 74 src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.html @@ -741,7 +741,7 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 68 + 63 src/app/dialogs/video-info-dialog/video-info-dialog.component.html @@ -869,44 +869,44 @@ Delete success! src/app/components/recent-videos/recent-videos.component.ts - 279 + 270 OK. src/app/components/recent-videos/recent-videos.component.ts - 279 + 270 src/app/components/recent-videos/recent-videos.component.ts - 282 + 273 src/app/components/recent-videos/recent-videos.component.ts - 285 + 276 Delete failed! src/app/components/recent-videos/recent-videos.component.ts - 282 + 273 src/app/components/recent-videos/recent-videos.component.ts - 285 + 276 Successfully deleted file: src/app/components/recent-videos/recent-videos.component.ts - 300 + 291 src/app/components/recent-videos/recent-videos.component.ts - 308 + 299 @@ -1065,75 +1065,75 @@ Successfully ran task! src/app/components/tasks/tasks.component.ts - 78 + 84 Failed to run task! src/app/components/tasks/tasks.component.ts - 79 + 85 src/app/components/tasks/tasks.component.ts - 81 + 87 Successfully confirmed task! src/app/components/tasks/tasks.component.ts - 89 + 95 Failed to confirm task! src/app/components/tasks/tasks.component.ts - 90 + 96 src/app/components/tasks/tasks.component.ts - 92 + 98 Reset tasks src/app/components/tasks/tasks.component.ts - 132 + 138 Would you like to reset your tasks? All your schedules will be removed as well. src/app/components/tasks/tasks.component.ts - 133 + 139 Reset src/app/components/tasks/tasks.component.ts - 134 + 140 Tasks successfully reset! src/app/components/tasks/tasks.component.ts - 142 + 148 Failed to reset tasks! src/app/components/tasks/tasks.component.ts - 144 + 150 src/app/components/tasks/tasks.component.ts - 147 + 153 @@ -1221,8 +1221,8 @@ Delete and redownload subscription video button - - Delete and blacklist + + Delete and don't download again src/app/components/unified-file-card/unified-file-card.component.html 37 @@ -1570,11 +1570,11 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 53 + 48 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 66 + 61 Category custom file output placeholder @@ -1586,11 +1586,11 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 56 + 51 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 69 + 64 src/app/main/main.component.html @@ -1610,11 +1610,11 @@ src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 57 + 52 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 70 + 65 src/app/main/main.component.html @@ -1710,27 +1710,15 @@ Max quality placeholder - - Streaming-only mode - - src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 39 - - - src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 52 - - Streaming-only mode - Custom args src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 44 + 39 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 57 + 52 src/app/main/main.component.html @@ -1742,11 +1730,11 @@ These are added after the standard args. src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html - 47 + 42 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 60 + 55 Custom args hint @@ -1929,7 +1917,7 @@ Subscribe src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html - 81 + 76 Subscribe button @@ -1937,14 +1925,14 @@ You must specify an amount of time src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts - 82 + 79 ERROR: src/app/dialogs/subscribe-dialog/subscribe-dialog.component.ts - 98 + 95 @@ -2346,7 +2334,7 @@ src/app/main/main.component.ts - 774 + 775