diff --git a/.gitignore b/.gitignore index 9d2ac87..32c9fd9 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ backend/subscriptions/playlists/* backend/subscriptions/archives/* backend/*.exe src/assets/default.json +src/api-types backend/appdata/db.json backend/appdata/archives/archive_audio.txt backend/appdata/archives/archive_video.txt @@ -65,4 +66,4 @@ backend/appdata/logs/error.log backend/appdata/users.json backend/users/* backend/appdata/cookies.txt -backend/public \ No newline at end of file +backend/public diff --git a/Public API v1.yaml b/Public API v1.yaml index e82328a..15013e0 100644 --- a/Public API v1.yaml +++ b/Public API v1.yaml @@ -21,14 +21,16 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body' + $ref: '#/components/schemas/Mp3DownloadRequest' responses: '200': description: OK content: application/json: schema: - $ref: '#/components/schemas/inline_response_200' + $ref: '#/components/schemas/Mp3DownloadResponse' + '500': + description: Server download error security: - Auth query parameter: [] /api/tomp4: @@ -46,14 +48,16 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_1' + $ref: '#/components/schemas/Mp4DownloadRequest' responses: '200': description: OK content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_1' + $ref: '#/components/schemas/Mp4DownloadResponse' + '500': + description: Server download error security: - Auth query parameter: [] /api/getMp3s: @@ -69,8 +73,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_2' - requestBody: {} + $ref: '#/components/schemas/GetMp3sResponse' security: - Auth query parameter: [] /api/getMp4s: @@ -86,7 +89,23 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_3' + $ref: '#/components/schemas/GetMp4sResponse' + security: + - Auth query parameter: [] + /api/getAllFiles: + post: + tags: + - files + summary: Get all files + description: Gets all files and playlists stored in the db + operationId: get-getAllFiles + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetAllFilesResponse' security: - Auth query parameter: [] /api/getFile: @@ -100,14 +119,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_2' + $ref: '#/components/schemas/GetFileRequest' responses: '200': description: OK content: application/json: schema: - $ref: '#/components/schemas/inline_response_200_4' + $ref: '#/components/schemas/GetFileResponse' security: - Auth query parameter: [] /api/enableSharing: @@ -122,7 +141,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_3' + $ref: '#/components/schemas/SharingToggle' responses: '200': description: OK @@ -144,7 +163,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/body_4' + $ref: '#/components/schemas/SharingToggle' responses: '200': description: OK @@ -718,7 +737,7 @@ paths: type: object properties: user: - $ref: '#/components/schemas/user' + $ref: '#/components/schemas/User' token: type: string permissions: @@ -761,7 +780,7 @@ paths: type: object properties: user: - $ref: '#/components/schemas/user' + $ref: '#/components/schemas/User' description: Use this endpoint to register a user. It will only work if registration is enabled. requestBody: content: @@ -958,7 +977,7 @@ paths: users: type: array items: - $ref: '#/components/schemas/user' + $ref: '#/components/schemas/User' description: 'Gets all users, returns a list of the user objects including their user permissions, videos, playlists, subscriptions, etc.' security: - Auth query parameter: [] @@ -966,7 +985,12 @@ paths: - multi-user mode components: schemas: - body: + FileType: + type: string + enum: + - audio + - video + BaseDownloadRequest: required: - url type: object @@ -975,64 +999,72 @@ components: type: string customQualityConfiguration: type: string + description: Video format code. Overrides other quality options. example: '251' - maxBitrate: - type: string - example: '160' customArgs: type: string + description: Custom command-line arguments for youtubedl. Overrides all other options, except url. customOutput: type: string + description: Custom output filename template. youtubeUsername: type: string + description: Login with this account ID youtubePassword: type: string - inline_response_200: - required: - - audiopathEncoded - - uid - type: object - properties: - uid: - type: string - file_names: - type: string - audiopathEncoded: - type: string - body_1: - required: - - url - type: object - properties: - url: - type: string - customQualityConfiguration: - type: string - example: 242+251 - selectedHeight: - type: string - example: '1080' - customArgs: - type: string - customOutput: - type: string - youtubeUsername: + description: Account password + ui_uid: type: string - youtubePassword: - type: string - inline_response_200_1: + nullable: true + Mp3DownloadRequest: + allOf: + - $ref: '#/components/schemas/BaseDownloadRequest' + - type: object + properties: + maxBitrate: + type: string + description: Specify ffmpeg/avconv audio quality + example: '160' + Mp4DownloadRequest: + allOf: + - $ref: '#/components/schemas/BaseDownloadRequest' + - type: object + properties: + selectedHeight: + type: string + description: Height of the video, if known + example: '1080' + BaseDownloadResponse: required: - uid - - videopathEncoded type: object properties: uid: type: string file_names: - type: string - videopathEncoded: - type: string - inline_response_200_2: + nullable: true + type: array + items: + type: string + Mp3DownloadResponse: + allOf: + - $ref: '#/components/schemas/BaseDownloadResponse' + - type: object + required: + - audiopathEncoded + properties: + audiopathEncoded: + type: string + Mp4DownloadResponse: + allOf: + - $ref: '#/components/schemas/BaseDownloadResponse' + - type: object + required: + - videopathEncoded + properties: + videopathEncoded: + type: string + GetMp3sResponse: required: - mp3s - playlists @@ -1041,48 +1073,66 @@ components: mp3s: type: array items: - $ref: '#/components/schemas/inline_response_200_2_mp3s' + $ref: '#/components/schemas/DatabaseFile' playlists: type: array description: All audio playlists items: - $ref: '#/components/schemas/inline_response_200_2_playlists' - inline_response_200_3: + $ref: '#/components/schemas/Playlist' + GetMp4sResponse: required: - mp4s + - playlists type: object properties: mp4s: type: array items: - $ref: '#/components/schemas/inline_response_200_3_mp4s' + $ref: '#/components/schemas/DatabaseFile' playlists: type: array description: All video playlists items: - type: object - body_2: + $ref: '#/components/schemas/Playlist' + GetAllFilesResponse: + required: + - files + - playlists + type: object + properties: + files: + type: array + items: + $ref: '#/components/schemas/DatabaseFile' + playlists: + type: array + description: All video playlists + items: + $ref: '#/components/schemas/Playlist' + GetFileRequest: required: - uid type: object properties: uid: type: string + description: Video UID type: + $ref: '#/components/schemas/FileType' + uuid: type: string - inline_response_200_4: + description: User UID + GetFileResponse: required: - - file - success type: object properties: success: - type: string + type: boolean file: - $ref: '#/components/schemas/inline_response_200_2_mp3s' - body_3: + $ref: '#/components/schemas/DatabaseFile' + SharingToggle: required: - - is_playlist - type - uid type: object @@ -1090,7 +1140,7 @@ components: uid: type: string type: - type: string + $ref: '#/components/schemas/FileType' is_playlist: type: boolean inline_response_200_5: @@ -1100,19 +1150,6 @@ components: properties: success: type: boolean - body_4: - required: - - type - - uid - type: object - properties: - type: - type: string - uid: - type: string - description: uid is either the video uid or the playlist ID - is_playlist: - type: boolean body_5: required: - url @@ -1303,7 +1340,7 @@ components: type: object properties: playlist: - $ref: '#/components/schemas/inline_response_200_2_playlists' + $ref: '#/components/schemas/Playlist' type: type: string success: @@ -1457,7 +1494,7 @@ components: properties: new_config_file: type: object - inline_response_200_2_mp3s: + DatabaseFile: required: - duration - id @@ -1497,7 +1534,7 @@ components: type: string sharingEnabled: type: boolean - inline_response_200_2_playlists: + Playlist: required: - fileNames - id @@ -1515,45 +1552,6 @@ components: type: string thumbnailURL: type: string - inline_response_200_3_mp4s: - required: - - duration - - id - - isAudio - - path - - size - - thumbnailURL - - title - - uid - - upload_date - - uploader - - url - type: object - properties: - id: - type: string - title: - type: string - thumbnailURL: - type: string - isAudio: - type: boolean - duration: - type: number - url: - type: string - uploader: - type: string - size: - type: number - path: - type: string - upload_date: - type: string - uid: - type: string - sharingEnabled: - type: boolean inline_response_200_6_new_sub: required: - id @@ -1633,7 +1631,7 @@ components: type: string type: type: string - user: + User: title: user type: object properties: diff --git a/package-lock.json b/package-lock.json index 08cd667..2cfdacc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9618,6 +9618,76 @@ "is-wsl": "^2.1.1" } }, + "openapi-typescript-codegen": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.4.11.tgz", + "integrity": "sha512-KWhJE4xlFXDd7sNyEiJeUx7+H1hqQ+WETzwrcXCE+CNR+pmfKkOH87fjPRUPu1TY3hSgDgZ6ex7F3lWLbirvMQ==", + "dev": true, + "requires": { + "camelcase": "6.0.0", + "commander": "6.1.0", + "handlebars": "4.7.6", + "js-yaml": "3.14.0", + "mkdirp": "1.0.4", + "path": "0.12.7", + "rimraf": "3.0.2" + }, + "dependencies": { + "camelcase": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", + "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", + "dev": true + }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -10048,6 +10118,33 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "dev": true, + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + } + } + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", diff --git a/package.json b/package.json index 14aa13e..0e1ff92 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", - "electron": "ng build --base-href ./ && electron ." + "electron": "ng build --base-href ./ && electron .", + "generate": "openapi --input ./\"Public API v1.yaml\" --output ./src/api-types --exportCore false --exportServices false --exportModels true", + "prepare": "npm run generate" }, "engines": { "node": "12.3.1", @@ -66,6 +68,7 @@ "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", + "openapi-typescript-codegen": "^0.4.11", "protractor": "~5.1.2", "ts-node": "~3.0.4", "tslint": "~5.3.2" diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index b444e11..3625449 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -63,7 +63,7 @@ export class MainComponent implements OnInit { youtubeUsername = null; youtubePassword = null; urlError = false; - path = ''; + path: string | string[] = ''; url = ''; exists = ''; percentDownloaded: number; diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 416fd2d..2153f73 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -8,9 +8,9 @@ import { THEMES_CONFIG } from '../themes'; import { Router, CanActivate } from '@angular/router'; import { DOCUMENT } from '@angular/common'; import { BehaviorSubject } from 'rxjs'; -import { v4 as uuid } from 'uuid'; import { MatSnackBar } from '@angular/material/snack-bar'; import * as Fingerprint2 from 'fingerprintjs2'; +import type { FileType, GetAllFilesResponse, GetFileRequest, GetFileResponse, GetMp3sResponse, GetMp4sResponse, Mp3DownloadRequest, Mp3DownloadResponse, Mp4DownloadRequest, Mp4DownloadResponse } from 'api-types'; @Injectable() export class PostsService implements CanActivate { @@ -25,7 +25,9 @@ export class PostsService implements CanActivate { // auth auth_token = '4241b401-7236-493e-92b5-b72696b9d853'; session_id = null; - httpOptions = null; + httpOptions: { + params: HttpParams + }; http_params: string = null; unauthorized = false; @@ -155,27 +157,29 @@ export class PostsService implements CanActivate { } // tslint:disable-next-line: max-line-length - makeMP3(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null) { - return this.http.post(this.path + 'tomp3', {url: url, - maxBitrate: selectedQuality, - customQualityConfiguration: customQualityConfiguration, - customArgs: customArgs, - customOutput: customOutput, - youtubeUsername: youtubeUsername, - youtubePassword: youtubePassword, - ui_uid: ui_uid}, this.httpOptions); + makeMP3(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid: string = null) { + const body: Mp3DownloadRequest = {url: url, + maxBitrate: selectedQuality, + customQualityConfiguration: customQualityConfiguration, + customArgs: customArgs, + customOutput: customOutput, + youtubeUsername: youtubeUsername, + youtubePassword: youtubePassword, + ui_uid: ui_uid} + return this.http.post(this.path + 'tomp3', body, this.httpOptions); } // tslint:disable-next-line: max-line-length makeMP4(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null) { - return this.http.post(this.path + 'tomp4', {url: url, - selectedHeight: selectedQuality, - customQualityConfiguration: customQualityConfiguration, - customArgs: customArgs, - customOutput: customOutput, - youtubeUsername: youtubeUsername, - youtubePassword: youtubePassword, - ui_uid: ui_uid}, this.httpOptions); + const body: Mp4DownloadRequest = {url: url, + selectedHeight: selectedQuality, + customQualityConfiguration: customQualityConfiguration, + customArgs: customArgs, + customOutput: customOutput, + youtubeUsername: youtubeUsername, + youtubePassword: youtubePassword, + ui_uid: ui_uid} + return this.http.post(this.path + 'tomp4', body, this.httpOptions); } killAllDownloads() { @@ -207,19 +211,20 @@ export class PostsService implements CanActivate { } getMp3s() { - return this.http.get(this.path + 'getMp3s', this.httpOptions); + return this.http.get(this.path + 'getMp3s', this.httpOptions); } getMp4s() { - return this.http.get(this.path + 'getMp4s', this.httpOptions); + return this.http.get(this.path + 'getMp4s', this.httpOptions); } - getFile(uid, type, uuid = null) { - return this.http.post(this.path + 'getFile', {uid: uid, type: type, uuid: uuid}, this.httpOptions); + getFile(uid: string, type: FileType, uuid: string = null) { + const body: GetFileRequest = {uid: uid, type: type, uuid: uuid}; + return this.http.post(this.path + 'getFile', body, this.httpOptions); } getAllFiles() { - return this.http.post(this.path + 'getAllFiles', {}, this.httpOptions); + return this.http.post(this.path + 'getAllFiles', {}, this.httpOptions); } downloadFileFromServer(fileName, type, outputName = null, fullPathProvided = null, subscriptionName = null, subPlaylist = null,