diff --git a/back/pom.xml b/back/pom.xml index 91fb7a1..b4e6689 100644 --- a/back/pom.xml +++ b/back/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.kar karideo - 0.2.0 + 0.3.0 3.1 21 @@ -20,7 +20,7 @@ kangaroo-and-rabbit archidata - 0.7.1 + 0.7.3 org.slf4j diff --git a/back/src/org/kar/karideo/api/MediaResource.java b/back/src/org/kar/karideo/api/MediaResource.java index 38eaec7..82699a6 100644 --- a/back/src/org/kar/karideo/api/MediaResource.java +++ b/back/src/org/kar/karideo/api/MediaResource.java @@ -35,7 +35,6 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; @Path("/media") @Produces(MediaType.APPLICATION_JSON) @@ -63,7 +62,7 @@ public class MediaResource { @Consumes(MediaType.APPLICATION_JSON) @Operation(description = "Modify a specific Media", tags = "GLOBAL") public Media patch(@PathParam("id") final Long id, @AsyncType(Media.class) final String jsonRequest) throws Exception { - System.out.println("update video " + id + " ==> '" + jsonRequest + "'"); + LOGGER.info("update video {} ==> '{}'", id, jsonRequest); DataAccess.updateWithJson(Media.class, id, jsonRequest); return DataAccess.get(Media.class, id); } @@ -85,12 +84,19 @@ public class MediaResource { @RolesAllowed("ADMIN") @Consumes({ MediaType.MULTIPART_FORM_DATA }) @Operation(description = "Create a new Media", tags = "GLOBAL") - @AsyncType(Media.class) @TypeScriptProgress - public Response uploadFile(@FormDataParam("fileName") String fileName, @FormDataParam("universe") String universe, @FormDataParam("series") String series, - //@FormDataParam("seriesId") String seriesId, Not used ... - @FormDataParam("season") String season, @FormDataParam("episode") String episode, @FormDataParam("title") String title, @FormDataParam("typeId") String typeId, - @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws FailException { + public Media uploadFile( // + @FormDataParam("fileName") String fileName, // + @FormDataParam("universe") String universe, // + @FormDataParam("series") String series, // + //@FormDataParam("seriesId") String seriesId, // Not used ... + @FormDataParam("season") String season, // + @FormDataParam("episode") String episode, // + @FormDataParam("title") String title, // + @FormDataParam("typeId") String typeId, // + @FormDataParam("file") final InputStream fileInputStream, // + @FormDataParam("file") final FormDataContentDisposition fileMetaData // + ) throws FailException { try { // correct input string stream : fileName = multipartCorrection(fileName); @@ -102,16 +108,16 @@ public class MediaResource { typeId = multipartCorrection(typeId); //public NodeSmall uploadFile(final FormDataMultiPart form) { - System.out.println("Upload media file: " + fileMetaData); - System.out.println(" - fileName: " + fileName); - System.out.println(" - universe: " + universe); - System.out.println(" - series: " + series); - System.out.println(" - season: " + season); - System.out.println(" - episode: " + episode); - System.out.println(" - title: " + title); - System.out.println(" - type: " + typeId); - System.out.println(" - fileInputStream: " + fileInputStream); - System.out.println(" - fileMetaData: " + fileMetaData); + LOGGER.info("Upload media file: {}", fileMetaData); + LOGGER.info(" - fileName: {}", fileName); + LOGGER.info(" - universe: {}", universe); + LOGGER.info(" - series: {}", series); + LOGGER.info(" - season: {}", season); + LOGGER.info(" - episode: {}", episode); + LOGGER.info(" - title: {}", title); + LOGGER.info(" - type: {}", typeId); + LOGGER.info(" - fileInputStream: {}", fileInputStream); + LOGGER.info(" - fileMetaData: {}", fileMetaData); System.out.flush(); if (typeId == null) { throw new InputException("typeId", "TypiId is not specified"); @@ -121,7 +127,7 @@ public class MediaResource { final String sha512 = DataResource.saveTemporaryFile(fileInputStream, tmpUID); Data data = DataResource.getWithSha512(sha512); if (data == null) { - System.out.println("Need to add the data in the BDD ... "); + LOGGER.info("Need to add the data in the BDD ... "); System.out.flush(); try { data = DataResource.createNewData(tmpUID, fileName, sha512); @@ -130,33 +136,33 @@ public class MediaResource { ex.printStackTrace(); throw new FailException("can not create input media (the data model has an internal error"); } - } else if (data!= null && data.deleted) { - System.out.println("Data already exist but deleted"); + } else if (data!= null && data.deleted != null && data.deleted) { + LOGGER.info("Data already exist but deleted"); System.out.flush(); DataTools.undelete(data.id); data.deleted = false; } else { - System.out.println("Data already exist ... all good"); + LOGGER.info("Data already exist ... all good"); System.out.flush(); } // Fist step: retieve all the Id of each parents:... - System.out.println("Find typeNode"); + LOGGER.info("Find typeNode"); // check if id of type exist: final Type typeNode = TypeResource.getId(Long.parseLong(typeId)); if (typeNode == null) { DataResource.removeTemporaryFile(tmpUID); throw new InputException("typeId", "TypeId does not exist ..."); } - System.out.println(" ==> " + typeNode); - System.out.println("Find seriesNode"); + LOGGER.info(" ==> {}", typeNode); + LOGGER.info("Find seriesNode"); // get uid of group: Series seriesNode = null; if (series != null) { seriesNode = SeriesResource.getOrCreate(series, typeNode.id); } - System.out.println(" ==> " + seriesNode); - System.out.println("Find seasonNode"); + LOGGER.info(" ==> {}", seriesNode); + LOGGER.info("Find seasonNode"); // get uid of season: Season seasonNode = null; if (seriesNode == null && season != null) { @@ -167,10 +173,9 @@ public class MediaResource { seasonNode = SeasonResource.getOrCreate(season, seriesNode.id); } - System.out.println(" ==> " + seasonNode); - System.out.println("add media"); + LOGGER.info(" ==> {}", seasonNode); + LOGGER.info("add media"); - final long uniqueSQLID = -1; try { final Media media = new Media(); media.name = title; @@ -189,17 +194,17 @@ public class MediaResource { media.episode = Integer.parseInt(episode); } final Media out = DataAccess.insert(media); - DataResource.removeTemporaryFile(tmpUID); - System.out.println("uploaded .... compleate: " + uniqueSQLID); - final Media creation = get(uniqueSQLID); - return Response.ok(creation).build(); + LOGGER.info("Generate new media {}", out); + return out; } catch (final SQLException ex) { ex.printStackTrace(); - System.out.println("Catch error:" + ex.getMessage()); + LOGGER.error("Catch error: {}", ex.getMessage()); throw new FailException("Catch SQLerror ==> check server logs"); + } finally { + DataResource.removeTemporaryFile(tmpUID); } } catch (final Exception ex) { - System.out.println("Catch an unexpected error ... " + ex.getMessage()); + LOGGER.error("Catch an unexpected error ... {} ", ex.getMessage()); ex.printStackTrace(); throw new FailException("Catch Exception ==> check server logs"); } diff --git a/back/src/org/kar/karideo/migration/Migration20240226.java b/back/src/org/kar/karideo/migration/Migration20240226.java index ceda4f8..e088645 100644 --- a/back/src/org/kar/karideo/migration/Migration20240226.java +++ b/back/src/org/kar/karideo/migration/Migration20240226.java @@ -119,9 +119,17 @@ public class Migration20240226 extends MigrationSqlStep { } } }); + /* I am not sure then I prefer keep the primary key for the moment addAction(""" ALTER TABLE `data` DROP `id`; """); + */ + addAction(""" + ALTER TABLE `data` CHANGE `id` `idOld` bigint NOT NULL DEFAULT 0; + """); + addAction(""" + ALTER TABLE `data` DROP PRIMARY KEY; + """); addAction(""" ALTER TABLE `data` CHANGE `uuid` `id` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE)); """); diff --git a/back/src/org/kar/karideo/model/Media.java b/back/src/org/kar/karideo/model/Media.java index b8aeada..502adf4 100644 --- a/back/src/org/kar/karideo/model/Media.java +++ b/back/src/org/kar/karideo/model/Media.java @@ -49,4 +49,13 @@ public class Media extends GenericDataSoftDelete { // List of Id of the specific covers @DataJson(targetEntity = Data.class) public List covers = null; + + @Override + public String toString() { + return "Media [name=" + this.name + ", description=" + this.description + ", dataId=" + this.dataId + ", typeId=" + this.typeId + + ", seriesId=" + this.seriesId + ", seasonId=" + this.seasonId + ", episode=" + this.episode + ", date=" + this.date + + ", time=" + this.time + ", ageLimit=" + this.ageLimit + ", covers=" + this.covers + "]"; + } + + } diff --git a/front/package.json b/front/package.json index 0d40e22..6f93373 100644 --- a/front/package.json +++ b/front/package.json @@ -19,31 +19,31 @@ }, "private": true, "dependencies": { - "@angular/animations": "^17.3.4", - "@angular/cdk": "^17.3.4", - "@angular/common": "^17.3.4", - "@angular/compiler": "^17.3.4", - "@angular/core": "^17.3.4", - "@angular/forms": "^17.3.4", - "@angular/material": "^17.3.4", - "@angular/platform-browser": "^17.3.4", - "@angular/platform-browser-dynamic": "^17.3.4", - "@angular/router": "^17.3.4", + "@angular/animations": "^17.3.5", + "@angular/cdk": "^17.3.5", + "@angular/common": "^17.3.5", + "@angular/compiler": "^17.3.5", + "@angular/core": "^17.3.5", + "@angular/forms": "^17.3.5", + "@angular/material": "^17.3.5", + "@angular/platform-browser": "^17.3.5", + "@angular/platform-browser-dynamic": "^17.3.5", + "@angular/router": "^17.3.5", "rxjs": "^7.8.1", "zone.js": "^0.14.4", "zod": "3.22.4", - "@kangaroo-and-rabbit/kar-cw": "^0.2.0" + "@kangaroo-and-rabbit/kar-cw": "^0.2.1" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.3.4", + "@angular-devkit/build-angular": "^17.3.5", "@angular-eslint/builder": "17.3.0", "@angular-eslint/eslint-plugin": "17.3.0", "@angular-eslint/eslint-plugin-template": "17.3.0", "@angular-eslint/schematics": "17.3.0", "@angular-eslint/template-parser": "17.3.0", - "@angular/cli": "^17.3.4", - "@angular/compiler-cli": "^17.3.4", - "@angular/language-service": "^17.3.4", + "@angular/cli": "^17.3.5", + "@angular/compiler-cli": "^17.3.5", + "@angular/language-service": "^17.3.5", "npm-check-updates": "^16.14.18", "tslib": "^2.6.2" } diff --git a/front/src/app/app.module.ts b/front/src/app/app.module.ts index 6fc05d5..9acd14d 100644 --- a/front/src/app/app.module.ts +++ b/front/src/app/app.module.ts @@ -37,6 +37,7 @@ import { environment } from 'environments/environment'; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { CommonModule } from '@angular/common'; +import { FileDragNDropDirective } from './scene/upload/file-drag-n-drop.directive'; @NgModule({ declarations: [ @@ -58,7 +59,8 @@ import { CommonModule } from '@angular/common'; VideoEditScene, SeasonEditScene, SeriesEditScene, - UploadScene + UploadScene, + FileDragNDropDirective, ], imports: [ FormsModule, diff --git a/front/src/app/back-api/data-resource.ts b/front/src/app/back-api/data-resource.ts index b18efa3..a12d927 100644 --- a/front/src/app/back-api/data-resource.ts +++ b/front/src/app/back-api/data-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {UUID, } from "./model" export namespace DataResource { diff --git a/front/src/app/back-api/front.ts b/front/src/app/back-api/front.ts index 3744fe7..d478ff9 100644 --- a/front/src/app/back-api/front.ts +++ b/front/src/app/back-api/front.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {} from "./model" export namespace Front { diff --git a/front/src/app/back-api/health-check.ts b/front/src/app/back-api/health-check.ts index c4a8fe7..e87eff3 100644 --- a/front/src/app/back-api/health-check.ts +++ b/front/src/app/back-api/health-check.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {HealthResult, isHealthResult, } from "./model" export namespace HealthCheck { diff --git a/front/src/app/back-api/media-resource.ts b/front/src/app/back-api/media-resource.ts index 4cff95c..b8ed4ed 100644 --- a/front/src/app/back-api/media-resource.ts +++ b/front/src/app/back-api/media-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {UUID, Long, Media, isMedia, } from "./model" export namespace MediaResource { @@ -66,33 +66,6 @@ export namespace MediaResource { data, }, isMedia); }; - /** - * Upload a new season cover media - */ - export function uploadCover({ restConfig, params, data, progress, }: { - restConfig: RESTConfig, - params: { - id: Long, - }, - data: { - fileName: string, - file: File, - }, - progress?: ProgressCallback, - }): Promise { - return RESTRequestJson({ - restModel: { - endPoint: "/media/{id}/cover", - requestType: HTTPRequestModel.POST, - contentType: HTTPMimeType.MULTIPART, - accept: HTTPMimeType.JSON, - }, - restConfig, - params, - data, - progress, - }, isMedia); - }; /** * Get all Media */ @@ -108,6 +81,33 @@ export namespace MediaResource { restConfig, }, isMedia); }; + /** + * Upload a new season cover media + */ + export function uploadCover({ restConfig, params, data, callback, }: { + restConfig: RESTConfig, + params: { + id: Long, + }, + data: { + fileName: string, + file: File, + }, + callback?: RESTCallbacks, + }): Promise { + return RESTRequestJson({ + restModel: { + endPoint: "/media/{id}/cover", + requestType: HTTPRequestModel.POST, + contentType: HTTPMimeType.MULTIPART, + accept: HTTPMimeType.JSON, + }, + restConfig, + params, + data, + callback, + }, isMedia); + }; /** * Remove a specific cover of a media */ @@ -132,7 +132,7 @@ export namespace MediaResource { /** * Create a new Media */ - export function uploadFile({ restConfig, data, progress, }: { + export function uploadFile({ restConfig, data, callback, }: { restConfig: RESTConfig, data: { fileName: string, @@ -144,7 +144,7 @@ export namespace MediaResource { typeId: string, title: string, }, - progress?: ProgressCallback, + callback?: RESTCallbacks, }): Promise { return RESTRequestJson({ restModel: { @@ -155,7 +155,7 @@ export namespace MediaResource { }, restConfig, data, - progress, + callback, }, isMedia); }; } diff --git a/front/src/app/back-api/model.ts b/front/src/app/back-api/model.ts index cfadf63..0a0406b 100644 --- a/front/src/app/back-api/model.ts +++ b/front/src/app/back-api/model.ts @@ -122,9 +122,9 @@ export function isLocalTime(data: any): data is LocalTime { export const ZodRestErrorResponse = zod.object({ uuid: ZodUUID.optional(), - time: zod.string().max(255).optional(), - error: zod.string().max(255).optional(), + name: zod.string().max(255).optional(), message: zod.string().max(255).optional(), + time: zod.string().max(255).optional(), status: ZodInteger, statusMessage: zod.string().max(255).optional() }); diff --git a/front/src/app/back-api/rest-tools.ts b/front/src/app/back-api/rest-tools.ts index 4180752..d818f57 100644 --- a/front/src/app/back-api/rest-tools.ts +++ b/front/src/app/back-api/rest-tools.ts @@ -41,7 +41,7 @@ export interface RESTModel { accept?: HTTPMimeType; // Content of the local data. contentType?: HTTPMimeType; - // Mode of the TOKEN in urk or Header + // Mode of the TOKEN in URL or Header (?token:${tokenInUrl}) tokenInUrl?: boolean; } @@ -71,13 +71,28 @@ function isNullOrUndefined(data: any): data is undefined | null { return data === undefined || data === null; } -export type RESTRequestType = { +// generic progression callback +export type ProgressCallback = (count: number, total: number) => void; + +export interface RESTAbort { + abort?: () => boolean +} + + +// Rest generic callback have a basic model to upload and download advancement. +export interface RESTCallbacks { + progressUpload?: ProgressCallback, + progressDownload?: ProgressCallback, + abortHandle?: RESTAbort, +}; + +export interface RESTRequestType { restModel: RESTModel, restConfig: RESTConfig, data?: any, params?: object, queries?: object, - progress?: ProgressCallback, + callback?: RESTCallbacks, }; function removeTrailingSlashes(input: string): string { @@ -124,63 +139,91 @@ export function RESTUrl({ restModel, restConfig, params, queries }: RESTRequestT } -export type ProgressCallback = (count: number, total: number) => void; -// input: RequestInfo | URL, init?: RequestInit): Promise; -export function fetchProgress(generateUrl: string, { method, headers, body }: { +export function fetchProgress(generateUrl: string, { method, headers, body }: { method: HTTPRequestModel, headers: any, body: any, -}, progress: ProgressCallback): Promise { - - //async function fetchForm(form, options = {}) { - const action = generateUrl; - const data = body; - const xhr = new XMLHttpRequest(); - console.log(`call fetch progress ...`); +}, { progressUpload, progressDownload, abortHandle }: RESTCallbacks): Promise { + const xhr = { + io: new XMLHttpRequest() + } return new Promise((resolve, reject) => { - xhr.responseType = 'blob'; - xhr.onreadystatechange = () => { - if (xhr.readyState != 4) { - console.log(` ==> READY state`); - // done - return; - } - console.log(` ==> has finish ...`); - const response = new Response(xhr.response, { - status: xhr.status, - statusText: xhr.statusText + // Stream the upload progress + if (progressUpload) { + xhr.io.upload.addEventListener("progress", (dataEvent) => { + if (dataEvent.lengthComputable) { + //console.log(` ==> has a progress event: ${dataEvent.loaded} / ${dataEvent.total}`); + progressUpload(dataEvent.loaded, dataEvent.total); + } }); - resolve(response); } - // If fail: - xhr.addEventListener('error', () => { - console.log(` ==> IS REJECTED`); + // Stream the download progress + if (progressDownload) { + xhr.io.addEventListener("progress", (dataEvent) => { + if (dataEvent.lengthComputable) { + //console.log(` ==> download progress:: ${dataEvent.loaded} / ${dataEvent.total}`); + progressUpload(dataEvent.loaded, dataEvent.total); + } + }); + } + if (abortHandle) { + abortHandle.abort = () => { + if (xhr.io) { + console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`); + xhr.io.abort(); + return true; + } + console.log(`Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`); + return false; + } + } + // Check if we have an internal Fail: + xhr.io.addEventListener('error', () => { + xhr.io = undefined; reject(new TypeError('Failed to fetch')) }); - // Link the progression callback - if (progress) { - xhr.addEventListener('progress', (dataEvent) => { - console.log(` ==> has a progress event: ${dataEvent.loaded} / ${dataEvent.total}`); - progress(dataEvent.loaded, dataEvent.total); + + // Capture the end of the stream + xhr.io.addEventListener("loadend", () => { + if (xhr.io.readyState !== XMLHttpRequest.DONE) { + //console.log(` ==> READY state`); + return; + } + if (xhr.io.status === 0) { + //the stream has been aborted + reject(new TypeError('Fetch has been aborted')); + return; + } + // Stream is ended, transform in a generic response: + const response = new Response(xhr.io.response, { + status: xhr.io.status, + statusText: xhr.io.statusText }); - } - console.log(` ==> open`); - // open the socket - xhr.open(method, action, true); - console.log(` ==> set header`); - // configure the header + const headersArray = xhr.io.getAllResponseHeaders().trim().replaceAll("\r\n", "\n").split('\n'); + headersArray.forEach(function (header) { + const firstColonIndex = header.indexOf(':'); + if (firstColonIndex !== -1) { + var key = header.substring(0, firstColonIndex).trim(); + var value = header.substring(firstColonIndex + 1).trim(); + response.headers.set(key, value); + } else { + response.headers.set(header, ""); + } + }); + xhr.io = undefined; + resolve(response); + }); + xhr.io.open(method, generateUrl, true); if (!isNullOrUndefined(headers)) { for (const [key, value] of Object.entries(headers)) { - xhr.setRequestHeader(key, value as string); + xhr.io.setRequestHeader(key, value as string); } } - console.log(` ==> send`); - xhr.send(data); - console.log(` ==> send done`); + xhr.io.send(body); }); } -export function RESTRequest({ restModel, restConfig, data, params, queries, progress }: RESTRequestType): Promise { +export function RESTRequest({ restModel, restConfig, data, params, queries, callback }: RESTRequestType): Promise { // Create the URL PATH: let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries }); let headers: any = {}; @@ -207,21 +250,25 @@ export function RESTRequest({ restModel, restConfig, data, params, queries, prog } body = formData } - console.log(`Call ${generateUrl}`) return new Promise((resolve, reject) => { - let action: Promise = undefined; - if (isNullOrUndefined(progress)) { + let action: undefined | Promise = undefined; + if (isNullOrUndefined(callback) + || (isNullOrUndefined(callback.progressDownload) + && isNullOrUndefined(callback.progressUpload) + && isNullOrUndefined(callback.abortHandle))) { + // No information needed: call the generic fetch interface action = fetch(generateUrl, { method: restModel.requestType, headers, body, }); } else { + // need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x action = fetchProgress(generateUrl, { - method: restModel.requestType, + method: restModel.requestType ?? HTTPRequestModel.GET, headers, body, - }, progress); + }, callback); } action.then((response: Response) => { if (response.status >= 200 && response.status <= 299) { @@ -268,7 +315,7 @@ export function RESTRequest({ restModel, restConfig, data, params, queries, prog status: 999, error: error, statusMessage: "Fetch catch error", - message: "http-wrapper.ts detect an error in the fetch request" + message: "rest-tools.ts detect an error in the fetch request" }); }); }); diff --git a/front/src/app/back-api/season-resource.ts b/front/src/app/back-api/season-resource.ts index 4ae90eb..b419678 100644 --- a/front/src/app/back-api/season-resource.ts +++ b/front/src/app/back-api/season-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {UUID, Long, Season, isSeason, } from "./model" export namespace SeasonResource { @@ -85,33 +85,6 @@ export namespace SeasonResource { data, }, isSeason); }; - /** - * Upload a new season cover season - */ - export function uploadCover({ restConfig, params, data, progress, }: { - restConfig: RESTConfig, - params: { - id: Long, - }, - data: { - fileName: string, - file: File, - }, - progress?: ProgressCallback, - }): Promise { - return RESTRequestJson({ - restModel: { - endPoint: "/season/{id}/cover", - requestType: HTTPRequestModel.POST, - contentType: HTTPMimeType.MULTIPART, - accept: HTTPMimeType.JSON, - }, - restConfig, - params, - data, - progress, - }, isSeason); - }; /** * Get a specific Season with his ID */ @@ -127,6 +100,33 @@ export namespace SeasonResource { restConfig, }, isSeason); }; + /** + * Upload a new season cover season + */ + export function uploadCover({ restConfig, params, data, callback, }: { + restConfig: RESTConfig, + params: { + id: Long, + }, + data: { + fileName: string, + file: File, + }, + callback?: RESTCallbacks, + }): Promise { + return RESTRequestJson({ + restModel: { + endPoint: "/season/{id}/cover", + requestType: HTTPRequestModel.POST, + contentType: HTTPMimeType.MULTIPART, + accept: HTTPMimeType.JSON, + }, + restConfig, + params, + data, + callback, + }, isSeason); + }; /** * Remove a specific cover of a season */ diff --git a/front/src/app/back-api/series-resource.ts b/front/src/app/back-api/series-resource.ts index aa2e6b4..daf8157 100644 --- a/front/src/app/back-api/series-resource.ts +++ b/front/src/app/back-api/series-resource.ts @@ -1,8 +1,8 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" -import { UUID, Long, Series, isSeries, } from "./model" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import {UUID, Long, Series, isSeries, } from "./model" export namespace SeriesResource { /** @@ -85,33 +85,6 @@ export namespace SeriesResource { data, }, isSeries); }; - /** - * Upload a new season cover Series - */ - export function uploadCover({ restConfig, params, data, progress, }: { - restConfig: RESTConfig, - params: { - id: Long, - }, - data: { - fileName: string, - file: File, - }, - progress?: ProgressCallback, - }): Promise { - return RESTRequestJson({ - restModel: { - endPoint: "/series/{id}/cover", - requestType: HTTPRequestModel.POST, - contentType: HTTPMimeType.MULTIPART, - accept: HTTPMimeType.JSON, - }, - restConfig, - params, - data, - progress, - }, isSeries); - }; /** * Get all Series */ @@ -127,6 +100,33 @@ export namespace SeriesResource { restConfig, }, isSeries); }; + /** + * Upload a new season cover Series + */ + export function uploadCover({ restConfig, params, data, callback, }: { + restConfig: RESTConfig, + params: { + id: Long, + }, + data: { + fileName: string, + file: File, + }, + callback?: RESTCallbacks, + }): Promise { + return RESTRequestJson({ + restModel: { + endPoint: "/series/{id}/cover", + requestType: HTTPRequestModel.POST, + contentType: HTTPMimeType.MULTIPART, + accept: HTTPMimeType.JSON, + }, + restConfig, + params, + data, + callback, + }, isSeries); + }; /** * Remove a specific Series of a season */ diff --git a/front/src/app/back-api/type-resource.ts b/front/src/app/back-api/type-resource.ts index d2c0683..5fd39be 100644 --- a/front/src/app/back-api/type-resource.ts +++ b/front/src/app/back-api/type-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {UUID, Long, Type, isType, } from "./model" export namespace TypeResource { @@ -85,33 +85,6 @@ export namespace TypeResource { data, }, isType); }; - /** - * Upload a new season cover Type - */ - export function uploadCover({ restConfig, params, data, progress, }: { - restConfig: RESTConfig, - params: { - id: Long, - }, - data: { - fileName: string, - file: File, - }, - progress?: ProgressCallback, - }): Promise { - return RESTRequestJson({ - restModel: { - endPoint: "/type/{id}/cover", - requestType: HTTPRequestModel.POST, - contentType: HTTPMimeType.MULTIPART, - accept: HTTPMimeType.JSON, - }, - restConfig, - params, - data, - progress, - }, isType); - }; /** * Get all Type */ @@ -127,6 +100,33 @@ export namespace TypeResource { restConfig, }, isType); }; + /** + * Upload a new season cover Type + */ + export function uploadCover({ restConfig, params, data, callback, }: { + restConfig: RESTConfig, + params: { + id: Long, + }, + data: { + fileName: string, + file: File, + }, + callback?: RESTCallbacks, + }): Promise { + return RESTRequestJson({ + restModel: { + endPoint: "/type/{id}/cover", + requestType: HTTPRequestModel.POST, + contentType: HTTPMimeType.MULTIPART, + accept: HTTPMimeType.JSON, + }, + restConfig, + params, + data, + callback, + }, isType); + }; /** * Remove a specific cover of a type */ diff --git a/front/src/app/back-api/user-media-advancement-resource.ts b/front/src/app/back-api/user-media-advancement-resource.ts index e27c2d9..e1e264b 100644 --- a/front/src/app/back-api/user-media-advancement-resource.ts +++ b/front/src/app/back-api/user-media-advancement-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {Long, UserMediaAdvancement, MediaInformationsDelta, isUserMediaAdvancement, } from "./model" export namespace UserMediaAdvancementResource { diff --git a/front/src/app/back-api/user-resource.ts b/front/src/app/back-api/user-resource.ts index c254028..872274a 100644 --- a/front/src/app/back-api/user-resource.ts +++ b/front/src/app/back-api/user-resource.ts @@ -1,7 +1,7 @@ /** * API of the server (auto-generated code) */ -import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, ProgressCallback, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" +import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import {Long, UserKarideo, UserOut, isUserKarideo, isUserOut, } from "./model" export namespace UserResource { diff --git a/front/src/app/scene/season-edit/season-edit.html b/front/src/app/scene/season-edit/season-edit.html index d995a59..8d25c7d 100644 --- a/front/src/app/scene/season-edit/season-edit.html +++ b/front/src/app/scene/season-edit/season-edit.html @@ -147,7 +147,8 @@ [mediaUploaded]="upload.mediaSendSize" [mediaSize]="upload.mediaSize" [result]="upload.result" - [error]="upload.error"> + [error]="upload.error" + (abort)="abortUpload()"> { self.upload.mediaSendSize = count; self.upload.mediaSize = total; - }) + }, this.cancelHandle) .then((response: any) => { self.upload.result = 'Cover added done'; // we retrive the whiole media ==> update data ... diff --git a/front/src/app/scene/series-edit/series-edit.html b/front/src/app/scene/series-edit/series-edit.html index ff0e34d..2e9ef27 100644 --- a/front/src/app/scene/series-edit/series-edit.html +++ b/front/src/app/scene/series-edit/series-edit.html @@ -171,7 +171,8 @@ [mediaUploaded]="upload.mediaSendSize" [mediaSize]="upload.mediaSize" [result]="upload.result" - [error]="upload.error"> + [error]="upload.error" + (abort)="abortUpload()"> { self.upload.mediaSendSize = count; self.upload.mediaSize = total; - }) + }, this.cancelHandle) .then((response: any) => { self.upload.result = 'Cover added done'; // we retrive the whiole media ==> update data ... diff --git a/front/src/app/scene/upload/file-drag-n-drop.directive.ts b/front/src/app/scene/upload/file-drag-n-drop.directive.ts new file mode 100644 index 0000000..a55192a --- /dev/null +++ b/front/src/app/scene/upload/file-drag-n-drop.directive.ts @@ -0,0 +1,44 @@ +import { Directive, HostListener, HostBinding, Output, EventEmitter, Input } from '@angular/core'; + +@Directive({ + selector: '[fileDragDrop]' +}) +export class FileDragNDropDirective { + //@Input() private allowed_extensions : Array = ['png', 'jpg', 'bmp']; + @Output() private filesChangeEmiter: EventEmitter = new EventEmitter(); + //@Output() private filesInvalidEmiter : EventEmitter = new EventEmitter(); + @HostBinding('style.background') private background = '#eee'; + @HostBinding('style.border') private borderStyle = '2px dashed'; + @HostBinding('style.border-color') private borderColor = '#696D7D'; + @HostBinding('style.border-radius') private borderRadius = '10px'; + + constructor() { } + + @HostListener('dragover', ['$event']) public onDragOver(evt) { + evt.preventDefault(); + evt.stopPropagation(); + this.background = 'lightgray'; + this.borderColor = 'cadetblue'; + this.borderStyle = '3px solid'; + } + + @HostListener('dragleave', ['$event']) public onDragLeave(evt) { + evt.preventDefault(); + evt.stopPropagation(); + this.background = '#eee'; + this.borderColor = '#696D7D'; + this.borderStyle = '2px dashed'; + } + + @HostListener('drop', ['$event']) public onDrop(evt) { + evt.preventDefault(); + evt.stopPropagation(); + this.background = '#eee'; + this.borderColor = '#696D7D'; + this.borderStyle = '2px dashed'; + + let files = evt.dataTransfer.files; + let valid_files: Array = files; + this.filesChangeEmiter.emit(valid_files); + } +} \ No newline at end of file diff --git a/front/src/app/scene/upload/upload.html b/front/src/app/scene/upload/upload.html index 015038a..f44d140 100644 --- a/front/src/app/scene/upload/upload.html +++ b/front/src/app/scene/upload/upload.html @@ -3,34 +3,19 @@ Upload Media

-
- - - - - - - - - - - - - - - -
format: - The format of the media permit to automatic find meta-data:
- Univers:Series name-sXX-eXX-my name of my media.mkv
- example: Stargate:SG1-s55-e22-Asgard.mkv
-
Media: - -
+
+

+
+ + +
+

+
+ The format of the media permit to automatic find meta-data:
+ Univers:Series name-sXX-eXX-my name of my media.mkv
+ example: Stargate:SG1-s55-e22-Asgard.mkv
+
@if(parsedElement.length !== 0) {
@@ -210,11 +195,13 @@
}
- + [error]="upload.error" + (abort)="abortUpload()">