diff --git a/Public API v1.yaml b/Public API v1.yaml index 96ba54d..e508fd5 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -437,11 +437,7 @@ paths: content: application/json: schema: - type: object - properties: {} - application/xml: - schema: - $ref: '#/components/schemas/body_15' + $ref: '#/components/schemas/DownloadFileRequest' responses: '200': description: 'The file itself is in the response, as well as an options object.' @@ -474,7 +470,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_17' + $ref: '#/components/schemas/DownloadArchiveRequest' responses: '200': description: The archive text file is sent as a response @@ -495,7 +491,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_14' + $ref: '#/components/schemas/UpdaterStatus' security: - Auth query parameter: [] /api/updateServer: @@ -509,7 +505,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_18' + $ref: '#/components/schemas/UpdateServerRequest' responses: '200': description: OK @@ -590,7 +586,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_16' + $ref: '#/components/schemas/GenerateNewApiKeyResponse' security: - Auth query parameter: [] /api/deleteMp3: @@ -627,7 +623,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_17' + $ref: '#/components/schemas/ConfigResponse' security: - Auth query parameter: [] /api/setConfig: @@ -639,7 +635,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_22' + $ref: '#/components/schemas/SetConfigRequest' responses: '200': description: OK @@ -647,6 +643,8 @@ paths: application/json: schema: $ref: '#/components/schemas/SuccessObject' + '404': + description: Tried to save invalid config file. security: - Auth query parameter: [] /api/downloads: @@ -937,6 +935,13 @@ components: enum: - audio - video + Config: + required: + - YoutubeDLMaterial + type: object + properties: + YoutubeDLMaterial: + type: object BaseDownloadRequest: required: - url @@ -1304,29 +1309,33 @@ components: type: string type: $ref: '#/components/schemas/FileType' - body_15: + DownloadFileRequest: required: - fileNames - type type: object properties: fileNames: - type: array - description: Array of 1 or more files to download - items: - type: string + oneOf: + - type: string + - type: array + description: Array of 1 or more files to download + items: + type: string zip_mode: type: boolean type: - type: string + $ref: '#/components/schemas/FileType' outputName: type: string fullPathProvided: + type: boolean + uuid: type: string subscriptionName: type: boolean description: Only used for subscriptions - subscriptionPlaylist: + subPlaylist: type: boolean description: Only used for subscriptions DeleteFileRequest: @@ -1351,7 +1360,7 @@ components: properties: archive_dir: type: string - inline_response_200_14: + UpdaterStatus: required: - details - updating @@ -1361,7 +1370,11 @@ components: type: boolean details: type: string - body_18: + error: + type: boolean + UpdateServerRequest: + required: + - tag type: object properties: tag: @@ -1387,7 +1400,7 @@ components: properties: unhashed_pin: type: string - inline_response_200_16: + GenerateNewApiKeyResponse: required: - new_api_key type: object @@ -1404,23 +1417,23 @@ components: type: string blacklistMode: type: boolean - inline_response_200_17: + ConfigResponse: required: - config_file - success type: object properties: config_file: - type: object + $ref: '#/components/schemas/Config' success: type: boolean - body_22: + SetConfigRequest: required: - new_config_file type: object properties: new_config_file: - type: object + $ref: '#/components/schemas/Config' DatabaseFile: required: - duration @@ -1621,7 +1634,7 @@ components: subscriptions: type: array items: - $ref: '#/components/schemas/inline_response_200_9_subscription' + $ref: '#/components/schemas/Subscription' created: type: number role: @@ -1643,6 +1656,11 @@ components: - sharing - advanced_download - downloads_manager + YesNo: + type: string + enum: + - 'yes' + - 'no' BaseChangePermissionsRequest: required: - permission @@ -1652,10 +1670,7 @@ components: permission: $ref: '#/components/schemas/UserPermission' new_value: - type: string - enum: - - 'yes' - - 'no' + $ref: '#/components/schemas/YesNo' ChangeUserPermissionsRequest: allOf: - $ref: '#/components/schemas/BaseChangePermissionsRequest' diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 8333f79..11a6dcc 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { PostsService } from 'app/posts.services'; import { Router } from '@angular/router'; +import type { FileType } from '../../../api-types'; @Component({ selector: 'app-recent-videos', @@ -161,7 +162,7 @@ export class RecentVideosComponent implements OnInit { // normal subscriptions !new_tab ? this.router.navigate(['/player', {fileNames: file.id, type: file.isAudio ? 'audio' : 'video', subscriptionName: sub.name, - subPlaylist: sub.isPlaylist}]) + subPlaylist: sub.isPlaylist}]) : window.open(`/#/player;fileNames=${file.id};type=${file.isAudio ? 'audio' : 'video'};subscriptionName=${sub.name};subPlaylist=${sub.isPlaylist}`); } } else { @@ -186,7 +187,7 @@ export class RecentVideosComponent implements OnInit { } downloadSubscriptionFile(file) { - const type = file.isAudio ? 'audio' : 'video'; + 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.id, type, null, null, sub.name, sub.isPlaylist, @@ -199,7 +200,7 @@ export class RecentVideosComponent implements OnInit { } downloadNormalFile(file) { - const type = file.isAudio ? 'audio' : 'video'; + const type = (file.isAudio ? 'audio' : 'video') as FileType; const ext = type === 'audio' ? '.mp3' : '.mp4' const name = file.id; this.downloading_content[type][name] = true; @@ -276,7 +277,7 @@ export class RecentVideosComponent implements OnInit { const result = b.registered - a.registered; return result; } - + durationStringToNumber(dur_str) { let num_sum = 0; const dur_str_parts = dur_str.split(':'); 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 fe1fbf4..9ca066f 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,7 @@ import { Component, OnInit } from '@angular/core'; import { PostsService } from 'app/posts.services'; import { MatSnackBar } from '@angular/material/snack-bar'; +import type { UpdaterStatus } from '../../../api-types'; @Component({ selector: 'app-update-progress-dialog', @@ -9,7 +10,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; }) export class UpdateProgressDialogComponent implements OnInit { - updateStatus = null; + updateStatus: UpdaterStatus = null; updateInterval = 250; errored = false; diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 28e672c..c0bb894 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -20,7 +20,7 @@ import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.com import { Platform } from '@angular/cdk/platform'; import { v4 as uuid } from 'uuid'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; -import type { FileType } from 'api-types'; +import type { FileType } from '../../api-types'; export let audioFilesMouseHovering = false; export let videoFilesMouseHovering = false; @@ -34,7 +34,7 @@ export interface Download { percent_complete: number; downloading: boolean; is_playlist: boolean; - error: boolean | string; + error?: boolean | string; fileNames?: string[]; complete?: boolean; timestamp_start?: number; @@ -740,7 +740,7 @@ export class MainComponent implements OnInit { downloadAudioFile(name) { this.downloading_content['audio'][name] = true; - this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => { + this.postsService.downloadFileFromServer(name, 'audio' as FileType).subscribe(res => { this.downloading_content['audio'][name] = false; const blob: Blob = res; saveAs(blob, decodeURIComponent(name) + '.mp3'); @@ -757,7 +757,7 @@ export class MainComponent implements OnInit { downloadVideoFile(name) { this.downloading_content['video'][name] = true; - this.postsService.downloadFileFromServer(name, 'video').subscribe(res => { + this.postsService.downloadFileFromServer(name, 'video' as FileType).subscribe(res => { this.downloading_content['video'][name] = false; const blob: Blob = res; saveAs(blob, decodeURIComponent(name) + '.mp4'); diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts index 79eb371..57e2ea4 100644 --- a/src/app/player/player.component.ts +++ b/src/app/player/player.component.ts @@ -7,7 +7,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-media-dialog.component'; -import type { FileType } from 'api-types'; +import type { FileType } from '../../api-types'; export interface IMedia { title: string; @@ -35,7 +35,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { // params fileNames: string[]; - type: string; + type: FileType; id = null; // used for playlists (not subscription) uid = null; // used for non-subscription files (audio, video, playlist) subscriptionName = null; @@ -74,7 +74,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { ngOnInit(): void { this.innerWidth = window.innerWidth; - this.type = this.route.snapshot.paramMap.get('type'); + this.type = this.route.snapshot.paramMap.get('type') as FileType; this.id = this.route.snapshot.paramMap.get('id'); this.uid = this.route.snapshot.paramMap.get('uid'); this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName'); @@ -160,7 +160,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy { if (!this.id) { // regular video/audio file (not playlist) this.fileNames = [this.db_file['id']]; - this.type = this.db_file['isAudio'] ? 'audio' : 'video'; + this.type = (this.db_file['isAudio'] ? 'audio' : 'video') as FileType; if (!already_has_filenames) { this.parseFileNames(); } } } diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index c7e7322..d0fad49 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -10,13 +10,19 @@ import { DOCUMENT } from '@angular/common'; import { BehaviorSubject } from 'rxjs'; import { MatSnackBar } from '@angular/material/snack-bar'; import * as Fingerprint2 from 'fingerprintjs2'; -import { +import type { + ChangeRolePermissionsRequest, + ChangeUserPermissionsRequest, + ConfigResponse, CreatePlaylistRequest, CreatePlaylistResponse, DeleteMp3Mp4Request, DeletePlaylistRequest, DeleteSubscriptionFileRequest, + DownloadArchiveRequest, + DownloadFileRequest, FileType, + GenerateNewApiKeyResponse, GetAllDownloadsResponse, GetAllFilesResponse, GetAllSubscriptionsResponse, @@ -35,16 +41,21 @@ import { Mp4DownloadRequest, Mp4DownloadResponse, Playlist, + SetConfigRequest, SharingToggle, SubscribeRequest, SubscribeResponse, SubscriptionRequestData, SuccessObject, + UpdaterStatus, UnsubscribeRequest, UnsubscribeResponse, UpdatePlaylistFilesRequest, UpdatePlaylistRequest, -} from 'api-types'; + UpdateServerRequest, + UserPermission, + YesNo, +} from '../api-types'; @Injectable() export class PostsService implements CanActivate { @@ -224,7 +235,7 @@ export class PostsService implements CanActivate { if (isDevMode()) { return this.http.get('./assets/default.json'); } else { - return this.http.get(this.path + 'config', this.httpOptions); + return this.http.get(this.path + 'config', this.httpOptions); } } @@ -233,7 +244,8 @@ export class PostsService implements CanActivate { } setConfig(config) { - return this.http.post(this.path + 'setConfig', {new_config_file: config}, this.httpOptions); + const body: SetConfigRequest = {new_config_file: config}; + return this.http.post(this.path + 'setConfig', body, this.httpOptions); } deleteFile(uid: string, isAudio: boolean, blacklistMode = false) { @@ -262,19 +274,19 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getAllFiles', {}, this.httpOptions); } - downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null, - uid = null, uuid = null, id = null) { - return this.http.post(this.path + 'downloadFile', {fileNames: fileName, - type: type, - zip_mode: Array.isArray(fileName), - outputName: outputName, - fullPathProvided: fullPathProvided, - subscriptionName: subscriptionName, - subPlaylist: subPlaylist, - uuid: uuid, - uid: uid, - id: id - }, + downloadFileFromServer(fileName: string | string[], type: FileType, outputName: string = null, fullPathProvided: boolean = null, subscriptionName: boolean = null, subPlaylist: boolean = null, + uid = null, uuid: string = null, id = null) { + const body: DownloadFileRequest = {fileNames: fileName, + type: type, + zip_mode: Array.isArray(fileName), + outputName: outputName, + fullPathProvided: fullPathProvided, + subscriptionName: subscriptionName, + subPlaylist: subPlaylist, + uuid: uuid, + id: id, + }; + return this.http.post(this.path + 'downloadFile', body, {responseType: 'blob', params: this.httpOptions.params}); } @@ -283,7 +295,8 @@ export class PostsService implements CanActivate { } downloadArchive(sub) { - return this.http.post(this.path + 'downloadArchive', {sub: sub}, {responseType: 'blob', params: this.httpOptions.params}); + const body: DownloadArchiveRequest = {sub: sub}; + return this.http.post(this.path + 'downloadArchive', body, {responseType: 'blob', params: this.httpOptions.params}); } getFileInfo(fileNames, type, urlMode) { @@ -299,7 +312,7 @@ export class PostsService implements CanActivate { } generateNewAPIKey() { - return this.http.post(this.path + 'generateNewAPIKey', {}, this.httpOptions); + return this.http.post(this.path + 'generateNewAPIKey', {}, this.httpOptions); } enableSharing(uid: string, type: FileType, is_playlist: boolean) { @@ -393,12 +406,13 @@ export class PostsService implements CanActivate { } // updates the server to the latest version - updateServer(tag) { - return this.http.post(this.path + 'updateServer', {tag: tag}, this.httpOptions); + updateServer(tag: string) { + const body: UpdateServerRequest = {tag: tag}; + return this.http.post(this.path + 'updateServer', body, this.httpOptions); } getUpdaterStatus() { - return this.http.get(this.path + 'updaterStatus', this.httpOptions); + return this.http.get(this.path + 'updaterStatus', this.httpOptions); } // gets tag of the latest version of youtubedl-material @@ -534,11 +548,11 @@ export class PostsService implements CanActivate { } changeUser(change_obj) { - return this.http.post(this.path + 'updateUser', {change_object: change_obj}, this.httpOptions); + return this.http.post(this.path + 'updateUser', {change_object: change_obj}, this.httpOptions); } deleteUser(uid) { - return this.http.post(this.path + 'deleteUser', {uid: uid}, this.httpOptions); + return this.http.post(this.path + 'deleteUser', {uid: uid}, this.httpOptions); } changeUserPassword(user_uid, new_password) { @@ -553,13 +567,15 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'getRoles', {}, this.httpOptions); } - setUserPermission(user_uid, permission, new_value) { - return this.http.post(this.path + 'changeUserPermissions', {user_uid: user_uid, permission: permission, new_value: new_value}, + setUserPermission(user_uid: string, permission: UserPermission, new_value: YesNo) { + const body: ChangeUserPermissionsRequest = {user_uid: user_uid, permission: permission, new_value: new_value}; + return this.http.post(this.path + 'changeUserPermissions', body, this.httpOptions); } - setRolePermission(role_name, permission, new_value) { - return this.http.post(this.path + 'changeRolePermissions', {role: role_name, permission: permission, new_value: new_value}, + setRolePermission(role_name: string, permission: UserPermission, new_value: YesNo) { + const body: ChangeRolePermissionsRequest = {role: role_name, permission: permission, new_value: new_value}; + return this.http.post(this.path + 'changeRolePermissions', body, this.httpOptions); } diff --git a/src/app/subscription/subscription/subscription.component.ts b/src/app/subscription/subscription/subscription.component.ts index 794a268..eb2f35b 100644 --- a/src/app/subscription/subscription/subscription.component.ts +++ b/src/app/subscription/subscription/subscription.component.ts @@ -3,6 +3,7 @@ import { PostsService } from 'app/posts.services'; import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { EditSubscriptionDialogComponent } from 'app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component'; +import type { FileType } from '../../../api-types'; @Component({ selector: 'app-subscription', @@ -151,7 +152,7 @@ export class SubscriptionComponent implements OnInit { } this.downloading = true; - this.postsService.downloadFileFromServer(fileNames, 'video', this.subscription.name, true).subscribe(res => { + this.postsService.downloadFileFromServer(fileNames, 'video' as FileType, this.subscription.name, true).subscribe(res => { this.downloading = false; const blob: Blob = res; saveAs(blob, this.subscription.name + '.zip');