Compare commits

...

16 Commits
v0.2.0 ... main

125 changed files with 15765 additions and 24661 deletions

View File

@ -1,7 +1,32 @@
#!/bin/bash
version_file="../version.txt"
# update new release dependency
cd back
mvn versions:set -DnewVersion=$(cat ../version.txt)
# update the Maven version number
mvn versions:set -DnewVersion=$(sed 's/dev/SNAPSHOT/g' $version_file)
if grep -q "DEV" "$version_file"; then
# update all versions release of dependency
mvn versions:use-latest-releases
# update our manage dependency as snapshoot
mvn versions:use-latest-versions -Dincludes=kangaroo-and-rabbit
else
# update our manage dependency as release (must be done before)
mvn versions:use-latest-releases -Dincludes=kangaroo-and-rabbit
fi
cd -
cd front
if grep -q "dev" "$version_file"; then
# update all dependency
pnpm install
pnpm run update_packages
else
# in case of release ==> can not do it automatically ...
echo not implemented
fi
cd -

View File

@ -6,7 +6,7 @@
FROM archlinux:base-devel AS builder
# update system
RUN pacman -Syu --noconfirm && pacman-db-upgrade \
&& pacman -S --noconfirm jdk-openjdk maven npm \
&& pacman -S --noconfirm jdk-openjdk maven npm pnpm \
&& pacman -Scc --noconfirm
ENV PATH /tmp/node_modules/.bin:$PATH
@ -29,14 +29,15 @@ RUN mvn clean compile assembly:single
######################################################################################
FROM builder AS buildFront
ADD front/package-lock.json \
front/package.json \
RUN echo "@kangaroo-and-rabbit:registry=https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/npm/" > /root/.npmrc
ADD front/package.json \
front/karma.conf.js \
front/protractor.conf.js \
/tmp/
# install and cache app dependencies
RUN npm install
RUN pnpm install
ADD front/e2e \
front/tsconfig.json \
@ -58,15 +59,6 @@ FROM bellsoft/liberica-openjdk-alpine:latest
# add wget to manage the health check...
RUN apk add --no-cache wget
#FROM archlinux:base
#RUN pacman -Syu --noconfirm && pacman-db-upgrade
## install package
#RUN pacman -S --noconfirm jdk-openjdk wget
## intall npm
#RUN pacman -S --noconfirm npm
## clean all the caches Need only on the release environment
#RUN pacman -Scc --noconfirm
ENV LANG=C.UTF-8
COPY --from=buildBack /tmp/out/maven/*.jar /application/application.jar

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId>
<artifactId>karideo</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
<properties>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
@ -20,12 +20,17 @@
<dependency>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.6.1</version>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.1</version>
</dependency>
<!--
************************************************************
@ -35,15 +40,25 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<version>5.11.0-M2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
@ -180,24 +195,10 @@
<mainClass>org.kar.karideo.WebLauncher</mainClass>
</configuration>
</plugin>
<!-- Check the style of the code -->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<failsOnError>true</failsOnError>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.12.2</version>
<version>2.23.0</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@ -221,7 +222,23 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->

View File

@ -10,10 +10,7 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.catcher.ExceptionCatcher;
import org.kar.archidata.catcher.FailExceptionCatcher;
import org.kar.archidata.catcher.InputExceptionCatcher;
import org.kar.archidata.catcher.SystemExceptionCatcher;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.db.DBConfig;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
@ -21,17 +18,18 @@ import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.karideo.api.Front;
import org.kar.karideo.api.HealthCheck;
import org.kar.karideo.api.MediaResource;
import org.kar.karideo.api.SeasonResource;
import org.kar.karideo.api.SeriesResource;
import org.kar.karideo.api.TypeResource;
import org.kar.karideo.api.UserMediaAdvancementResource;
import org.kar.karideo.api.UserResource;
import org.kar.karideo.api.VideoResource;
import org.kar.karideo.filter.KarideoAuthenticationFilter;
import org.kar.karideo.migration.Initialization;
import org.kar.karideo.migration.Migration20230810;
import org.kar.karideo.migration.Migration20231015;
import org.kar.karideo.migration.Migration20231126;
import org.kar.karideo.migration.Migration20240226;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -60,7 +58,7 @@ public class WebLauncher {
migrationEngine.add(new Migration20230810());
migrationEngine.add(new Migration20231015());
migrationEngine.add(new Migration20231126());
//migrationEngine.add(new Migration20231126());
migrationEngine.add(new Migration20240226());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig);
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
@ -94,17 +92,14 @@ public class WebLauncher {
// global authentication system
rc.register(KarideoAuthenticationFilter.class);
// register exception catcher
rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
rc.register(ExceptionCatcher.class);
GenericCatcher.addAll(rc);
// add default resource:
rc.register(UserResource.class);
rc.register(SeriesResource.class);
rc.register(DataResource.class);
rc.register(SeasonResource.class);
rc.register(TypeResource.class);
rc.register(VideoResource.class);
rc.register(MediaResource.class);
rc.register(UserMediaAdvancementResource.class);
rc.register(HealthCheck.class);

View File

@ -1,7 +1,20 @@
package org.kar.karideo;
import java.util.List;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.externalRestApi.AnalyzeApi;
import org.kar.archidata.externalRestApi.TsGenerateApi;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.karideo.api.Front;
import org.kar.karideo.api.HealthCheck;
import org.kar.karideo.api.MediaResource;
import org.kar.karideo.api.SeasonResource;
import org.kar.karideo.api.SeriesResource;
import org.kar.karideo.api.TypeResource;
import org.kar.karideo.api.UserMediaAdvancementResource;
import org.kar.karideo.api.UserResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -10,7 +23,18 @@ public class WebLauncherLocal extends WebLauncher {
private WebLauncherLocal() {}
public static void main(final String[] args) throws InterruptedException {
public static void generateObjects() throws Exception {
LOGGER.info("Generate APIs");
final List<Class<?>> listOfResources = List.of(Front.class, HealthCheck.class, SeasonResource.class, SeriesResource.class, TypeResource.class, UserMediaAdvancementResource.class,
UserResource.class, MediaResource.class, DataResource.class);
final AnalyzeApi api = new AnalyzeApi();
api.addAllApi(listOfResources);
TsGenerateApi.generateApi(api, "../front/src/app/back-api/");
LOGGER.info("Generate APIs (DONE)");
}
public static void main(final String[] args) throws Exception {
generateObjects();
final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process();
LOGGER.info("end-configure the server & wait finish process:");

View File

@ -1,6 +1,5 @@
package org.kar.karideo.api;
import org.kar.archidata.api.FrontGeneric;
import jakarta.ws.rs.Path;

View File

@ -6,6 +6,7 @@ import org.kar.archidata.tools.JWTWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@ -18,11 +19,13 @@ import jakarta.ws.rs.core.Response;
public class HealthCheck {
static final Logger LOGGER = LoggerFactory.getLogger(HealthCheck.class);
public record HealthResult(
String value) {};
public record HealthResult(String value) {
};
@GET
@PermitAll
@Operation(description = "Get the server state (health)", tags = "SYSTEM")
public HealthResult getHealth() throws FailException {
if (JWTWrapper.getPublicKeyJson() == null && !ConfigBaseVariable.getTestMode()) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Missing Jwt public token");

View File

@ -4,12 +4,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.exception.InputException;
import org.kar.archidata.model.Data;
@ -21,6 +24,7 @@ import org.kar.karideo.model.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
@ -31,22 +35,23 @@ 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("/video")
@Produces({ MediaType.APPLICATION_JSON })
public class VideoResource {
static final Logger LOGGER = LoggerFactory.getLogger(VideoResource.class);
@Path("/media")
@Produces(MediaType.APPLICATION_JSON)
public class MediaResource {
static final Logger LOGGER = LoggerFactory.getLogger(MediaResource.class);
@GET
@RolesAllowed("USER")
public List<Media> get() throws Exception {
@Operation(description = "Get all Media", tags = "GLOBAL")
public List<Media> gets() throws Exception {
return DataAccess.gets(Media.class);
}
@GET
@Path("{id}")
@RolesAllowed("USER")
@Operation(description = "Get a specific Media with his ID", tags = "GLOBAL")
public Media get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Media.class, id);
}
@ -55,8 +60,9 @@ public class VideoResource {
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Media put(@PathParam("id") final Long id, final String jsonRequest) throws Exception {
System.out.println("update video " + id + " ==> '" + jsonRequest + "'");
@Operation(description = "Modify a specific Media", tags = "GLOBAL")
public Media patch(@PathParam("id") final Long id, @AsyncType(Media.class) final String jsonRequest) throws Exception {
LOGGER.info("update video {} ==> '{}'", id, jsonRequest);
DataAccess.updateWithJson(Media.class, id, jsonRequest);
return DataAccess.get(Media.class, id);
}
@ -75,13 +81,22 @@ public class VideoResource {
}
@POST
@Path("/upload/")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
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 {
@Operation(description = "Create a new Media", tags = "GLOBAL")
@TypeScriptProgress
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);
@ -93,16 +108,16 @@ public class VideoResource {
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");
@ -112,7 +127,7 @@ public class VideoResource {
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);
@ -121,33 +136,33 @@ public class VideoResource {
ex.printStackTrace();
throw new FailException("can not create input media (the data model has an internal error");
}
} else if (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();
DataResource.undelete(data.id);
DataTools.undelete(data.uuid);
data.deleted = false;
} else {
System.out.println("Data already exist ... all good");
LOGGER.info("Data already exist ... all good");
System.out.flush();
}
// Fist step: retive all the Id of each parents:...
System.out.println("Find typeNode");
// Fist step: retieve all the Id of each parents:...
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) {
@ -158,14 +173,13 @@ public class VideoResource {
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;
media.dataId = data.id;
media.dataId = data.uuid;
media.typeId = typeNode.id;
media.seriesId = null;
if (seriesNode != null) {
@ -180,44 +194,56 @@ public class VideoResource {
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");
}
}
@POST
@Path("{id}/add_cover")
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Media.class, id, fileName, fileInputStream, fileMetaData);
@AsyncType(Media.class)
@Operation(description = "Upload a new season cover media", tags = "GLOBAL")
@TypeScriptProgress
public Media uploadCover( //
@PathParam("id") final Long id, //
@FormDataParam("fileName") final String fileName, //
@FormDataParam("file") final InputStream fileInputStream, //
@FormDataParam("file") final FormDataContentDisposition fileMetaData//
) throws Exception {
DataTools.uploadCover(Media.class, id, fileName, fileInputStream, fileMetaData);
return DataAccess.get(Media.class, id);
}
@GET
@Path("{id}/rm_cover/{coverId}")
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception {
AddOnManyToMany.removeLink(Media.class, id, "cover", coverId);
return Response.ok(DataAccess.get(Media.class, id)).build();
@Operation(description = "Remove a specific cover of a media", tags = "GLOBAL")
public Media removeCover( //
@PathParam("id") final Long id, //
@PathParam("coverId") final UUID coverId //
) throws Exception {
AddOnDataJson.removeLink(Media.class, id, "covers", coverId);
return DataAccess.get(Media.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception {
@Operation(description = "Remove a specific Media", tags = "GLOBAL")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Media.class, id);
return Response.ok().build();
}
}

View File

@ -2,19 +2,23 @@ package org.kar.karideo.api;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.tools.DataTools;
import org.kar.karideo.model.Season;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
@ -25,23 +29,16 @@ 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("/season")
@Produces({ MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class SeasonResource {
static final Logger LOGGER = LoggerFactory.getLogger(SeasonResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Season getWithId(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Season.class, id);
}
@GET
@RolesAllowed("USER")
public List<Season> get() throws Exception {
@Operation(description = "Get a specific Season with his ID", tags = "GLOBAL")
public List<Season> gets() throws Exception {
return DataAccess.gets(Season.class);
}
@ -49,26 +46,27 @@ public class SeasonResource {
@Path("{id}")
@RolesAllowed("USER")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Get all season", tags = "GLOBAL")
public Season get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Season.class, id);
}
/* =============================================================================
* ADMIN SECTION:
* ============================================================================= */
/* ============================================================================= ADMIN SECTION: ============================================================================= */
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Season put(final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Season.class, jsonRequest);
@Operation(description = "Create a new season", tags = "GLOBAL")
public Season post(final Season jsonRequest) throws Exception {
return DataAccess.insert(jsonRequest);
}
@PATCH
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Season put(@PathParam("id") final Long id, final String jsonRequest) throws Exception {
@Operation(description = "Modify a specific season", tags = "GLOBAL")
public Season patch(@PathParam("id") final Long id, @AsyncType(Season.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Season.class, id, jsonRequest);
return DataAccess.get(Season.class, id);
}
@ -76,26 +74,30 @@ public class SeasonResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception {
@Operation(description = "Remove a specific season", tags = "GLOBAL")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Season.class, id);
return Response.ok().build();
}
@POST
@Path("{id}/add_cover")
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Season.class, id, fileName, fileInputStream, fileMetaData);
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Operation(description = "Upload a new season cover season", tags = "GLOBAL")
@TypeScriptProgress
public Season uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Season.class, id, fileName, fileInputStream, fileMetaData);
return DataAccess.get(Season.class, id);
}
@GET
@Path("{id}/rm_cover/{coverId}")
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception {
AddOnManyToMany.removeLink(Season.class, id, "cover", coverId);
return Response.ok(DataAccess.get(Season.class, id)).build();
@Operation(description = "Remove a specific cover of a season", tags = "GLOBAL")
public Season removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Season.class, id, "covers", coverId);
return DataAccess.get(Season.class, id);
}
public static Season getOrCreate(final String name, final Long seriesId) {

View File

@ -2,19 +2,23 @@ package org.kar.karideo.api;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.tools.DataTools;
import org.kar.karideo.model.Series;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
@ -25,23 +29,16 @@ 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("/series")
@Produces({ MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class SeriesResource {
static final Logger LOGGER = LoggerFactory.getLogger(SeriesResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Series getWithId(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Series.class, id);
}
@GET
@RolesAllowed("USER")
public List<Series> get() throws Exception {
@Operation(description = "Get all Series", tags = "GLOBAL")
public List<Series> gets() throws Exception {
return DataAccess.gets(Series.class);
}
@ -49,26 +46,27 @@ public class SeriesResource {
@Path("{id}")
@RolesAllowed("USER")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Get a specific Series with his ID", tags = "GLOBAL")
public Series get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Series.class, id);
}
/* =============================================================================
* ADMIN SECTION:
* ============================================================================= */
/* ============================================================================= ADMIN SECTION: ============================================================================= */
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Series put(final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Series.class, jsonRequest);
@Operation(description = "Create a new Series", tags = "GLOBAL")
public Series post(final Series jsonRequest) throws Exception {
return DataAccess.insert(jsonRequest);
}
@PATCH
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Series put(@PathParam("id") final Long id, final String jsonRequest) throws Exception {
@Operation(description = "Modify a specific Series", tags = "GLOBAL")
public Series patch(@PathParam("id") final Long id, @AsyncType(Series.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Series.class, id, jsonRequest);
return DataAccess.get(Series.class, id);
}
@ -76,26 +74,30 @@ public class SeriesResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception {
@Operation(description = "Remove a specific Series", tags = "GLOBAL")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Series.class, id);
return Response.ok().build();
}
@POST
@Path("{id}/add_cover")
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Series.class, id, fileName, fileInputStream, fileMetaData);
@Operation(description = "Upload a new season cover Series", tags = "GLOBAL")
@TypeScriptProgress
public Series uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Series.class, id, fileName, fileInputStream, fileMetaData);
return DataAccess.get(Series.class, id);
}
@GET
@Path("{id}/rm_cover/{coverId}")
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception {
AddOnManyToMany.removeLink(Series.class, id, "cover", coverId);
return Response.ok(DataAccess.get(Series.class, id)).build();
@Operation(description = "Remove a specific Series of a season", tags = "GLOBAL")
public Series removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Series.class, id, "covers", coverId);
return DataAccess.get(Series.class, id);
}
public static Series getOrCreate(final String name, final Long typeId) {

View File

@ -2,18 +2,22 @@ package org.kar.karideo.api;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.tools.DataTools;
import org.kar.karideo.model.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
@ -24,23 +28,16 @@ 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("/type")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class TypeResource {
static final Logger LOGGER = LoggerFactory.getLogger(TypeResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Type getWithId(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Type.class, id);
}
@GET
@RolesAllowed("USER")
public List<Type> get() throws Exception {
@Operation(description = "Get all Type", tags = "GLOBAL")
public List<Type> gets() throws Exception {
return DataAccess.gets(Type.class);
}
@ -48,6 +45,7 @@ public class TypeResource {
@Path("{id}")
@RolesAllowed("USER")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Get a specific Type with his ID", tags = "GLOBAL")
public Type get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Type.class, id);
}
@ -56,22 +54,22 @@ public class TypeResource {
return DataAccess.get(Type.class, id);
}
/* =============================================================================
* ADMIN SECTION:
* ============================================================================= */
/* ============================================================================= ADMIN SECTION: ============================================================================= */
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Type put(final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Type.class, jsonRequest);
@Operation(description = "Create a new Type", tags = "GLOBAL")
public Type post(final Type jsonRequest) throws Exception {
return DataAccess.insert(jsonRequest);
}
@PATCH
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Type put(@PathParam("id") final Long id, final String jsonRequest) throws Exception {
@Operation(description = "Modify a specific Type", tags = "GLOBAL")
public Type patch(@PathParam("id") final Long id, @AsyncType(Type.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Type.class, id, jsonRequest);
return DataAccess.get(Type.class, id);
}
@ -79,26 +77,30 @@ public class TypeResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception {
@Operation(description = "Remove a specific Type", tags = "GLOBAL")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Type.class, id);
return Response.ok().build();
}
@POST
@Path("{id}/add_cover")
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Type.class, id, fileName, fileInputStream, fileMetaData);
@Operation(description = "Upload a new season cover Type", tags = "GLOBAL")
@TypeScriptProgress
public Type uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Type.class, id, fileName, fileInputStream, fileMetaData);
return DataAccess.get(Type.class, id);
}
@GET
@Path("{id}/rm_cover/{coverId}")
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception {
AddOnManyToMany.removeLink(Type.class, id, "cover", coverId);
return Response.ok(DataAccess.get(Type.class, id)).build();
@Operation(description = "Remove a specific cover of a type", tags = "GLOBAL")
public Type removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Type.class, id, "covers", coverId);
return DataAccess.get(Type.class, id);
}
public static Type getOrCreate(final String name) {

View File

@ -11,6 +11,7 @@ import org.kar.karideo.model.UserMediaAdvancement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
@ -21,37 +22,34 @@ import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
@Path("/advancement")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class UserMediaAdvancementResource {
static final Logger LOGGER = LoggerFactory.getLogger(UserMediaAdvancementResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public UserMediaAdvancement getWithId(@Context final SecurityContext sc, @PathParam("id") final Long id) throws Exception {
@Operation(description = "Get a specific user advancement with his ID", tags = "GLOBAL")
public UserMediaAdvancement get(@Context final SecurityContext sc, @PathParam("id") final Long id) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
return DataAccess.getWhere(UserMediaAdvancement.class, new Condition(new QueryAnd(new QueryCondition("mediaId", "=", id), new QueryCondition("userId", "=", gc.userByToken.id))));
}
@GET
@RolesAllowed("USER")
@Operation(description = "Get all user advancement", tags = "GLOBAL")
public List<UserMediaAdvancement> gets(@Context final SecurityContext sc) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
return DataAccess.getsWhere(UserMediaAdvancement.class, new Condition(new QueryCondition("userId", "=", gc.userByToken.id)));
}
/* =============================================================================
* Modification SECTION:
* ============================================================================= */
/* ============================================================================= Modification SECTION: ============================================================================= */
public record MediaInformations(
int time,
float percent,
int count) {}
public record MediaInformations(int time, float percent, int count) {
}
// @POST
// @Path("{id}")
@ -68,17 +66,16 @@ public class UserMediaAdvancementResource {
return DataAccess.insert(elem);
}
public record MediaInformationsDelta(
int time,
float percent,
boolean addCount) {}
public record MediaInformationsDelta(int time, float percent, boolean addCount) {
}
@PATCH
@Path("{id}")
@RolesAllowed("USER")
@Consumes(MediaType.APPLICATION_JSON)
public UserMediaAdvancement put(@Context final SecurityContext sc, @PathParam("id") final Long id, final MediaInformationsDelta data) throws Exception {
final UserMediaAdvancement elem = getWithId(sc, id);
@Operation(description = "Modify a user advancement", tags = "GLOBAL")
public UserMediaAdvancement patch(@Context final SecurityContext sc, @PathParam("id") final Long id, final MediaInformationsDelta data) throws Exception {
final UserMediaAdvancement elem = get(sc, id);
if (elem == null) {
// insert element
if (data.addCount) {
@ -100,10 +97,10 @@ public class UserMediaAdvancementResource {
@DELETE
@Path("{id}")
@RolesAllowed("USER")
public Response delete(@Context final SecurityContext sc, @PathParam("id") final Long id) throws Exception {
final UserMediaAdvancement elem = getWithId(sc, id);
@Operation(description = "Remove a specific user advancement", tags = "GLOBAL")
public void remove(@Context final SecurityContext sc, @PathParam("id") final Long id) throws Exception {
final UserMediaAdvancement elem = get(sc, id);
DataAccess.delete(UserMediaAdvancement.class, elem.id);
return Response.ok().build();
}
}

View File

@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@ -20,7 +21,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;
@Path("/users")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
@ -41,7 +42,8 @@ public class UserResource {
// curl http://localhost:9993/api/users
@GET
@RolesAllowed("ADMIN")
public List<UserKarideo> getUsers() {
@Operation(description = "Get all the users", tags = "SYSTEM")
public List<UserKarideo> gets() {
System.out.println("getUsers");
try {
return DataAccess.gets(UserKarideo.class);
@ -56,7 +58,8 @@ public class UserResource {
@GET
@Path("{id}")
@RolesAllowed("ADMIN")
public UserKarideo getUsers(@Context final SecurityContext sc, @PathParam("id") final long userId) {
@Operation(description = "Get a specific user data", tags = "SYSTEM")
public UserKarideo get(@Context final SecurityContext sc, @PathParam("id") final long userId) {
System.out.println("getUser " + userId);
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
System.out.println("===================================================");
@ -74,6 +77,7 @@ public class UserResource {
@GET
@Path("me")
@RolesAllowed("USER")
@Operation(description = "Get the user personal data", tags = "SYSTEM")
public UserOut getMe(@Context final SecurityContext sc) {
LOGGER.debug("getMe()");
final GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@ -1,5 +1,8 @@
package org.kar.karideo.migration;
import java.util.List;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.User;
@ -8,11 +11,16 @@ import org.kar.karideo.model.Season;
import org.kar.karideo.model.Series;
import org.kar.karideo.model.Type;
import org.kar.karideo.model.UserMediaAdvancement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Initialization extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Initialization.class);
public static final int KARSO_INITIALISATION_ID = 1;
public static final List<Class<?>> CLASSES_BASE = List.of(Data.class, Media.class, Type.class, Series.class, Season.class, User.class, UserMediaAdvancement.class);
@Override
public String getName() {
return "Initialization";
@ -24,31 +32,24 @@ public class Initialization extends MigrationSqlStep {
@Override
public void generateStep() throws Exception {
addClass(Data.class);
addClass(Media.class);
addClass(Type.class);
addClass(Series.class);
addClass(Season.class);
addClass(User.class);
addClass(UserMediaAdvancement.class);
for (final Class<?> clazz : CLASSES_BASE) {
addClass(clazz);
}
addAction("""
INSERT INTO `type` (`id`, `name`, `description`) VALUES
(1, 'Documentary', 'Documentary (animals, space, earth...)'),
(2, 'Movie', 'Movie with real humans (film)'),
(3, 'Animation', 'Animation movies (film)'),
(4, 'Short movie', 'Small movies (less 2 minutes)'),
(5, 'TV show', 'TV show for old peoples'),
(6, 'Animation TV show', 'TV show for young peoples'),
(7, 'Theater', 'Theater play'),
(8, 'One man show', 'Recorded stand up'),
(9, 'Concert', 'Recorded concert'),
(10, 'Opera', 'Recorded opera');
(UUID_TO_BIN('15237fd7-d4ee-11ee-a8dd-02420a030203'), 'Documentary', 'Documentary (animals, space, earth...)'),
(UUID_TO_BIN('553146c1-d4ee-11ee-a8dd-02420a030203'), 'Movie', 'Movie with real humans (film)'),
(UUID_TO_BIN('59c430a3-d4ee-11ee-a8dd-02420a030203'), 'Animation', 'Animation movies (film)'),
(UUID_TO_BIN('5cd619e3-d4ee-11ee-a8dd-02420a030203'), 'Short movie', 'Small movies (less 2 minutes)'),
(UUID_TO_BIN('5fbbf085-d4ee-11ee-a8dd-02420a030203'), 'TV show', 'TV show for old peoples'),
(UUID_TO_BIN('66dcb6ba-d4ee-11ee-a8dd-02420a030203'), 'Animation TV show', 'TV show for young peoples'),
(UUID_TO_BIN('69ee5c15-d4ee-11ee-a8dd-02420a030203'), 'Theater', 'Theater play'),
(UUID_TO_BIN('6ce72530-d4ee-11ee-a8dd-02420a030203'), 'One man show', 'Recorded stand up'),
(UUID_TO_BIN('6ff1691a-d4ee-11ee-a8dd-02420a030203'), 'Concert', 'Recorded concert'),
(UUID_TO_BIN('730815ef-d4ee-11ee-a8dd-02420a030203'), 'Opera', 'Recorded opera');
""");
// set start increment element to permit to add after default elements
addAction("""
ALTER TABLE `data` AUTO_INCREMENT = 1000;
""", "mysql");
addAction("""
ALTER TABLE `media` AUTO_INCREMENT = 1000;
""", "mysql");
@ -66,4 +67,25 @@ public class Initialization extends MigrationSqlStep {
""", "mysql");
}
public static void dropAll() {
for (final Class<?> element : CLASSES_BASE) {
try {
DataAccess.drop(element);
} catch (final Exception ex) {
LOGGER.error("Fail to drop table !!!!!!");
ex.printStackTrace();
}
}
}
public static void cleanAll() {
for (final Class<?> element : CLASSES_BASE) {
try {
DataAccess.cleanAll(element);
} catch (final Exception ex) {
LOGGER.error("Fail to clean table !!!!!!");
ex.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,137 @@
package org.kar.karideo.migration;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongLong;
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.archidata.tools.UuidUtils;
import org.kar.karideo.migration.model.CoverConversion;
import org.kar.karideo.migration.model.MediaConversion;
import org.kar.karideo.migration.model.UUIDConversion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20240226 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2024-02-26: convert base with UUID";
}
public Migration20240226() {
}
@Override
public void generateStep() throws Exception {
addAction("""
ALTER TABLE `data` ADD `uuid` binary(16) AFTER `id`;
""");
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final UUIDConversion elem : datas) {
elem.uuid = UuidUtils.nextUUID();
}
for (final UUIDConversion elem : datas) {
DataAccess.update(elem, elem.id, List.of("uuid"), new OverrideTableName("data"));
}
});
addAction("""
ALTER TABLE `data` CHANGE `uuid` `uuid` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
""");
final List<String> tableToTransform = List.of("media", "season", "series", "type", "user");
for (final String tableName : tableToTransform) {
addAction("ALTER TABLE `" + tableName + "` ADD `covers` text NULL;");
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
final List<CoverConversion> medias = DataAccess.gets(CoverConversion.class, new AccessDeletedItems(), new OverrideTableName(tableName));
final List<LinkTableLongLong> links = DataAccess.gets(LinkTableLongLong.class, new OverrideTableName(tableName + "_link_cover"));
LOGGER.info("Get somes data: {} {} {}", datas.size(), medias.size(), links.size());
for (final CoverConversion media : medias) {
final List<UUID> values = new ArrayList<>();
for (final LinkTableLongLong link : links) {
if (link.object1Id.equals(media.id)) {
for (final UUIDConversion data : datas) {
if (data.id.equals(link.object2Id)) {
values.add(data.uuid);
break;
}
}
break;
}
}
if (values.size() != 0) {
media.covers = values;
LOGGER.info(" update: {} => {}", media.id, media.covers);
DataAccess.update(media, media.id, List.of("covers"), new OverrideTableName(tableName));
}
}
});
addAction("DROP TABLE `" + tableName + "_link_cover`;");
}
addAction("""
ALTER TABLE `media` ADD `dataUUID` binary(16) AFTER dataId;
""");
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
final List<MediaConversion> medias = DataAccess.gets(MediaConversion.class, new AccessDeletedItems(), new OverrideTableName("media"));
for (final MediaConversion media : medias) {
for (final UUIDConversion data : datas) {
if (data.id.equals(media.dataId)) {
media.dataUUID = data.uuid;
DataAccess.update(media, media.id, List.of("dataUUID"), new OverrideTableName("media"));
break;
}
}
}
});
addAction("""
ALTER TABLE `media` DROP `dataId`;
""");
addAction("""
ALTER TABLE `media` CHANGE `dataUUID` `dataId` binary(16) NOT NULL;
""");
// Move the files...
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final UUIDConversion data : datas) {
final String origin = DataResource.getFileDataOld(data.id);
final String destination = DataResource.getFileData(data.uuid);
LOGGER.info("move file = {}", origin);
LOGGER.info(" ==> {}", destination);
try {
Files.move(Paths.get(origin), Paths.get(destination), StandardCopyOption.ATOMIC_MOVE);
} catch (final NoSuchFileException ex) {
LOGGER.error("MOVE_ERROR : {} -> {}", origin, destination);
}
}
});
/* 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));
""");
addAction("""
ALTER TABLE `data` ADD PRIMARY KEY `id` (`id`);
""");
}
}

View File

@ -0,0 +1,15 @@
package org.kar.karideo.migration.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import jakarta.persistence.Id;
public class CoverConversion {
@Id
public Long id = null;
@DataJson
public List<UUID> covers = null;
}

View File

@ -0,0 +1,12 @@
package org.kar.karideo.migration.model;
import java.util.UUID;
import jakarta.persistence.Id;
public class MediaConversion {
@Id
public Long id = null;
public Long dataId = null;
public UUID dataUUID = null;
}

View File

@ -0,0 +1,11 @@
package org.kar.karideo.migration.model;
import java.util.UUID;
import jakarta.persistence.Id;
public class UUIDConversion {
@Id
public Long id = null;
public UUID uuid = null;
}

View File

@ -1,53 +1,60 @@
package org.kar.karideo.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "media")
@JsonInclude(JsonInclude.Include.NON_NULL)
//@SQLDelete(sql = "UPDATE table_product SET deleted = true WHERE id=?")
//@Where(clause = "deleted=false")
public class Media extends GenericDataSoftDelete {
// Name of the media (this represent the title)
@Schema(description = "Name of the media (this represent the title)")
@Column(nullable = false, length = 0)
public String name;
// Description of the media
@Schema(description = "Description of the media")
@Column(length = 0)
public String description;
// Foreign Key Id of the data
@Schema(description = "Foreign Key Id of the data")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Data.class)
@Column(nullable = false)
public Long dataId;
// Type of the media")
public UUID dataId;
@Schema(description = "Type of the media")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Type.class)
public Long typeId;
// Series reference of the media
@Schema(description = "Series reference of the media")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Series.class)
public Long seriesId;
// Saison reference of the media
@Schema(description = "Season reference of the media")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Season.class)
public Long seasonId;
// Episide Id
@Schema(description = "Episode Id")
public Integer episode;
// ")
public Integer date;
// Creation years of the media
@Schema(description = "Creation years of the media")
public Integer time;
// Limitation Age of the media
@Schema(description = "Limitation Age of the media")
public Integer ageLimit;
// List of Id of the specific covers
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Data.class)
public List<Long> covers = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
public List<UUID> 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 + "]";
}
}

View File

@ -1,9 +1,10 @@
package org.kar.karideo.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.model.Data;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -11,7 +12,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@ -29,7 +29,7 @@ public class Season extends GenericDataSoftDelete {
@Schema(description = "series parent ID")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Series.class)
public Long parentId;
@Schema(description = "List of Id of the sopecific covers")
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Data.class)
public List<Long> covers = null;
@Schema(description = "List of Id of the specific covers")
@DataJson()
public List<UUID> covers = null;
}

View File

@ -1,9 +1,10 @@
package org.kar.karideo.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.model.Data;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -11,7 +12,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@ -29,7 +29,7 @@ public class Series extends GenericDataSoftDelete {
@Schema(description = "series parent ID")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Type.class)
public Long parentId;
@Schema(description = "List of Id of the sopecific covers")
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Data.class)
public List<Long> covers = null;
@Schema(description = "List of Id of the specific covers")
@DataJson()
public List<UUID> covers = null;
}

View File

@ -1,17 +1,16 @@
package org.kar.karideo.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.model.Data;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
@Table(name = "type")
@ -24,7 +23,7 @@ public class Type extends GenericDataSoftDelete {
@Column(length = 0)
@Schema(description = "Description of the media")
public String description;
@Schema(description = "List of Id of the sopecific covers")
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Data.class)
public List<Long> covers = null;
@Schema(description = "List of Id of the specific covers")
@DataJson()
public List<UUID> covers = null;
}

View File

@ -1,6 +1,7 @@
package org.kar.karideo.model;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -15,21 +16,22 @@ import jakarta.persistence.Table;
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserMediaAdvancement extends GenericDataSoftDelete {
@DataNotRead
@Column(nullable = false)
@Schema(description = "Foreign Key Id of the user")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = UserKarideo.class)
public long userId;
public Long userId;
@Column(nullable = false)
@Schema(description = "Id of the media")
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Media.class)
public long mediaId;
public Long mediaId;
@Column(nullable = false)
@Schema(description = "Percent of admencement in the media")
public float percent;
@Schema(description = "Percent of advancement in the media")
public Float percent;
@Column(nullable = false)
@Schema(description = "Number of second of admencement in the media")
public int time;
@Schema(description = "Number of second of advancement in the media")
public Integer time;
@Column(nullable = false)
@Schema(description = "Number of time this media has been read")
public int count;
public Integer count;
}

View File

@ -12,17 +12,23 @@ public class WebLauncherTest extends WebLauncher {
public WebLauncherTest() {
LOGGER.debug("Configure REST system");
// for local test:
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12345/test/api/";
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12342/test/api/";
// Enable the test mode permit to access to the test token (never use it in production).
ConfigBaseVariable.testMode = "true";
//ConfigBaseVariable.dbPort = "3306";
// for the test we a in memory sqlite..
if (true) {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
//ConfigBaseVariable.dbHost = "localhost";
//ConfigBaseVariable.dbUser = "root";
//ConfigBaseVariable.dbPassword = "ZERTYSDGFVHSDFGHJYZSDFGSQxfgsqdfgsqdrf4564654";
}
} else {
// Enable this if you want to access to a local MySQL base to test with an adminer
ConfigBaseVariable.bdDatabase = "test_db";
ConfigBaseVariable.dbPort = "3309";
ConfigBaseVariable.dbUser = "root";
ConfigBaseVariable.dbPassword = "password";
}
}
}

View File

@ -2,7 +2,6 @@
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"defaultProject" : "karideo",
"projects": {
"karideo": {
"root": "",
@ -16,9 +15,21 @@
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills" : "src/polyfills.ts",
"assets" : [ "src/assets", "src/favicon.ico" ],
"styles" : [ "src/styles.less", "src/generic_page.less", "src/theme.color.blue.less", "src/theme.checkbox.less", "src/theme.modal.less" ],
"preserveSymlinks": true,
"polyfills": [
"zone.js"
],
"assets": [
"src/assets",
"src/favicon.ico"
],
"styles": [
"src/styles.less",
"src/generic_page.less",
"src/theme.color.blue.less",
"src/theme.checkbox.less",
"src/theme.modal.less"
],
"scripts": []
},
"configurations": {
@ -31,41 +42,48 @@
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements" : [ {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
} ]
}
]
},
"develop": {
"optimization": false,
"outputHashing": "none",
"sourceMap" : true,
"namedChunks": true,
"aot" : true,
"aot": false,
"extractLicenses": true,
"vendorChunk": true,
"buildOptimizer" : false
"buildOptimizer": false,
"sourceMap": {
"scripts": true,
"styles": true,
"hidden": false,
"vendor": true
}
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget" : "karideo:build"
"buildTarget": "karideo:build"
},
"configurations": {
"production": {
"browserTarget" : "karideo:build:production"
"buildTarget": "karideo:build:production"
},
"develop": {
"browserTarget" : "karideo:build:develop"
"buildTarget": "karideo:build:develop"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget" : "karideo:build"
"buildTarget": "karideo:build"
}
},
"test": {
@ -73,11 +91,22 @@
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills" : "src/polyfills.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles" : [ "src/styles.less", "src/generic_page.less", "src/theme.color.blue.less", "src/theme.checkbox.less", "src/theme.modal.less" ],
"assets" : [ "src/assets", "src/favicon.ico" ]
"styles": [
"src/styles.less",
"src/generic_page.less",
"src/theme.color.blue.less",
"src/theme.checkbox.less",
"src/theme.modal.less"
],
"assets": [
"src/assets",
"src/favicon.ico"
]
}
},
"lint": {
@ -94,8 +123,13 @@
"TTTTTTlint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig" : [ "src/tsconfig.app.json", "src/tsconfig.spec.json" ],
"exclude" : [ "**/node_modules/**" ]
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@ -115,8 +149,12 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig" : [ "e2e/tsconfig.e2e.json" ],
"exclude" : [ "**/node_modules/**" ]
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}

21757
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,36 +6,45 @@
"all": "npm run build && npm run test",
"ng": "ng",
"dev": "ng serve --configuration=develop --watch --port 4202",
"dev-hot-update": "ng serve --configuration=develop --watch --hmr --port 4202",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"style": "prettier --write .",
"e2e": "ng e2e"
"e2e": "ng e2e",
"update_packages": "ncu --upgrade",
"install_dependency": "pnpm install --force",
"link_kar_cw": "pnpm link ../../kar-cw/dist/kar-cw/",
"unlink_kar_cw": "pnpm unlink ../../kar-cw/dist/kar-cw/"
},
"private": true,
"dependencies": {
"@angular/animations": "^14.2.10",
"@angular/cdk": "^14.2.7",
"@angular/common": "^14.2.10",
"@angular/compiler": "^14.2.10",
"@angular/core": "^14.2.10",
"@angular/forms": "^14.2.10",
"@angular/material": "^14.2.7",
"@angular/platform-browser": "^14.2.10",
"@angular/platform-browser-dynamic": "^14.2.10",
"@angular/router": "^14.2.10",
"rxjs": "^7.5.7",
"zone.js": "^0.12.0"
"@angular/animations": "^18.0.2",
"@angular/cdk": "^18.0.2",
"@angular/common": "^18.0.2",
"@angular/compiler": "^18.0.2",
"@angular/core": "^18.0.2",
"@angular/forms": "^18.0.2",
"@angular/material": "^18.0.2",
"@angular/platform-browser": "^18.0.2",
"@angular/platform-browser-dynamic": "^18.0.2",
"@angular/router": "^18.0.2",
"rxjs": "^7.8.1",
"zone.js": "^0.14.6",
"zod": "3.23.8",
"@kangaroo-and-rabbit/kar-cw": "^0.4.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.2.9",
"@angular-eslint/builder": "14.2.0",
"@angular-eslint/eslint-plugin": "14.2.0",
"@angular-eslint/eslint-plugin-template": "14.2.0",
"@angular-eslint/schematics": "14.2.0",
"@angular-eslint/template-parser": "14.2.0",
"@angular/cli": "^14.2.9",
"@angular/compiler-cli": "^14.2.10",
"@angular/language-service": "^14.2.10"
"@angular-devkit/build-angular": "^18.0.3",
"@angular-eslint/builder": "18.0.1",
"@angular-eslint/eslint-plugin": "18.0.1",
"@angular-eslint/eslint-plugin-template": "18.0.1",
"@angular-eslint/schematics": "18.0.1",
"@angular-eslint/template-parser": "18.0.1",
"@angular/cli": "^18.0.3",
"@angular/compiler-cli": "^18.0.2",
"@angular/language-service": "^18.0.2",
"npm-check-updates": "^16.14.20",
"tslib": "^2.6.3"
}
}

9816
front/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,10 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
import { ForbiddenScene, HomeOutScene, NotFound404Scene, SsoScene } from 'common/scene';
import { OnlyAdminGuard, OnlyUnregisteredGuardHome, OnlyUsersGuard, OnlyUsersGuardHome } from 'common/service';
import { HelpScene, HomeScene, SeasonEditScene, SeasonScene, SeriesEditScene, SeriesScene, SettingsScene, TypeScene, VideoEditScene, VideoScene } from './scene';
import { UploadScene } from './scene/upload/upload';
import { ForbiddenScene, OnlyUsersGuardHome, HomeOutScene, OnlyUnregisteredGuardHome, SsoScene, OnlyAdminGuard, OnlyUsersGuard, NotFound404Scene } from '@kangaroo-and-rabbit/kar-cw';
// import { HelpComponent } from './help/help.component';
// see https://angular.io/guide/router

View File

@ -1,11 +1,14 @@
<!-- Generig global menu -->
<app-top-menu [menu]="currentMenu" (callback)="eventOnMenu($event)"></app-top-menu>
<karcw-top-menu [menu]="currentMenu" (callback)="eventOnMenu($event)" />
<!-- all interfaced pages -->
<div class="main-content" *ngIf="autoConnectedDone">
@if(autoConnectedDone) {
<div class="main-content">
<router-outlet ></router-outlet>
</div>
<div class="main-content" *ngIf="!autoConnectedDone">
}
@else {
<div class="main-content">
<div class="generic-page">
<div class="fill-all colomn_mutiple">
<b style="color:red;">Auto-connection in progress</b>
@ -13,3 +16,4 @@
</div>
</div>
</div>
}

View File

@ -5,12 +5,8 @@
*/
import { Component, OnInit } from '@angular/core';
import { EventOnMenu } from 'common/component/top-menu/top-menu';
import { MenuItem, MenuPosition } from 'common/model';
import { UserService, SessionService, SSOService } from 'common/service';
import { isNullOrUndefined } from 'common/utils';
import { ArianeService } from './service';
import { UserRoles222 } from 'common/service/session';
import { ArianeService, MediaService, SeasonService, SeriesService, TypeService } from './service';
import { EventOnMenu, MenuItem, MenuPosition, SSOService, SessionService, UserRoles222, UserService, isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
enum MenuEventType {
SSO_LOGIN = "SSO_CALL_LOGIN",
@ -38,6 +34,11 @@ export class AppComponent implements OnInit {
location: string = "home";
constructor(
private mediaService: MediaService,
private seasonService: SeasonService,
private seriesService: SeriesService,
private typeService: TypeService,
private userService: UserService,
private sessionService: SessionService,
private ssoService: SSOService,
@ -52,7 +53,7 @@ export class AppComponent implements OnInit {
this.updateMainMenu();
let self = this;
this.sessionService.change.subscribe((isConnected) => {
console.log(`receive event from session ...${isConnected}`);
self.isConnected = isConnected;
self.autoConnectedDone = true;
self.updateMainMenu();
@ -67,13 +68,10 @@ export class AppComponent implements OnInit {
});
this.userService.checkAutoConnect().then(() => {
console.log(` ==>>>>> Autoconnect THEN !!!`);
self.autoConnectedDone = true;
}).catch(() => {
console.log(` ==>>>>> Autoconnect CATCH !!!`);
self.autoConnectedDone = true;
}).finally(() => {
console.log(` ==>>>>> Autoconnect FINALLY !!!`);
self.autoConnectedDone = true;
});
this.arianeService.segmentChange.subscribe((_segmentName: string) => {
@ -122,7 +120,6 @@ export class AppComponent implements OnInit {
}
updateMainMenu(): void {
console.log("update main menu :");
if (this.isConnected) {
this.currentMenu = [
{
@ -229,11 +226,9 @@ export class AppComponent implements OnInit {
},
];
}
console.log(" ==> DONE");
}
getSegmentDisplayable(): string {
let segment = this.arianeService.getCurrrentSegment();
let segment = this.arianeService.getCurrentSegment();
if (segment === "type") {
return "Type";
}

View File

@ -5,7 +5,7 @@
*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
@ -20,11 +20,24 @@ import { PopInCreateType } from './popin/create-type/create-type';
import { AppComponent } from './app.component';
import {
HomeScene, HelpScene, TypeScene, SeriesScene, SeasonScene, VideoScene, SettingsScene,
VideoEditScene, SeasonEditScene, SeriesEditScene
VideoEditScene, SeasonEditScene, SeriesEditScene,
} from './scene';
import { TypeService, DataService, SeriesService, SeasonService, VideoService, ArianeService, AdvancementService } from './service';
import {
DataService,
AdvancementService,
MediaService,
SeasonService,
SeriesService,
TypeService,
ArianeService,
} from './service';
import { UploadScene } from './scene/upload/upload';
import { common_module_declarations, common_module_imports, common_module_providers, common_module_exports } from 'common/module';
import { KarCWModule } from '@kangaroo-and-rabbit/kar-cw';
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: [
@ -47,24 +60,28 @@ import { common_module_declarations, common_module_imports, common_module_provid
SeasonEditScene,
SeriesEditScene,
UploadScene,
...common_module_declarations,
FileDragNDropDirective,
],
imports: [
FormsModule,
ReactiveFormsModule,
CommonModule,
BrowserModule,
RouterModule,
AppRoutingModule,
HttpClientModule,
...common_module_imports,
KarCWModule,
],
providers: [
TypeService,
{ provide: 'ENVIRONMENT', useValue: environment },
DataService,
SeriesService,
SeasonService,
VideoService,
ArianeService,
AdvancementService,
...common_module_providers,
MediaService,
SeasonService,
SeriesService,
TypeService,
ArianeService,
],
exports: [
AppComponent,
@ -73,10 +90,10 @@ import { common_module_declarations, common_module_imports, common_module_provid
ElementSeasonComponent,
ElementVideoComponent,
PopInCreateType,
...common_module_exports,
],
bootstrap: [
AppComponent
]
],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
})
export class AppModule { }

View File

@ -0,0 +1,128 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import {
UUID,
} from "../model";
export namespace DataResource {
/**
* Get back some data from the data environment (with a beautiful name (permit download with basic name)
*/
export function retrieveDataFull({
restConfig,
queries,
params,
data,
}: {
restConfig: RESTConfig,
queries: {
Authorization?: string,
},
params: {
name: string,
uuid: UUID,
},
data: string,
}): Promise<object> {
return RESTRequestJson({
restModel: {
endPoint: "/data/{uuid}/{name}",
requestType: HTTPRequestModel.GET,
},
restConfig,
params,
queries,
data,
});
};
/**
* Get back some data from the data environment
*/
export function retrieveDataId({
restConfig,
queries,
params,
data,
}: {
restConfig: RESTConfig,
queries: {
Authorization?: string,
},
params: {
uuid: UUID,
},
data: string,
}): Promise<object> {
return RESTRequestJson({
restModel: {
endPoint: "/data/{uuid}",
requestType: HTTPRequestModel.GET,
},
restConfig,
params,
queries,
data,
});
};
/**
* Get a thumbnail of from the data environment (if resize is possible)
*/
export function retrieveDataThumbnailId({
restConfig,
queries,
params,
data,
}: {
restConfig: RESTConfig,
queries: {
Authorization?: string,
},
params: {
uuid: UUID,
},
data: string,
}): Promise<object> {
return RESTRequestJson({
restModel: {
endPoint: "/data/thumbnail/{uuid}",
requestType: HTTPRequestModel.GET,
},
restConfig,
params,
queries,
data,
});
};
/**
* Insert a new data in the data environment
*/
export function uploadFile({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: {
file: File,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/data//upload/",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
},
restConfig,
data,
});
};
}

View File

@ -0,0 +1,6 @@
/**
* Interface of the server (auto-generated code)
*/
export namespace Front {
}

View File

@ -0,0 +1,35 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTConfig,
RESTRequestJson,
} from "../rest-tools";
import {
HealthResult,
isHealthResult,
} from "../model";
export namespace HealthCheck {
/**
* Get the server state (health)
*/
export function getHealth({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<HealthResult> {
return RESTRequestJson({
restModel: {
endPoint: "/health_check/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isHealthResult);
};
}

View File

@ -0,0 +1,12 @@
/**
* Interface of the server (auto-generated code)
*/
export * from "./data-resource"
export * from "./front"
export * from "./health-check"
export * from "./media-resource"
export * from "./season-resource"
export * from "./series-resource"
export * from "./type-resource"
export * from "./user-media-advancement-resource"
export * from "./user-resource"

View File

@ -0,0 +1,215 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTCallbacks,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
Media,
MediaWrite,
UUID,
ZodMedia,
isMedia,
} from "../model";
export namespace MediaResource {
/**
* Get a specific Media with his ID
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<Media> {
return RESTRequestJson({
restModel: {
endPoint: "/media/{id}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isMedia);
};
export const ZodGetsTypeReturn = zod.array(ZodMedia);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get all Media
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/media/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
/**
* Modify a specific Media
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: MediaWrite,
}): Promise<Media> {
return RESTRequestJson({
restModel: {
endPoint: "/media/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isMedia);
};
/**
* Remove a specific Media
*/
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/media/{id}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
restConfig,
params,
});
};
/**
* Remove a specific cover of a media
*/
export function removeCover({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
coverId: UUID,
id: Long,
},
}): Promise<Media> {
return RESTRequestJson({
restModel: {
endPoint: "/media/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, 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<Media> {
return RESTRequestJson({
restModel: {
endPoint: "/media/{id}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
callback,
}, isMedia);
};
/**
* Create a new Media
*/
export function uploadFile({
restConfig,
data,
callback,
}: {
restConfig: RESTConfig,
data: {
fileName: string,
file: File,
series: string,
universe: string,
season: string,
episode: string,
typeId: string,
title: string,
},
callback?: RESTCallbacks,
}): Promise<Media> {
return RESTRequestJson({
restModel: {
endPoint: "/media/",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
data,
callback,
}, isMedia);
};
}

View File

@ -0,0 +1,204 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTCallbacks,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
Season,
SeasonWrite,
UUID,
ZodSeason,
isSeason,
} from "../model";
export namespace SeasonResource {
/**
* Get all season
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<Season> {
return RESTRequestJson({
restModel: {
endPoint: "/season/{id}",
requestType: HTTPRequestModel.GET,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isSeason);
};
export const ZodGetsTypeReturn = zod.array(ZodSeason);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get a specific Season with his ID
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/season/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
/**
* Modify a specific season
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: SeasonWrite,
}): Promise<Season> {
return RESTRequestJson({
restModel: {
endPoint: "/season/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isSeason);
};
/**
* Create a new season
*/
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: SeasonWrite,
}): Promise<Season> {
return RESTRequestJson({
restModel: {
endPoint: "/season/",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
data,
}, isSeason);
};
/**
* Remove a specific season
*/
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/season/{id}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
restConfig,
params,
});
};
/**
* Remove a specific cover of a season
*/
export function removeCover({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
coverId: UUID,
id: Long,
},
}): Promise<Season> {
return RESTRequestJson({
restModel: {
endPoint: "/season/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, 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<Season> {
return RESTRequestJson({
restModel: {
endPoint: "/season/{id}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
callback,
}, isSeason);
};
}

View File

@ -0,0 +1,204 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTCallbacks,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
Series,
SeriesWrite,
UUID,
ZodSeries,
isSeries,
} from "../model";
export namespace SeriesResource {
/**
* Get a specific Series with his ID
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<Series> {
return RESTRequestJson({
restModel: {
endPoint: "/series/{id}",
requestType: HTTPRequestModel.GET,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isSeries);
};
export const ZodGetsTypeReturn = zod.array(ZodSeries);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get all Series
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/series/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
/**
* Modify a specific Series
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: SeriesWrite,
}): Promise<Series> {
return RESTRequestJson({
restModel: {
endPoint: "/series/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isSeries);
};
/**
* Create a new Series
*/
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: SeriesWrite,
}): Promise<Series> {
return RESTRequestJson({
restModel: {
endPoint: "/series/",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
data,
}, isSeries);
};
/**
* Remove a specific Series
*/
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/series/{id}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
restConfig,
params,
});
};
/**
* Remove a specific Series of a season
*/
export function removeCover({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
coverId: UUID,
id: Long,
},
}): Promise<Series> {
return RESTRequestJson({
restModel: {
endPoint: "/series/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, 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<Series> {
return RESTRequestJson({
restModel: {
endPoint: "/series/{id}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
callback,
}, isSeries);
};
}

View File

@ -0,0 +1,204 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTCallbacks,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
Type,
TypeWrite,
UUID,
ZodType,
isType,
} from "../model";
export namespace TypeResource {
/**
* Get a specific Type with his ID
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<Type> {
return RESTRequestJson({
restModel: {
endPoint: "/type/{id}",
requestType: HTTPRequestModel.GET,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isType);
};
export const ZodGetsTypeReturn = zod.array(ZodType);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get all Type
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/type/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
/**
* Modify a specific Type
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: TypeWrite,
}): Promise<Type> {
return RESTRequestJson({
restModel: {
endPoint: "/type/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isType);
};
/**
* Create a new Type
*/
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: TypeWrite,
}): Promise<Type> {
return RESTRequestJson({
restModel: {
endPoint: "/type/",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
data,
}, isType);
};
/**
* Remove a specific Type
*/
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/type/{id}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
restConfig,
params,
});
};
/**
* Remove a specific cover of a type
*/
export function removeCover({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
coverId: UUID,
id: Long,
},
}): Promise<Type> {
return RESTRequestJson({
restModel: {
endPoint: "/type/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, 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<Type> {
return RESTRequestJson({
restModel: {
endPoint: "/type/{id}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
callback,
}, isType);
};
}

View File

@ -0,0 +1,124 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTConfig,
RESTRequestJson,
RESTRequestVoid,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
MediaInformationsDeltaWrite,
UserMediaAdvancement,
ZodUserMediaAdvancement,
isUserMediaAdvancement,
} from "../model";
export namespace UserMediaAdvancementResource {
/**
* Get a specific user advancement with his ID
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<UserMediaAdvancement> {
return RESTRequestJson({
restModel: {
endPoint: "/advancement/{id}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isUserMediaAdvancement);
};
export const ZodGetsTypeReturn = zod.array(ZodUserMediaAdvancement);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get all user advancement
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/advancement/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
/**
* Modify a user advancement
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: MediaInformationsDeltaWrite,
}): Promise<UserMediaAdvancement> {
return RESTRequestJson({
restModel: {
endPoint: "/advancement/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isUserMediaAdvancement);
};
/**
* Remove a specific user advancement
*/
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/advancement/{id}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
restConfig,
params,
});
};
}

View File

@ -0,0 +1,93 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPMimeType,
HTTPRequestModel,
RESTConfig,
RESTRequestJson,
} from "../rest-tools";
import { z as zod } from "zod"
import {
Long,
UserKarideo,
UserOut,
ZodUserKarideo,
isUserKarideo,
isUserOut,
} from "../model";
export namespace UserResource {
/**
* Get a specific user data
*/
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
}): Promise<UserKarideo> {
return RESTRequestJson({
restModel: {
endPoint: "/users/{id}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isUserKarideo);
};
/**
* Get the user personal data
*/
export function getMe({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<UserOut> {
return RESTRequestJson({
restModel: {
endPoint: "/users/me",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isUserOut);
};
export const ZodGetsTypeReturn = zod.array(ZodUserKarideo);
export type GetsTypeReturn = zod.infer<typeof ZodGetsTypeReturn>;
export function isGetsTypeReturn(data: any): data is GetsTypeReturn {
try {
ZodGetsTypeReturn.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGetsTypeReturn' error=${e}`);
return false;
}
}
/**
* Get all the users
*/
export function gets({
restConfig,
}: {
restConfig: RESTConfig,
}): Promise<GetsTypeReturn> {
return RESTRequestJson({
restModel: {
endPoint: "/users/",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isGetsTypeReturn);
};
}

View File

@ -0,0 +1,7 @@
/**
* Interface of the server (auto-generated code)
*/
export * from "./model";
export * from "./api";
export * from "./rest-tools";

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodFloat = zod.number();
export type Float = zod.infer<typeof ZodFloat>;

View File

@ -0,0 +1,46 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodGenericData} from "./generic-data";
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
/**
* Deleted state
*/
deleted: zod.boolean().readonly().optional(),
});
export type GenericDataSoftDelete = zod.infer<typeof ZodGenericDataSoftDelete>;
export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelete {
try {
ZodGenericDataSoftDelete.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataSoftDelete' error=${e}`);
return false;
}
}
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataSoftDelete.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type GenericDataSoftDeleteWrite = zod.infer<typeof ZodGenericDataSoftDeleteWrite>;
export function isGenericDataSoftDeleteWrite(data: any): data is GenericDataSoftDeleteWrite {
try {
ZodGenericDataSoftDeleteWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,46 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
import {ZodGenericTiming} from "./generic-timing";
export const ZodGenericData = ZodGenericTiming.extend({
/**
* Unique Id of the object
*/
id: ZodLong.readonly(),
});
export type GenericData = zod.infer<typeof ZodGenericData>;
export function isGenericData(data: any): data is GenericData {
try {
ZodGenericData.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericData' error=${e}`);
return false;
}
}
export const ZodGenericDataWrite = ZodGenericData.omit({
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type GenericDataWrite = zod.infer<typeof ZodGenericDataWrite>;
export function isGenericDataWrite(data: any): data is GenericDataWrite {
try {
ZodGenericDataWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,48 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodIsoDate} from "./iso-date";
export const ZodGenericTiming = zod.object({
/**
* Create time of the object
*/
createdAt: ZodIsoDate.readonly().optional(),
/**
* When update the object
*/
updatedAt: ZodIsoDate.readonly().optional(),
});
export type GenericTiming = zod.infer<typeof ZodGenericTiming>;
export function isGenericTiming(data: any): data is GenericTiming {
try {
ZodGenericTiming.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericTiming' error=${e}`);
return false;
}
}
export const ZodGenericTimingWrite = ZodGenericTiming.omit({
createdAt: true,
updatedAt: true,
}).partial();
export type GenericTimingWrite = zod.infer<typeof ZodGenericTimingWrite>;
export function isGenericTimingWrite(data: any): data is GenericTimingWrite {
try {
ZodGenericTimingWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericTimingWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,35 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodHealthResult = zod.object({
});
export type HealthResult = zod.infer<typeof ZodHealthResult>;
export function isHealthResult(data: any): data is HealthResult {
try {
ZodHealthResult.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodHealthResult' error=${e}`);
return false;
}
}
export const ZodHealthResultWrite = ZodHealthResult.partial();
export type HealthResultWrite = zod.infer<typeof ZodHealthResultWrite>;
export function isHealthResultWrite(data: any): data is HealthResultWrite {
try {
ZodHealthResultWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodHealthResultWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,24 @@
/**
* Interface of the server (auto-generated code)
*/
export * from "./float"
export * from "./generic-data"
export * from "./generic-data-soft-delete"
export * from "./generic-timing"
export * from "./health-result"
export * from "./int"
export * from "./integer"
export * from "./iso-date"
export * from "./long"
export * from "./media"
export * from "./media-informations-delta"
export * from "./rest-error-response"
export * from "./season"
export * from "./series"
export * from "./timestamp"
export * from "./type"
export * from "./user"
export * from "./user-karideo"
export * from "./user-media-advancement"
export * from "./user-out"
export * from "./uuid"

View File

@ -0,0 +1,35 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const Zodint = zod.object({
});
export type int = zod.infer<typeof Zodint>;
export function isint(data: any): data is int {
try {
Zodint.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='Zodint' error=${e}`);
return false;
}
}
export const ZodintWrite = Zodint.partial();
export type intWrite = zod.infer<typeof ZodintWrite>;
export function isintWrite(data: any): data is intWrite {
try {
ZodintWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodintWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodInteger = zod.number().safe();
export type Integer = zod.infer<typeof ZodInteger>;

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodIsoDate = zod.string().datetime({ precision: 3 });
export type IsoDate = zod.infer<typeof ZodIsoDate>;

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodLong = zod.number();
export type Long = zod.infer<typeof ZodLong>;

View File

@ -0,0 +1,35 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodMediaInformationsDelta = zod.object({
});
export type MediaInformationsDelta = zod.infer<typeof ZodMediaInformationsDelta>;
export function isMediaInformationsDelta(data: any): data is MediaInformationsDelta {
try {
ZodMediaInformationsDelta.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodMediaInformationsDelta' error=${e}`);
return false;
}
}
export const ZodMediaInformationsDeltaWrite = ZodMediaInformationsDelta.partial();
export type MediaInformationsDeltaWrite = zod.infer<typeof ZodMediaInformationsDeltaWrite>;
export function isMediaInformationsDeltaWrite(data: any): data is MediaInformationsDeltaWrite {
try {
ZodMediaInformationsDeltaWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodMediaInformationsDeltaWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,86 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodUUID} from "./uuid";
import {ZodLong} from "./long";
import {ZodInteger} from "./integer";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodMedia = ZodGenericDataSoftDelete.extend({
/**
* Name of the media (this represent the title)
*/
name: zod.string(),
/**
* Description of the media
*/
description: zod.string().optional(),
/**
* Foreign Key Id of the data
*/
dataId: ZodUUID,
/**
* Type of the media
*/
typeId: ZodLong.optional(),
/**
* Series reference of the media
*/
seriesId: ZodLong.optional(),
/**
* Season reference of the media
*/
seasonId: ZodLong.optional(),
/**
* Episode Id
*/
episode: ZodInteger.optional(),
date: ZodInteger.optional(),
/**
* Creation years of the media
*/
time: ZodInteger.optional(),
/**
* Limitation Age of the media
*/
ageLimit: ZodInteger.optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID),
});
export type Media = zod.infer<typeof ZodMedia>;
export function isMedia(data: any): data is Media {
try {
ZodMedia.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodMedia' error=${e}`);
return false;
}
}
export const ZodMediaWrite = ZodMedia.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type MediaWrite = zod.infer<typeof ZodMediaWrite>;
export function isMediaWrite(data: any): data is MediaWrite {
try {
ZodMediaWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodMediaWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,43 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodUUID} from "./uuid";
import {Zodint} from "./int";
export const ZodRestErrorResponse = zod.object({
uuid: ZodUUID.optional(),
name: zod.string(),
message: zod.string(),
time: zod.string(),
status: Zodint,
statusMessage: zod.string(),
});
export type RestErrorResponse = zod.infer<typeof ZodRestErrorResponse>;
export function isRestErrorResponse(data: any): data is RestErrorResponse {
try {
ZodRestErrorResponse.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodRestErrorResponse' error=${e}`);
return false;
}
}
export const ZodRestErrorResponseWrite = ZodRestErrorResponse.partial();
export type RestErrorResponseWrite = zod.infer<typeof ZodRestErrorResponseWrite>;
export function isRestErrorResponseWrite(data: any): data is RestErrorResponseWrite {
try {
ZodRestErrorResponseWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodRestErrorResponseWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,60 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
import {ZodUUID} from "./uuid";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodSeason = ZodGenericDataSoftDelete.extend({
/**
* Name of the media (this represent the title)
*/
name: zod.string(),
/**
* Description of the media
*/
description: zod.string().optional(),
/**
* series parent ID
*/
parentId: ZodLong,
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID),
});
export type Season = zod.infer<typeof ZodSeason>;
export function isSeason(data: any): data is Season {
try {
ZodSeason.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodSeason' error=${e}`);
return false;
}
}
export const ZodSeasonWrite = ZodSeason.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type SeasonWrite = zod.infer<typeof ZodSeasonWrite>;
export function isSeasonWrite(data: any): data is SeasonWrite {
try {
ZodSeasonWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodSeasonWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,60 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
import {ZodUUID} from "./uuid";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodSeries = ZodGenericDataSoftDelete.extend({
/**
* Name of the media (this represent the title)
*/
name: zod.string(),
/**
* Description of the media
*/
description: zod.string().optional(),
/**
* series parent ID
*/
parentId: ZodLong,
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID),
});
export type Series = zod.infer<typeof ZodSeries>;
export function isSeries(data: any): data is Series {
try {
ZodSeries.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodSeries' error=${e}`);
return false;
}
}
export const ZodSeriesWrite = ZodSeries.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type SeriesWrite = zod.infer<typeof ZodSeriesWrite>;
export function isSeriesWrite(data: any): data is SeriesWrite {
try {
ZodSeriesWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodSeriesWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodTimestamp = zod.string().datetime({ precision: 3 });
export type Timestamp = zod.infer<typeof ZodTimestamp>;

View File

@ -0,0 +1,55 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodUUID} from "./uuid";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodType = ZodGenericDataSoftDelete.extend({
/**
* Name of the media (this represent the title)
*/
name: zod.string(),
/**
* Description of the media
*/
description: zod.string().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID),
});
export type Type = zod.infer<typeof ZodType>;
export function isType(data: any): data is Type {
try {
ZodType.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodType' error=${e}`);
return false;
}
}
export const ZodTypeWrite = ZodType.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type TypeWrite = zod.infer<typeof ZodTypeWrite>;
export function isTypeWrite(data: any): data is TypeWrite {
try {
ZodTypeWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodTypeWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,42 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodUser} from "./user";
export const ZodUserKarideo = ZodUser.extend({
});
export type UserKarideo = zod.infer<typeof ZodUserKarideo>;
export function isUserKarideo(data: any): data is UserKarideo {
try {
ZodUserKarideo.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserKarideo' error=${e}`);
return false;
}
}
export const ZodUserKarideoWrite = ZodUserKarideo.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type UserKarideoWrite = zod.infer<typeof ZodUserKarideoWrite>;
export function isUserKarideoWrite(data: any): data is UserKarideoWrite {
try {
ZodUserKarideoWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserKarideoWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,65 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
import {ZodFloat} from "./float";
import {ZodInteger} from "./integer";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodUserMediaAdvancement = ZodGenericDataSoftDelete.extend({
/**
* Foreign Key Id of the user
*/
userId: ZodLong,
/**
* Id of the media
*/
mediaId: ZodLong,
/**
* Percent of advancement in the media
*/
percent: ZodFloat,
/**
* Number of second of advancement in the media
*/
time: ZodInteger,
/**
* Number of time this media has been read
*/
count: ZodInteger,
});
export type UserMediaAdvancement = zod.infer<typeof ZodUserMediaAdvancement>;
export function isUserMediaAdvancement(data: any): data is UserMediaAdvancement {
try {
ZodUserMediaAdvancement.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserMediaAdvancement' error=${e}`);
return false;
}
}
export const ZodUserMediaAdvancementWrite = ZodUserMediaAdvancement.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type UserMediaAdvancementWrite = zod.infer<typeof ZodUserMediaAdvancementWrite>;
export function isUserMediaAdvancementWrite(data: any): data is UserMediaAdvancementWrite {
try {
ZodUserMediaAdvancementWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserMediaAdvancementWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,38 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
export const ZodUserOut = zod.object({
id: ZodLong,
login: zod.string().max(255).optional(),
});
export type UserOut = zod.infer<typeof ZodUserOut>;
export function isUserOut(data: any): data is UserOut {
try {
ZodUserOut.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserOut' error=${e}`);
return false;
}
}
export const ZodUserOutWrite = ZodUserOut.partial();
export type UserOutWrite = zod.infer<typeof ZodUserOutWrite>;
export function isUserOutWrite(data: any): data is UserOutWrite {
try {
ZodUserOutWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserOutWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,53 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodTimestamp} from "./timestamp";
import {ZodUUID} from "./uuid";
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
export const ZodUser = ZodGenericDataSoftDelete.extend({
login: zod.string().max(128).optional(),
lastConnection: ZodTimestamp.optional(),
admin: zod.boolean(),
blocked: zod.boolean(),
removed: zod.boolean(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID).optional(),
});
export type User = zod.infer<typeof ZodUser>;
export function isUser(data: any): data is User {
try {
ZodUser.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUser' error=${e}`);
return false;
}
}
export const ZodUserWrite = ZodUser.omit({
deleted: true,
id: true,
createdAt: true,
updatedAt: true,
}).partial();
export type UserWrite = zod.infer<typeof ZodUserWrite>;
export function isUserWrite(data: any): data is UserWrite {
try {
ZodUserWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodUserWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,8 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodUUID = zod.string().uuid();
export type UUID = zod.infer<typeof ZodUUID>;

View File

@ -0,0 +1,445 @@
/** @file
* @author Edouard DUPIN
* @copyright 2024, Edouard DUPIN, all right reserved
* @license MPL-2
*/
import { RestErrorResponse, isRestErrorResponse } from "./model";
export enum HTTPRequestModel {
DELETE = "DELETE",
GET = "GET",
PATCH = "PATCH",
POST = "POST",
PUT = "PUT",
}
export enum HTTPMimeType {
ALL = "*/*",
CSV = "text/csv",
IMAGE = "image/*",
IMAGE_JPEG = "image/jpeg",
IMAGE_PNG = "image/png",
JSON = "application/json",
MULTIPART = "multipart/form-data",
OCTET_STREAM = "application/octet-stream",
TEXT_PLAIN = "text/plain",
}
export interface RESTConfig {
// base of the server: http(s)://my.server.org/plop/api/
server: string;
// Token to access of the data.
token?: string;
}
export interface RESTModel {
// base of the local API request: "sheep/{id}".
endPoint: string;
// Type of the request.
requestType?: HTTPRequestModel;
// Input type requested.
accept?: HTTPMimeType;
// Content of the local data.
contentType?: HTTPMimeType;
// Mode of the TOKEN in URL or Header (?token:${tokenInUrl})
tokenInUrl?: boolean;
}
export interface ModelResponseHttp {
status: number;
data: any;
}
function isNullOrUndefined(data: any): data is undefined | null {
return data === undefined || data === null;
}
// 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;
callback?: RESTCallbacks;
}
function replaceAll(input, searchValue, replaceValue) {
return input.split(searchValue).join(replaceValue);
}
function removeTrailingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "undefined";
}
return input.replace(/\/+$/, "");
}
function removeLeadingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "";
}
return input.replace(/^\/+/, "");
}
export function RESTUrl({
restModel,
restConfig,
params,
queries,
}: RESTRequestType): string {
// Create the URL PATH:
let generateUrl = `${removeTrailingSlashes(
restConfig.server
)}/${removeLeadingSlashes(restModel.endPoint)}`;
if (params !== undefined) {
for (let key of Object.keys(params)) {
generateUrl = replaceAll(generateUrl, `{${key}}`, `${params[key]}`);
}
}
if (
queries === undefined &&
(restConfig.token === undefined || restModel.tokenInUrl !== true)
) {
return generateUrl;
}
const searchParams = new URLSearchParams();
if (queries !== undefined) {
for (let key of Object.keys(queries)) {
const value = queries[key];
if (Array.isArray(value)) {
for (const element of value) {
searchParams.append(`${key}`, `${element}`);
}
} else {
searchParams.append(`${key}`, `${value}`);
}
}
}
if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
searchParams.append("Authorization", `Bearer ${restConfig.token}`);
}
return generateUrl + "?" + searchParams.toString();
}
export function fetchProgress(
generateUrl: string,
{
method,
headers,
body,
}: {
method: HTTPRequestModel;
headers: any;
body: any;
},
{ progressUpload, progressDownload, abortHandle }: RESTCallbacks
): Promise<Response> {
const xhr: {
io?: XMLHttpRequest;
} = {
io: new XMLHttpRequest(),
};
return new Promise((resolve, reject) => {
// Stream the upload progress
if (progressUpload) {
xhr.io?.upload.addEventListener("progress", (dataEvent) => {
if (dataEvent.lengthComputable) {
progressUpload(dataEvent.loaded, dataEvent.total);
}
});
}
// Stream the download progress
if (progressDownload) {
xhr.io?.addEventListener("progress", (dataEvent) => {
if (dataEvent.lengthComputable) {
progressDownload(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"));
});
// Capture the end of the stream
xhr.io?.addEventListener("loadend", () => {
if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
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,
});
const headersArray = replaceAll(
xhr.io.getAllResponseHeaders().trim(),
"\r\n",
"\n"
).split("\n");
headersArray.forEach(function (header) {
const firstColonIndex = header.indexOf(":");
if (firstColonIndex !== -1) {
const key = header.substring(0, firstColonIndex).trim();
const 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.io?.setRequestHeader(key, value as string);
}
}
xhr.io?.send(body);
});
}
export function RESTRequest({
restModel,
restConfig,
data,
params,
queries,
callback,
}: RESTRequestType): Promise<ModelResponseHttp> {
// Create the URL PATH:
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
let headers: any = {};
if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
headers["Authorization"] = `Bearer ${restConfig.token}`;
}
if (restModel.accept !== undefined) {
headers["Accept"] = restModel.accept;
}
if (restModel.requestType !== HTTPRequestModel.GET) {
// if Get we have not a content type, the body is empty
if (restModel.contentType !== HTTPMimeType.MULTIPART) {
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
headers["Content-Type"] = restModel.contentType;
}
}
let body = data;
if (restModel.contentType === HTTPMimeType.JSON) {
body = JSON.stringify(data);
} else if (restModel.contentType === HTTPMimeType.MULTIPART) {
const formData = new FormData();
for (const name in data) {
formData.append(name, data[name]);
}
body = formData;
}
return new Promise((resolve, reject) => {
let action: undefined | Promise<Response> = 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 ?? HTTPRequestModel.GET,
headers,
body,
},
callback
);
}
action
.then((response: Response) => {
if (response.status >= 200 && response.status <= 299) {
const contentType = response.headers.get("Content-Type");
if (
!isNullOrUndefined(restModel.accept) &&
restModel.accept !== contentType
) {
reject({
name: "Model accept type incompatible",
time: Date().toString(),
status: 901,
message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
statusMessage: "Fetch error",
error: "rest-tools.ts Wrong type in the message return type",
} as RestErrorResponse);
} else if (contentType === HTTPMimeType.JSON) {
response
.json()
.then((value: any) => {
resolve({ status: response.status, data: value });
})
.catch((reason: Error) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: 902,
message: `REST parse json fail: ${reason}`,
statusMessage: "Fetch parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
});
} else {
resolve({ status: response.status, data: response.body });
}
} else {
// the answer is not correct not a 2XX
// clone the response to keep the raw data if case of error:
response
.clone()
.json()
.then((value: any) => {
if (isRestErrorResponse(value)) {
reject(value);
} else {
response
.text()
.then((dataError: string) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: 903,
message: `REST parse error json with wrong type fail. ${dataError}`,
statusMessage: "Fetch parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch ERROR parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
});
}
})
.catch((reason: Error) => {
response
.text()
.then((dataError: string) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ${dataError} with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT FAIL",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
});
});
}
})
.catch((error: Error) => {
if (isRestErrorResponse(error)) {
reject(error);
} else {
reject({
name: "Request fail",
time: Date(),
status: 999,
message: error,
statusMessage: "Fetch catch error",
error: "rest-tools.ts detect an error in the fetch request",
});
}
});
});
}
export function RESTRequestJson<TYPE>(
request: RESTRequestType,
checker?: (data: any) => data is TYPE
): Promise<TYPE> {
return new Promise((resolve, reject) => {
RESTRequest(request)
.then((value: ModelResponseHttp) => {
if (isNullOrUndefined(checker)) {
console.log(`Have no check of MODEL in API: ${RESTUrl(request)}`);
resolve(value.data);
} else if (checker === undefined || checker(value.data)) {
resolve(value.data);
} else {
reject({
name: "Model check fail",
time: Date().toString(),
status: 950,
error: "REST Fail to verify the data",
statusMessage: "API cast ERROR",
message: "api.ts Check type as fail",
} as RestErrorResponse);
}
})
.catch((reason: RestErrorResponse) => {
reject(reason);
});
});
}
export function RESTRequestVoid(request: RESTRequestType): Promise<void> {
return new Promise((resolve, reject) => {
RESTRequest(request)
.then((value: ModelResponseHttp) => {
resolve();
})
.catch((reason: RestErrorResponse) => {
reject(reason);
});
});
}

View File

@ -4,6 +4,7 @@
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { UUID } from 'app/back-api';
//import { ModelResponseHttp } from '@app/service/http-wrapper';
import { DataService } from 'app/service/data';
@ -14,7 +15,7 @@ import { DataService } from 'app/service/data';
})
export class ElementDataImageComponent implements OnInit {
// input parameters
@Input() id:number = -1;
@Input() id?: UUID;
cover: string = '';
/*
imageCanvas:any;
@ -29,7 +30,7 @@ export class ElementDataImageComponent implements OnInit {
}
ngOnInit() {
this.cover = this.dataService.getCoverThumbnailUrl(this.id);
this.cover = this.dataService.getThumbnailUrl(this.id);
/*
let canvas = this.imageCanvas.nativeElement;
let ctx = canvas.getContext("2d");

View File

@ -1,19 +1,26 @@
<div class="imgContainer-small">
<div *ngIf="covers">
@if(covers) {
<div>
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
<div *ngIf="!covers" class="noImage">
}
@else {
<div class="noImage">
</div>
}
</div>
<div class="season-small">
Season {{numberSeason}}
</div>
<div class="description-small" *ngIf="count > 1">
{{count}} Episodes
@if(count > 0) {
<div class="description-small">
{{count}} épisode{{count > 1?"s":""}}
</div>
<div class="description-small" *ngIf="count == 1">
{{count}} Episode
}
@else {
<div class="description-small">
Aucun épisode
</div>
}

View File

@ -4,10 +4,10 @@
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { Season } from 'app/back-api';
import { SeasonService, DataService } from 'app/service';
import { NodeData } from 'common/model';
import { isNullOrUndefined } from 'common/utils';
@Component({
selector: 'app-element-season',
@ -16,7 +16,7 @@ import { isNullOrUndefined } from 'common/utils';
})
export class ElementSeasonComponent implements OnInit {
// input parameters
@Input() element:NodeData;
@Input() element: Season;
numberSeason: string;
count: number;
@ -24,8 +24,9 @@ export class ElementSeasonComponent implements OnInit {
description: string;
constructor(
private dataService: DataService,
private seasonService: SeasonService,
private dataService: DataService) {
) {
}
ngOnInit() {
@ -36,7 +37,7 @@ export class ElementSeasonComponent implements OnInit {
}
this.numberSeason = this.element.name;
this.description = this.element.description;
this.covers = this.dataService.getCoverListThumbnailUrl(this.element.covers);
this.covers = this.dataService.getListThumbnailUrl(this.element.covers);
let self = this;
this.seasonService.countVideo(this.element.id)
.then((response) => {

View File

@ -1,24 +1,24 @@
<div>
<div class="count-base">
<div class="count" *ngIf="countvideo">
{{countvideo}}
@if(countVideo) {
<div class="count">
{{countVideo}}
</div>
}
</div>
<div class="imgContainer-small">
<div *ngIf="covers">
@if(covers) {
<div>
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
<div *ngIf="!covers" class="noImage">
}
@else {
<div class="noImage">
</div>
}
</div>
<div class="title-small">
{{name}}
</div>
<!--
<div class="description-small" *ngIf="description">
{{description}}
</div>
-->
</div>

View File

@ -4,8 +4,8 @@
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { NodeData } from 'common/model';
import { isNullOrUndefined } from 'common/utils';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { Series } from 'app/back-api';
import { SeriesService, DataService } from 'app/service';
@ -16,17 +16,18 @@ import { SeriesService, DataService } from 'app/service';
})
export class ElementSeriesComponent implements OnInit {
// input parameters
@Input() element:NodeData;
@Input() element: Series;
name: string = 'plouf';
description: string = '';
countvideo:number = null;
countVideo: number = null;
covers: string[];
constructor(
private dataService: DataService,
private seriesService: SeriesService,
private dataService: DataService) {
) {
}
ngOnInit() {
@ -38,13 +39,13 @@ export class ElementSeriesComponent implements OnInit {
let self = this;
self.name = this.element.name;
self.description = this.element.description;
self.covers = self.dataService.getCoverListThumbnailUrl(this.element.covers);
self.covers = self.dataService.getListThumbnailUrl(this.element.covers);
this.seriesService.countVideo(this.element.id)
.then((response) => {
self.countvideo = response;
self.countVideo = response;
}).catch((response) => {
self.countvideo = 0;
self.countVideo = 0;
});
}
}

View File

@ -1,18 +1,24 @@
<div>
<div class="count-base">
<span class="count" *ngIf="countvideo">
{{countvideo}}
@if(countVideo) {
<span class="count">
{{countVideo}}
</span>
}
</div>
<div class="imgContainer-small">
<div *ngIf="covers">
@if(covers) {
<div>
<img src="{{covers[0]}}" class="miniature-small"/>
</div>
}
</div>
<div class="title-small">
{{name}}
</div>
<div class="description-small" *ngIf="description">
@if(description) {
<div class="description-small">
{{description}}
</div>
}
</div>

View File

@ -4,8 +4,8 @@
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { isArrayOf, isNullOrUndefined, isNumberFinite } from 'common/utils';
import { NodeData } from 'common/model';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { Type } from 'app/back-api';
import { TypeService, DataService } from 'app/service';
@ -16,39 +16,39 @@ import { TypeService, DataService } from 'app/service';
})
export class ElementTypeComponent implements OnInit {
// input parameters
@Input() element:NodeData;
@Input() element: Type;
public name: string = 'rr';
public description: string;
public countvideo:number;
public countVideo: number;
public covers: string[];
constructor(
private dataService: DataService,
private typeService: TypeService,
private dataService: DataService) {
) {
}
ngOnInit() {
if (isNullOrUndefined(this.element)) {
this.name = 'Not a media';
this.description = undefined;
this.countvideo = undefined;
this.countVideo = undefined;
this.covers = undefined;
return;
}
let self = this;
console.log(" ??? Get element ! " + JSON.stringify(this.element));
self.name = this.element.name;
self.description = this.element.description;
self.covers = self.dataService.getCoverListThumbnailUrl(this.element.covers);
self.covers = self.dataService.getListThumbnailUrl(this.element.covers);
this.typeService.countVideo(this.element.id)
.then((response: number) => {
self.countvideo = response;
self.countVideo = response;
}).catch(() => {
self.countvideo = 0;
self.countVideo = 0;
});
}
}

View File

@ -1,28 +1,31 @@
<div>
<div class="count-base">
<span class="views" *ngIf="advancement">
@if(advancement) {
<span class="views">
{{advancement.count}}
</span>
}
</div>
<div class="videoImgContainer">
<div *ngIf="covers">
@if(covers) {
<div>
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}" />
</div>
<div *ngIf="!covers" class="noImage">
}
@else {
<div class="noImage">
</div>
}
</div>
<div class="view-progess" [ngStyle]="updateAdvancement()"></div>
<div class="title-small" *ngIf="data">
<div class="title-small">
@if(data) {
{{episodeDisplay}} {{name}}
</div>
<div class="title-small" *ngIf="!data">
}
@else {
Error media: {{element?.id}}
}
</div>
<!--
<div class="description-small" *ngIf="description">
{{description}}
</div>
-->
</div>

View File

@ -4,12 +4,10 @@
* @license PROPRIETARY (see license file)
*/
import { Injectable, Component, OnInit, Input } from '@angular/core';
import { isMedia, Media } from 'app/model';
import { UserMediaAdvancement } from 'app/model/user-media-advancement';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { Media, UserMediaAdvancement } from 'app/back-api';
import { AdvancementService, DataService } from 'app/service';
import { NodeData } from 'common/model';
import { isNullOrUndefined } from 'common/utils';
@Component({
selector: 'app-element-video',
@ -20,7 +18,7 @@ import { isNullOrUndefined } from 'common/utils';
@Injectable()
export class ElementVideoComponent implements OnInit {
// input parameters
@Input() element: NodeData;
@Input() element: Media;
data: Media;
name: string = '';
@ -38,12 +36,6 @@ export class ElementVideoComponent implements OnInit {
// nothing to do.
}
ngOnInit() {
if (!isMedia(this.element)) {
this.data = undefined;
this.name = 'Not a media';
this.description = undefined;
return;
}
this.data = this.element;
this.name = this.element.name;
this.description = this.element.description;
@ -52,7 +44,7 @@ export class ElementVideoComponent implements OnInit {
} else {
this.episodeDisplay = `${this.element.episode} - `;
}
this.covers = this.dataService.getCoverListThumbnailUrl(this.element.covers);
this.covers = this.dataService.getListThumbnailUrl(this.element.covers);
this.advancementService.get(this.element.id)
.then((response: UserMediaAdvancement) => {

View File

@ -1,5 +0,0 @@
import { Media, isMedia } from "./media";
export {
Media, isMedia,
}

View File

@ -1,47 +0,0 @@
import { isNumberFinite, isString, isOptionalOf } from "common/utils";
import { isNodeData, NodeData } from "common/model/node";
export interface Media extends NodeData {
dataId?: number;
typeId?: number;
seriesId?: number;
seasonId?: number;
episode?: number;
date?: number;
time?: number;
ageLimit?: number;
};
export function isMedia(data: any): data is Media {
if (!isNodeData(data) as any) {
return false;
}
if (!isOptionalOf(data.dataId, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.typeId, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.seriesId, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.seasonId, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.episode, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.date, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.time, isNumberFinite)) {
return false;
}
if (!isOptionalOf(data.ageLimit, isNumberFinite)) {
return false;
}
return true;
}

View File

@ -1,37 +0,0 @@
import { isNumberFinite, isString, isOptionalOf, isNumber } from "common/utils";
import { isNodeData, NodeData } from "common/model/node";
export interface UserMediaAdvancement {
id: number;
// Id of the media
mediaId?: number;
// Percent of advancement in the media
percent?: number;
// "Number of second of advancement in the media
time?: number;
// Number of time this media has been read
count?: number;
};
export function isUserMediaAdvancement(data: any): data is UserMediaAdvancement {
if (!isNumberFinite(data.id)) {
return false;
}
if (!isNumberFinite(data.mediaId)) {
return false;
}
if (!isNumber(data.percent)) {
return false;
}
if (!isNumberFinite(data.time)) {
return false;
}
if (!isNumberFinite(data.count)) {
return false;
}
return true;
}

View File

@ -4,8 +4,7 @@
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit } from '@angular/core';
import { PopInService } from 'common/service/popin';
import { PopInService } from '@kangaroo-and-rabbit/kar-cw';
@Component({
selector: 'create-type',

View File

@ -3,9 +3,11 @@
Karideo
</div>
<div class="fill-all colomn_mutiple">
<div *ngFor="let data of dataList" class="item-home" (click)="onSelectType($event, data.id)" (auxclick)="onSelectType($event, data.id)">
@for (data of dataList; track data.id;) {
<div class="item-home" (click)="onSelectType($event, data.id)" (auxclick)="onSelectType($event, data.id)">
<app-element-type [element]="data"></app-element-type>
</div>
}
<div class="clear"></div>
</div>
</div>

View File

@ -24,14 +24,12 @@ export class HomeScene implements OnInit {
ngOnInit() {
let self = this;
this.typeService.getData()
this.typeService.gets()
.then((response) => {
self.error = '';
self.dataList = response;
console.log(`Get response: ${ JSON.stringify(response, null, 2)}`);
}).catch((response) => {
self.error = 'Wrong e-mail/login or password';
console.log(`[E] ${ self.constructor.name }: Does not get a correct response from the server ...`);
self.dataList = [];
});
this.arianeService.reset();

View File

@ -2,21 +2,26 @@
<div class="title">
Edit season
</div>
<div class="fill-all" *ngIf="itemIsRemoved">
@if(itemIsRemoved) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The season has been removed
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsNotFound">
}
@else if (itemIsNotFound){
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The season does not exist
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsLoading">
}
@else if (itemIsLoading){
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
Loading ...<br/>
@ -24,7 +29,9 @@
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
}
@else {
<div class="fill-all">
<div class="request_raw">
<div class="label">
Number:
@ -54,10 +61,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- Cover section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Covers
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="hide-element">
<input type="file"
#fileInput
@ -67,16 +74,18 @@
</div>
<div class="request_raw">
<div class="input">
<div class="cover" *ngFor="let element of coversDisplay">
@for (data of coversDisplay; track data.id;) {
<div class="cover" >
<div class="cover-image">
<img src="{{element.url}}"/>
<img src="{{data.url}}"/>
</div>
<div class="cover-button">
<button (click)="removeCover(element.id)">
<button (click)="removeCover(data.id)">
<i class="material-icons button-remove">highlight_off</i>
</button>
</div>
</div>
}
<div class="cover">
<div class="cover-no-image">
</div>
@ -91,10 +100,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- ADMIN section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Administration
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="request_raw">
<div class="label">
<i class="material-icons">data_usage</i> ID:
@ -117,23 +126,29 @@
<div class="label">
<i class="material-icons">delete_forever</i> Trash:
</div>
<div class="input" *ngIf="(videoCount == '0')">
@if(videoCount == '0') {
<div class="input">
<button class="button color-button-cancel color-shadow-black" (click)="removeItem()" type="submit">
<i class="material-icons">delete</i> Remove season
</button>
</div>
<div class="input" *ngIf="(videoCount != '0')">
}
@else {
<div class="input">
<i class="material-icons">new_releases</i> Can not remove season, video depending on it
</div>
}
</div>
<div class="clear"></div>
</div>
}
</div>
<upload-progress [mediaTitle]="upload.labelMediaTitle"
[mediaUploaded]="upload.mediaSendSize"
[mediaSize]="upload.mediaSize"
[result]="upload.result"
[error]="upload.error"></upload-progress>
[error]="upload.error"
(abort)="abortUpload()"></upload-progress>
<delete-confirm
[comment]="confirmDeleteComment"
[imageUrl]=confirmDeleteImageUrl

View File

@ -5,13 +5,11 @@
*/
import { Component, OnInit } from '@angular/core';
import { PopInService, UploadProgress, isNumberFinite } from '@kangaroo-and-rabbit/kar-cw';
import { Season, UUID } from 'app/back-api';
import { RESTAbort } from 'app/back-api/rest-tools';
import { SeasonService, ArianeService, DataService } from 'app/service';
import { NodeData } from 'common/model';
import { UploadProgress } from 'common/popin/upload-progress/upload-progress';
import { PopInService } from 'common/service';
import { isNumberFinite } from 'common/utils';
export interface ElementList {
value: number;
@ -32,11 +30,11 @@ export class SeasonEditScene implements OnInit {
error: string = '';
numberVal: number = null;
numberVal: string = null;
description: string = '';
coverFile: File;
coverFile?: File;
uploadFileValue: string = '';
selectedFiles: FileList;
selectedFiles?: FileList;
videoCount: string = null;
@ -46,8 +44,9 @@ export class SeasonEditScene implements OnInit {
// --------------- confirm section ------------------
public confirmDeleteComment: string = null;
public confirmDeleteImageUrl: string = null;
private deleteCoverId: number = null;
private deleteCoverId: UUID = null;
private deleteItemId: number = null;
cancelHandle: RESTAbort = {};
deleteConfirmed() {
if (this.deleteCoverId !== null) {
this.removeCoverAfterConfirm(this.deleteCoverId);
@ -67,10 +66,11 @@ export class SeasonEditScene implements OnInit {
constructor(
private dataService: DataService,
private seasonService: SeasonService,
private arianeService: ArianeService,
private popInService: PopInService,
private dataService: DataService) {
) {
}
@ -78,7 +78,7 @@ export class SeasonEditScene implements OnInit {
this.idSeason = this.arianeService.getSeasonId();
let self = this;
this.seasonService.get(this.idSeason)
.then((response: NodeData) => {
.then((response: Season) => {
console.log(`get response of season : ${JSON.stringify(response, null, 2)}`);
if (isNumberFinite(response.name)) {
self.numberVal = response.name;
@ -108,7 +108,7 @@ export class SeasonEditScene implements OnInit {
for (let iii = 0; iii < covers.length; iii++) {
this.coversDisplay.push({
id: covers[iii],
url: this.dataService.getCoverThumbnailUrl(covers[iii])
url: this.dataService.getThumbnailUrl(covers[iii])
});
}
} else {
@ -126,7 +126,7 @@ export class SeasonEditScene implements OnInit {
sendValues(): void {
console.log('send new values....');
let data = {
name: this.numberVal,
name: `${this.numberVal}`,
description: this.description
};
if (this.description === undefined) {
@ -149,6 +149,10 @@ export class SeasonEditScene implements OnInit {
event.preventDefault();
}
abortUpload(): void {
this.cancelHandle.abort();
}
// At the file input element
// (change)="selectFile($event)"
onChangeCover(value: any): void {
@ -168,10 +172,10 @@ export class SeasonEditScene implements OnInit {
this.upload.clear();
// display the upload pop-in
this.popInService.open('popin-upload-progress');
this.seasonService.uploadCover(file, this.idSeason, (count, total) => {
this.seasonService.uploadCover(this.idSeason, file, (count, total) => {
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 ...
@ -182,14 +186,14 @@ export class SeasonEditScene implements OnInit {
});
}
removeCover(id: number) {
removeCover(id: UUID) {
this.cleanConfirm();
this.confirmDeleteComment = `Delete the cover ID: ${id}`;
this.confirmDeleteImageUrl = this.dataService.getCoverThumbnailUrl(id);
this.confirmDeleteImageUrl = this.dataService.getThumbnailUrl(id);
this.deleteCoverId = id;
this.popInService.open('popin-delete-confirm');
}
removeCoverAfterConfirm(id: number) {
removeCoverAfterConfirm(id: UUID) {
console.log(`Request remove cover: ${id}`);
let self = this;
this.seasonService.deleteCover(this.idSeason, id)

View File

@ -1,29 +1,36 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="cover != null" >
@if(cover) {
<div class="cover">
<img src="{{cover}}"/>
</div>
}
</div>
<div [className]="cover != null ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div *ngIf="seriesName" class="title">
@if(seriesName) {
<div class="title">
{{seriesName}}
</div>
}
<div class="sub-title-main">
Season {{name}}
</div>
<div class="description" *ngIf="description">
@if(description) {
<div class="description">
{{description}}
</div>
}
</div>
</div>
<div class="fill-content colomn_mutiple">
<div class="clear"></div>
<div class="title" *ngIf="videos.length > 1">Videos:</div>
<div class="title" *ngIf="videos.length == 1">Video:</div>
<div *ngFor="let data of videos" class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
<div class="title">Video{{videos.length > 1?"s":""}}:</div>
@for (data of videos; track data.id;) {
<div class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
<app-element-video [element]="data"></app-element-video>
</div>
}
<div class="clear"></div>
</div>
</div>

View File

@ -24,11 +24,12 @@ export class SeasonScene implements OnInit {
videosError = '';
videos = [];
constructor(
private advancementService: AdvancementService,
private dataService: DataService,
private seasonService: SeasonService,
private seriesService: SeriesService,
private arianeService: ArianeService,
private dataService: DataService,
private advancementService: AdvancementService) {
) {
}
@ -46,9 +47,9 @@ export class SeasonScene implements OnInit {
self.cover = null;
self.covers = [];
} else {
self.cover = self.dataService.getCoverUrl(response.covers[0]);
self.cover = self.dataService.getThumbnailUrl(response.covers[0]);
for (let iii = 0; iii < response.covers.length; iii++) {
self.covers.push(self.dataService.getCoverUrl(response.covers[iii]));
self.covers.push(self.dataService.getThumbnailUrl(response.covers[iii]));
}
}
self.seriesService.get(self.seriesId)

View File

@ -2,21 +2,26 @@
<div class="title">
Edit series
</div>
<div class="fill-all" *ngIf="itemIsRemoved">
@if(itemIsRemoved) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The series has been removed
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsNotFound">
}
@else if(itemIsNotFound) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The series does not exist
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsLoading">
}
@else if(itemIsLoading) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
Loading ...<br/>
@ -24,7 +29,9 @@
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
}
@else {
<div class="fill-all">
<div class="request_raw">
<div class="label">
Type:
@ -32,7 +39,9 @@
<div class="input">
<select [ngModel]="typeId"
(ngModelChange)="onChangeType($event)">
<option *ngFor="let element of listType" [ngValue]="element.value">{{element.label}}</option>
@for (data of listType; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</div>
</div>
@ -66,10 +75,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- Cover section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Covers
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="hide-element">
<input type="file"
#fileInput
@ -79,16 +88,18 @@
</div>
<div class="request_raw">
<div class="input">
<div class="cover" *ngFor="let element of coversDisplay">
@for (data of coversDisplay; track data.id;) {
<div class="cover">
<div class="cover-image">
<img src="{{element.url}}"/>
<img src="{{data.url}}"/>
</div>
<div class="cover-button">
<button (click)="removeCover(element.id)">
<button (click)="removeCover(data.id)">
<i class="material-icons button-remove">highlight_off</i>
</button>
</div>
</div>
}
<div class="cover">
<div class="cover-no-image">
</div>
@ -103,10 +114,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- ADMIN section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Administration
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="request_raw">
<div class="label">
<i class="material-icons">data_usage</i> ID:
@ -138,24 +149,30 @@
<div class="label">
<i class="material-icons">delete_forever</i> Trash:
</div>
<div class="input" *ngIf="(videoCount == '0' && seasonsCount == '0')">
@if(videoCount == '0' && seasonsCount == '0') {
<div class="input">
<button class="button color-button-cancel color-shadow-black" (click)="removeItem()" type="submit">
<i class="material-icons">delete</i> Remove Series
</button>
</div>
<div class="input" *ngIf="(videoCount != '0' || seasonsCount != '0')">
}
@else {
<div class="input">
<i class="material-icons">new_releases</i> Can not remove season or video depending on it
</div>
}
</div>
<div class="clear"></div>
</div>
}
</div>
<upload-progress [mediaTitle]="upload.labelMediaTitle"
[mediaUploaded]="upload.mediaSendSize"
[mediaSize]="upload.mediaSize"
[result]="upload.result"
[error]="upload.error"></upload-progress>
[error]="upload.error"
(abort)="abortUpload()"></upload-progress>
<delete-confirm
[comment]="confirmDeleteComment"
[imageUrl]=confirmDeleteImageUrl

View File

@ -5,10 +5,11 @@
*/
import { Component, OnInit } from '@angular/core';
import { PopInService, UploadProgress } from '@kangaroo-and-rabbit/kar-cw';
import { UUID } from 'app/back-api';
import { RESTAbort } from 'app/back-api/rest-tools';
import { SeriesService, DataService, TypeService, ArianeService } from 'app/service';
import { UploadProgress } from 'common/popin/upload-progress/upload-progress';
import { PopInService } from 'common/service';
export class ElementList {
constructor(
@ -53,8 +54,9 @@ export class SeriesEditScene implements OnInit {
// --------------- confirm section ------------------
public confirmDeleteComment: string = null;
public confirmDeleteImageUrl: string = null;
private deleteCoverId: number = null;
private deleteCoverId: UUID = null;
private deleteItemId: number = null;
cancelHandle: RESTAbort = {};
deleteConfirmed() {
if (this.deleteCoverId !== null) {
this.removeCoverAfterConfirm(this.deleteCoverId);
@ -73,11 +75,13 @@ export class SeriesEditScene implements OnInit {
}
constructor(private dataService: DataService,
private typeService: TypeService,
constructor(
private dataService: DataService,
private seriesService: SeriesService,
private typeService: TypeService,
private arianeService: ArianeService,
private popInService: PopInService) {
private popInService: PopInService,
) {
}
@ -86,7 +90,7 @@ export class SeriesEditScene implements OnInit {
let self = this;
this.listType = [{ value: null, label: '---' }];
this.typeService.getData()
this.typeService.gets()
.then((response2) => {
for (let iii = 0; iii < response2.length; iii++) {
self.listType.push({ value: response2[iii].id, label: response2[iii].name });
@ -127,13 +131,17 @@ export class SeriesEditScene implements OnInit {
});
}
abortUpload(): void {
this.cancelHandle.abort();
}
updateCoverList(covers: any) {
this.coversDisplay = [];
if (covers !== undefined && covers !== null) {
for (let iii = 0; iii < covers.length; iii++) {
this.coversDisplay.push({
id: covers[iii],
url: this.dataService.getCoverThumbnailUrl(covers[iii])
url: this.dataService.getThumbnailUrl(covers[iii])
});
}
} else {
@ -203,10 +211,10 @@ export class SeriesEditScene implements OnInit {
this.upload.clear();
// display the upload pop-in
this.popInService.open('popin-upload-progress');
this.seriesService.uploadCover(file, this.idSeries, (count, total) => {
this.seriesService.uploadCover(this.idSeries, file, (count, total) => {
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 ...
@ -216,14 +224,14 @@ export class SeriesEditScene implements OnInit {
console.log('Can not add the cover in the video...');
});
}
removeCover(id: number) {
removeCover(id: UUID) {
this.cleanConfirm();
this.confirmDeleteComment = `Delete the cover ID: ${id}`;
this.confirmDeleteImageUrl = this.dataService.getCoverThumbnailUrl(id);
this.confirmDeleteImageUrl = this.dataService.getThumbnailUrl(id);
this.deleteCoverId = id;
this.popInService.open('popin-delete-confirm');
}
removeCoverAfterConfirm(id: number) {
removeCoverAfterConfirm(id: UUID) {
console.log(`Request remove cover: ${id}`);
let self = this;
this.seriesService.deleteCover(this.idSeries, id)

View File

@ -1,34 +1,44 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="cover != null" >
@if(cover) {
<div class="cover" >
<img src="{{cover}}"/>
</div>
}
</div>
<div [className]="cover != null ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="title">
{{name}}
</div>
<div class="description" *ngIf="description">
@if(description) {
<div class="description">
{{description}}
</div>
}
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="seasons.length != 0">
@if(seasons.length != 0) {
<div class="fill-content colomn_mutiple">
<div class="clear"></div>
<div class="title" *ngIf="seasons.length > 1">Seasons:</div>
<div class="title" *ngIf="seasons.length == 1">Season:</div>
<div *ngFor="let data of seasons" class="item-list" (click)="onSelectSeason($event, data.id)" (auxclick)="onSelectSeason($event, data.id)">
<div class="title">Season{{seasons.length > 1?"s":""}}:</div>
@for (data of seasons; track data.id;) {
<div class="item-list" (click)="onSelectSeason($event, data.id)" (auxclick)="onSelectSeason($event, data.id)">
<app-element-season [element]="data"></app-element-season>
</div>
}
</div>
<div class="fill-content colomn_mutiple" *ngIf="videos.length != 0">
}
@if(videos.length != 0) {
<div class="fill-content colomn_mutiple">
<div class="clear"></div>
<div class="title" *ngIf="videos.length > 1">Videos:</div>
<div class="title" *ngIf="videos.length == 1">Video:</div>
<div *ngFor="let data of videos" class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
<div class="title">Video{{videos.length > 1?"s":""}}:</div>
@for (data of videos; track data.id;) {
<div class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
<app-element-video [element]="data"></app-element-video>
</div>
}
</div>
}
<div class="clear"></div>
</div>

View File

@ -5,9 +5,9 @@
*/
import { Component, OnInit } from '@angular/core';
import { Season } from 'app/back-api';
import { SeriesService, DataService, ArianeService, AdvancementService } from 'app/service';
import { NodeData } from 'common/model';
@Component({
selector: 'app-series',
@ -22,14 +22,15 @@ export class SeriesScene implements OnInit {
cover: string = '';
covers: Array<string> = [];
seasonsError: string = '';
seasons: NodeData[] = [];
seasons: Season[] = [];
videosError: string = '';
videos: Array<any> = [];
constructor(
private advancementService: AdvancementService,
private dataService: DataService,
private seriesService: SeriesService,
private arianeService: ArianeService,
private dataService: DataService,
private advancementService: AdvancementService) {
) {
}
@ -51,9 +52,9 @@ export class SeriesScene implements OnInit {
self.cover = null;
self.covers = [];
} else {
self.cover = self.dataService.getCoverUrl(response.covers[0]);
self.cover = self.dataService.getThumbnailUrl(response.covers[0]);
for (let iii = 0; iii < response.covers.length; iii++) {
self.covers.push(self.dataService.getCoverUrl(response.covers[iii]));
self.covers.push(self.dataService.getThumbnailUrl(response.covers[iii]));
}
}
updateEnded.seriesMetadata = true;
@ -63,30 +64,32 @@ export class SeriesScene implements OnInit {
self.name = '???';
self.cover = null;
self.covers = [];
// no check just ==> an error occured on season
// no check just ==> an error occurred on season
});
//console.log(`get parameter id: ${ this.idSeries}`);
console.log(`this.seriesService.getSeason(${this.idSeries})`);
this.seriesService.getSeason(this.idSeries)
.then((response: NodeData[]) => {
//console.log(`>>>> get season : ${JSON.stringify(response)}`)
.then((response: Season[]) => {
console.log(`>>>> get season : ${JSON.stringify(response)}`)
self.seasonsError = '';
self.seasons = response;
updateEnded.subSaison = true;
self.checkIfJumpIsNeeded(updateEnded);
}).catch((response) => {
console.log(`>>>> get season (FAIL) : ${JSON.stringify(response)}`)
self.seasonsError = 'Can not get the list of season in this series';
self.seasons = [];
updateEnded.subSaison = true;
self.checkIfJumpIsNeeded(updateEnded);
});
this.seriesService.getVideo(this.idSeries)
.then((response: NodeData[]) => {
//console.log(`>>>> get video : ${JSON.stringify(response)}`)
.then((response: Season[]) => {
console.log(`>>>> get video : ${JSON.stringify(response)}`)
self.videosError = '';
self.videos = response;
updateEnded.subVideo = true;
self.checkIfJumpIsNeeded(updateEnded);
}).catch((response) => {
console.log(`>>>> get video (FAIL): ${JSON.stringify(response)}`)
self.videosError = 'Can not get the List of video without season';
self.videos = [];
updateEnded.subVideo = true;

View File

@ -1,30 +1,38 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="cover != null" >
@if(cover) {
<div class="cover" >
<img src="{{cover}}"/>
</div>
}
</div>
<div [className]="cover != null ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="title">
{{name}}
</div>
<div class="description" *ngIf="description">
@if(description) {
<div class="description">
{{description}}
</div>
}
</div>
</div>
<div class="fill-content colomn_mutiple">
<div class="clear"></div>
<div *ngFor="let data of series" class="item" (click)="onSelectSeries($event, data.id)" (auxclick)="onSelectSeries($event, data.id)">
@for (data of series; track data.id;) {
<div class="item" (click)="onSelectSeries($event, data.id)" (auxclick)="onSelectSeries($event, data.id)">
<app-element-series [element]="data"></app-element-series>
</div>
}
</div>
<div class="fill-content colomn_mutiple">
<div class="clear"></div>
<div *ngFor="let data of videos" class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
@for (data of videos; track data.id;) {
<div class="item item-video" (click)="onSelectVideo($event, data.id)" (auxclick)="onSelectVideo($event, data.id)">
<app-element-video [element]="data"></app-element-video>
</div>
}
</div>
<div class="clear"></div>
</div>

View File

@ -5,9 +5,9 @@
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Series } from 'app/back-api';
import { TypeService, DataService, ArianeService, AdvancementService } from 'app/service';
import { TypeService, DataService, ArianeService, AdvancementService, SeriesService } from 'app/service';
@Component({
selector: 'app-type',
@ -26,9 +26,10 @@ export class TypeScene implements OnInit {
videosError = '';
videos = [];
constructor(
private dataService: DataService,
private typeService: TypeService,
private seriesService: SeriesService,
private arianeService: ArianeService,
private dateService: DataService,
private advancementService: AdvancementService) {
/*
@ -44,19 +45,19 @@ export class TypeScene implements OnInit {
ngOnInit() {
this.typeId = this.arianeService.getTypeId();
let self = this;
console.log(`get type global id: ${this.typeId}`);
//console.log(`get type global id: ${this.typeId}`);
this.typeService.get(this.typeId)
.then((response) => {
self.name = response.name;
self.description = response.description;
console.log(` ==> get answer type detail: ${JSON.stringify(response)}`);
//console.log(` ==> get answer type detail: ${JSON.stringify(response)}`);
if (response.covers === undefined || response.covers === null || response.covers.length === 0) {
self.cover = null;
self.covers = [];
} else {
self.cover = self.dateService.getCoverUrl(response.covers[0]);
self.cover = self.dataService.getThumbnailUrl(response.covers[0]);
for (let iii = 0; iii < response.covers.length; iii++) {
self.covers.push(self.dateService.getCoverUrl(response.covers[iii]));
self.covers.push(self.dataService.getThumbnailUrl(response.covers[iii]));
}
}
}).catch((response) => {
@ -65,23 +66,23 @@ export class TypeScene implements OnInit {
self.covers = [];
self.cover = null;
});
this.typeService.getSubSeries(this.typeId)
.then((response) => {
console.log(` ==> get answer sub-series: ${JSON.stringify(response)}`);
this.seriesService.getSeriesWithType(this.typeId)
.then((response: Series[]) => {
//console.log(` ==> get answer sub-series: ${JSON.stringify(response)}`);
self.seriessError = '';
self.series = response;
}).catch((response) => {
console.log(` ==> get answer sub-series (ERROR): ${JSON.stringify(response)}`);
//console.log(` ==> get answer sub-series (ERROR): ${JSON.stringify(response)}`);
self.seriessError = 'Wrong e-mail/login or password';
self.series = [];
});
this.typeService.getSubVideo(this.typeId)
.then((response) => {
console.log(` ==> get answer sub-video: ${JSON.stringify(response)}`);
//console.log(` ==> get answer sub-video: ${JSON.stringify(response)}`);
self.videosError = '';
self.videos = response;
}).catch((response) => {
console.log(` ==> get answer sub-video (ERROR): ${JSON.stringify(response)}`);
//console.log(` ==> get answer sub-video (ERROR): ${JSON.stringify(response)}`);
self.videosError = 'Wrong e-mail/login or password';
self.videos = [];
});

View File

@ -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<string> = ['png', 'jpg', 'bmp'];
@Output() private filesChangeEmiter: EventEmitter<File[]> = new EventEmitter();
//@Output() private filesInvalidEmiter : EventEmitter<File[]> = 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<File> = files;
this.filesChangeEmiter.emit(valid_files);
}
}

View File

@ -3,41 +3,26 @@
Upload Media
</div>
<div class="clear"><br/></div>
<div class="request_raw_table">
<table>
<colgroup>
<col style="width:10%">
<col style="width:80%">
</colgroup>
<tbody>
<tr>
<td class="left-colomn">format:</td>
<td class="right-colomn">
<div class="request_raw_table drop-area" fileDragDrop
(filesChangeEmiter)="onChangeFile($event)">
<div class="clear"><br/></div>
<div class="centered">
<input type="file" name="file" id="file" (change)="onChangeFile($event.target.files)" multiple>
<label for="file"><span class="textLink"><span class="material-icons">cloud_upload</span> Select your file</span> or <i>Drop it here!</i></label>
</div>
<div class="clear"><br/></div>
<div class="centered">
The format of the media permit to automatic find meta-data:<br/>
Univers:Series name-sXX-eXX-my name of my media.mkv<br/>
<b>example:</b> Stargate:SG1-s55-e22-Asgard.mkv <br/>
</td>
</tr>
<tr>
<td class="left-colomn">Media:</td>
<td class="right-colomn">
<input type="file"
(change)="onChangeFile($event.target)"
placeholder="Select a media file"
accept=".mkv,.webm"
width="90%"
multiple/>
</td>
</tr>
</tbody>
</table>
</div>
<div *ngIf="this.parsedElement.length !== 0" class="title">
</div>
@if(parsedElement.length !== 0) {
<div class="title">
Meta-data:
</div>
<div class="clear"><br/></div>
<div *ngIf="this.parsedElement.length !== 0" class="fill-all">
<div class="fill-all">
<div class="request_raw_table">
<table>
<colgroup>
@ -52,7 +37,9 @@
<select [ngModel]="typeId"
(ngModelChange)="onChangeType($event)"
[class.error]="typeId === undefined">
<option *ngFor="let element of listType" [ngValue]="element.value">{{element.label}}</option>
@for (data of listType; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</td>
</tr>
@ -71,7 +58,9 @@
<td class="right-colomn">
<select [ngModel]="seriesId"
(ngModelChange)="onChangeSeries($event)">
<option *ngFor="let element of listSeries" [ngValue]="element.value">{{element.label}}</option>
@for (data of listSeries; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</td>
<td class="tool-colomn">
@ -111,7 +100,8 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let data of this.parsedElement">
@for (data of parsedElement; track data.id;) {
<tr>
<td class="left-colomn">
<input type="number"
pattern="[0-9]{0-4}"
@ -128,9 +118,11 @@
(input)="onTitle(data, $event.target.value)"
[class.error]="data.title === ''"
/>
<span *ngIf="data.nameDetected === true" class="error">
@if(data.nameDetected === true) {
<span class="error">
^^^This title already exist !!!
</span>
}
</td>
<td class="tool-colomn" >
<button class="button color-button-cancel color-shadow-black"
@ -141,6 +133,7 @@
</button>
</td>
</tr>
}
</tbody>
</table>
</div>
@ -154,7 +147,8 @@
</button>
</div>
<div class="clear"></div>
<div class="request_raw_table" *ngIf="this.listFileInBdd !== undefined">
@if(listFileInBdd !== undefined) {
<div class="request_raw_table">
<table>
<colgroup>
<col style="width:10%">
@ -168,15 +162,18 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let data of this.listFileInBdd">
@for (data of listFileInBdd; track data.id;) {
<tr>
<td class="left-colomn" [class.error]="data.episodeDetected === true">{{data.episode}}</td>
<td class="right-colomn" [class.error]="data.nameDetected === true">{{data.name}}</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
<div *ngIf="this.parsedElement.length !== 0" class="fill-all">
<div class="fill-all">
<div class="request_raw_table">
<table>
<colgroup>
@ -184,30 +181,27 @@
<col style="width:80%">
</colgroup>
<tbody>
<!-- no need
<tr *ngFor="let data of this.parsedElement">
<td class="left-colomn">Keep:</td>
<td class="right-colomn">
{{data.file.name}}
</td>
</tr>
-->
<tr *ngFor="let data of this.parsedFailedElement">
@for (data of parsedFailedElement; track data.file;) {
<tr>
<td class="left-colomn">Rejected:</td>
<td class="right-colomn">
{{data.file.name}}<br/> ==&gt; {{data.reason}}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
<upload-progress [mediaTitle]="upload.labelMediaTitle"
<upload-progress
[mediaTitle]="upload.labelMediaTitle"
[mediaUploaded]="upload.mediaSendSize"
[mediaSize]="upload.mediaSize"
[result]="upload.result"
[error]="upload.error"></upload-progress>
[error]="upload.error"
(abort)="abortUpload()"></upload-progress>
<!--
TODO: add a pop-in with:

View File

@ -1,5 +1,3 @@
.title {
//background-color: green;
font-size: 45px;
@ -14,30 +12,68 @@
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
}
.drop-area {
height: 230px;
display: table;
width: 100%;
background-color: #eee;
border: dotted 1px #aaa;
cursor: pointer;
}
.text-wrapper {
display: table-cell;
vertical-align: middle;
}
.centered {
font-family: sans-serif;
font-size: 1.3em;
text-align: center;
}
.textLink {
background-color: #217500;
color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
input[type="file"] {
display: none;
}
.request_raw_table {
display: block;
width: 90%;
margin: 0 auto;
table {
width: 100%;
th {
text-align: left;
}
.left-colomn {
text-align: right;
font-weight: bold;
input {
width: 95%;
font-size: 20px;
border: 0px;
}
}
.right-colomn {
input {
width: 95%;
font-size: 20px;
border: 0px;
}
select {
width: 95%;
font-size: 20px;
@ -45,11 +81,13 @@
}
}
}
.error {
border-color: rgba(200, 0, 0, 1.0);
background-color: rgba(256, 220, 220, 1.0);
}
}
.send_value {
width: 300px;
margin: 0 auto;

View File

@ -5,11 +5,11 @@
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PopInService, UploadProgress } from '@kangaroo-and-rabbit/kar-cw';
import { Series } from 'app/back-api';
import { RESTAbort } from 'app/back-api/rest-tools';
import { TypeService, SeriesService, VideoService, ArianeService, SeasonService } from 'app/service';
import { UploadProgress } from 'common/popin/upload-progress/upload-progress';
import { PopInService } from 'common/service';
import { TypeService, SeriesService, MediaService, SeasonService } from 'app/service';
export class ElementList {
constructor(
@ -23,6 +23,7 @@ export class FileParsedElement {
public nameDetected: boolean = false;
public episodeDetected: boolean = false;
constructor(
public id: number,
public file: File,
public series: string,
public season: number,
@ -52,10 +53,10 @@ export class UploadScene implements OnInit {
selectedFiles: FileList;
typeId: number = null;
seriesId: number = null;
saisonId: number = null;
seasonId: number = null;
needSend: boolean = false;
// list of all files already registered in the bdd to compare with the curent list of files.
// list of all files already registered in the bdd to compare with the current list of files.
listFileInBdd: any = null;
// section tha define the upload value to display in the pop-in of upload
@ -78,7 +79,7 @@ export class UploadScene implements OnInit {
*/
config = {
displayKey: 'description', // if objects array passed which key to be displayed defaults to description
search: true, // true/false for the search functionlity defaults to false,
search: true, // true/false for the search functionality defaults to false,
height: 'auto', // height of the list so that if there are more no of items it can show a scroll defaults to auto. With auto height scroll will never appear
placeholder: 'Select', // text to be displayed when no item is selected defaults to Select,
customComparator: () => { }, // a custom function using which user wants to sort the items. default is undefined and Array.sort() will be used in that case,
@ -93,11 +94,15 @@ export class UploadScene implements OnInit {
];
globalSeries: string = '';
globalSeason: number = null;
constructor(private typeService: TypeService,
private seriesService: SeriesService,
cancelHandle: RESTAbort = {};
dataUniqueId: number = 0;
constructor(
private MediaService: MediaService,
private seasonService: SeasonService,
private videoService: VideoService,
private popInService: PopInService) {
private seriesService: SeriesService,
private typeService: TypeService,
private popInService: PopInService,
) {
// nothing to do.
}
@ -123,7 +128,7 @@ export class UploadScene implements OnInit {
this.listType = [{ value: null, label: '---' }];
this.listSeries = [{ value: null, label: '---' }];
this.listSeason = [{ value: null, label: '---' }];
this.typeService.getData()
this.typeService.gets()
.then((response2) => {
for (let iii = 0; iii < response2.length; iii++) {
self.listType.push({ value: response2[iii].id, label: response2[iii].name });
@ -152,8 +157,8 @@ export class UploadScene implements OnInit {
let self = this;
this.updateNeedSend();
if (this.typeId !== null) {
self.typeService.getSubSeries(this.typeId)
.then((response2) => {
self.seriesService.getSeriesWithType(this.typeId)
.then((response2: Series[]) => {
for (let iii = 0; iii < response2.length; iii++) {
self.listSeries.push({ value: response2[iii].id, label: response2[iii].name });
}
@ -238,7 +243,7 @@ export class UploadScene implements OnInit {
this.typeId = null;
this.seriesId = null;
this.saisonId = null;
this.seasonId = null;
this.listSeries = [{ value: null, label: '---' }];
this.listSeason = [{ value: null, label: '---' }];
}
@ -302,22 +307,21 @@ export class UploadScene implements OnInit {
if (isNaN(season)) {
season = null;
}
// remove extention
// remove extension
title = title.replace(new RegExp('\\.(mkv|MKV|Mkv|webm|WEBM|Webm|mp4)'), '');
let tmp = new FileParsedElement(file, series, season, episode, title);
const tmp = new FileParsedElement(this.dataUniqueId++, file, series, season, episode, title);
console.log(`==>${JSON.stringify(tmp)}`);
// add it in the list.
this.parsedElement.push(tmp);
}
// At the file input element
// (change)="selectFile($event)"
onChangeFile(value: any): void {
onChangeFile(files: File[]): void {
this.clearData();
for(let iii = 0; iii < value.files.length; iii++) {
this.addFileWithMetaData(value.files[iii]);
for (let iii = 0; iii < files.length; iii++) {
this.addFileWithMetaData(files[iii]);
}
// check if all global parameters are generic:
if (this.parsedElement.length === 0) {
@ -350,7 +354,7 @@ export class UploadScene implements OnInit {
this.updateNeedSend();
this.seriesId = null;
this.saisonId = null;
this.seasonId = null;
let self = this;
if (this.globalSeries !== '') {
this.seriesService.getLike(this.globalSeries)
@ -362,9 +366,9 @@ export class UploadScene implements OnInit {
if (response.length === 0) {
self.seriesId = null;
} else if (response.length === 1) {
let serieElem = response[0];
self.seriesId = serieElem.id;
self.updateType(serieElem.parentId);
let seriesElem = response[0];
self.seriesId = seriesElem.id;
self.updateType(seriesElem.parentId);
}
self.updateListOfVideoToCheck();
}).catch((response) => {
@ -392,10 +396,11 @@ export class UploadScene implements OnInit {
self.upload.result = 'Media creation done';
}
}, (value: string) => {
console.log("Detect error from serveur ...********************");
self.upload.error = `Error in the upload of the data...${value}`;
});
}
uploadFile(eleemnent: FileParsedElement, id: number, total: number, sendDone: any, errorOccured: any): void {
uploadFile(element: FileParsedElement, id: number, total: number, sendDone: () => void, errorOccurred: (string) => void): void {
let self = this;
self.upload.labelMediaTitle = '';
@ -414,37 +419,38 @@ export class UploadScene implements OnInit {
self.upload.labelMediaTitle = `${self.upload.labelMediaTitle}s${self.globalSeason.toString()}`;
}
// add episode ID
if(eleemnent.episode !== null && eleemnent.episode !== undefined && eleemnent.episode.toString().length !== 0) {
if (element.episode !== null && element.episode !== undefined && element.episode.toString().length !== 0) {
if (self.upload.labelMediaTitle.length !== 0) {
self.upload.labelMediaTitle = `${self.upload.labelMediaTitle}-`;
}
self.upload.labelMediaTitle = `${self.upload.labelMediaTitle }e${ eleemnent.episode.toString()}`;
self.upload.labelMediaTitle = `${self.upload.labelMediaTitle}e${element.episode.toString()}`;
}
// add title
if (self.upload.labelMediaTitle.length !== 0) {
self.upload.labelMediaTitle = `${self.upload.labelMediaTitle}-`;
}
self.upload.labelMediaTitle = `[${ id + 1 }/${ total }]${ self.upload.labelMediaTitle }${eleemnent.title}`;
self.upload.labelMediaTitle = `[${id + 1}/${total}]${self.upload.labelMediaTitle}${element.title}`;
self.videoService.uploadFile(eleemnent.file,
self.MediaService.uploadFile(element.file,
self.globalSeries,
self.seriesId,
self.globalSeason,
eleemnent.episode,
eleemnent.title,
element.episode,
element.title,
self.typeId,
(count, totalTmp) => {
// console.log("upload : " + count*100/totalTmp);
self.upload.mediaSendSize = count;
self.upload.mediaSize = totalTmp;
})
}, this.cancelHandle)
.then((response) => {
console.log(`get response of video : ${JSON.stringify(response, null, 2)}`);
sendDone();
}).catch((response) => {
})
.catch((response) => {
// self.error = "Can not get the data";
console.log('Can not add the data in the system...');
errorOccured(JSON.stringify(response, null, 2));
errorOccurred(JSON.stringify(response, null, 2));
});
}
@ -518,7 +524,7 @@ export class UploadScene implements OnInit {
return;
}
self.saisonId = null;
self.seasonId = null;
// set 1 find the ID of the season:
this.seriesService.getSeason(this.seriesId)
.then((response: any[]) => {
@ -526,14 +532,14 @@ export class UploadScene implements OnInit {
for (let iii = 0; iii < response.length; iii++) {
// console.log(" - " + JSON.stringify(response[iii]) + 'compare with : ' + JSON.stringify(self.globalSeason));
if (response[iii].name === `${self.globalSeason}`) {
self.saisonId = response[iii].id;
self.seasonId = response[iii].id;
break;
}
}
if(self.saisonId === null) {
if (self.seasonId === null) {
return;
}
self.seasonService.getVideo(self.saisonId)
self.seasonService.getVideo(self.seasonId)
.then((response2: any[]) => {
self.listFileInBdd = response2;
// console.log("find video: " + response2.length);
@ -549,6 +555,10 @@ export class UploadScene implements OnInit {
});
}
abortUpload(): void {
this.cancelHandle.abort();
}
eventPopUpSeason(event: string): void {
console.log(`GET event: ${event}`);
this.popInService.close('popin-new-season');
@ -575,3 +585,7 @@ export class UploadScene implements OnInit {
this.popInService.open('popin-create-type');
}
}
function isNullOrUndefined(abort: () => boolean) {
throw new Error('Function not implemented.');
}

View File

@ -2,21 +2,26 @@
<div class="title">
Edit Media
</div>
<div class="fill-all" *ngIf="itemIsRemoved">
@if(itemIsRemoved) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The media has been removed
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsNotFound">
}
@else if(itemIsNotFound) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
The media does not exist
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="itemIsLoading">
}
@else if(itemIsLoading) {
<div class="fill-all">
<div class="message-big">
<br/><br/><br/>
Loading ...<br/>
@ -24,8 +29,9 @@
<br/><br/><br/>
</div>
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
}
@else {
<div class="fill-all">
<div class="request_raw">
<div class="label">
Title:
@ -69,7 +75,9 @@
<div class="input">
<select [ngModel]="data.typeId"
(ngModelChange)="onChangeType($event)">
<option *ngFor="let element of listType" [ngValue]="element.value">{{element.label}}</option>
@for (data of listType; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</div>
<div class="input_add">
@ -85,7 +93,9 @@
<div class="input">
<select [ngModel]="data.seriesId"
(ngModelChange)="onChangeSeries($event)">
<option *ngFor="let element of listSeries" [ngValue]="element.value">{{element.label}}</option>
@for (data of listSeries; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</div>
<div class="input_add">
@ -101,7 +111,9 @@
<div class="input">
<select [ngModel]="data.seasonId"
(ngModelChange)="onChangeSeason($event)">
<option *ngFor="let element of listSeason" [ngValue]="element.value">{{element.label}}</option>
@for (data of listSeason; track data.value;) {
<option [ngValue]="data.value">{{data.label}}</option>
}
</select>
</div>
<div class="input_add">
@ -128,10 +140,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- Cover section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Covers
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="hide-element">
<input type="file"
#fileInput
@ -141,16 +153,18 @@
</div>
<div class="request_raw">
<div class="input">
<div class="cover" *ngFor="let element of coversDisplay">
@for (data of coversDisplay; track data.id;) {
<div class="cover">
<div class="cover-image">
<img src="{{element.url}}"/>
<img src="{{data.url}}"/>
</div>
<div class="cover-button">
<button (click)="removeCover(element.id)">
<button (click)="removeCover(data.id)">
<i class="material-icons button-remove">highlight_off</i>
</button>
</div>
</div>
}
<div class="cover">
<div class="cover-no-image">
</div>
@ -165,10 +179,10 @@
<div class="clear"></div>
</div>
<!-- ------------------------- ADMIN section --------------------------------- -->
<div class="title" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="title">
Administration
</div>
<div class="fill-all" *ngIf="!itemIsRemoved && !itemIsNotFound && !itemIsLoading">
<div class="fill-all">
<div class="request_raw">
<div class="label">
<i class="material-icons">data_usage</i> ID:
@ -190,6 +204,7 @@
</div>
<div class="clear"></div>
</div>
}
</div>
<create-type ></create-type>
@ -198,7 +213,8 @@
[mediaUploaded]="upload.mediaSendSize"
[mediaSize]="upload.mediaSize"
[result]="upload.result"
[error]="upload.error"></upload-progress>
[error]="upload.error"
(abort)="abortUpload()"></upload-progress>
<delete-confirm
[comment]="confirmDeleteComment"

View File

@ -5,14 +5,11 @@
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DataService, TypeService, SeriesService, VideoService, ArianeService } from 'app/service';
import { UploadProgress } from 'common/popin/upload-progress/upload-progress';
import { NodeData } from 'common/model';
import { PopInService } from 'common/service';
import { Media } from 'app/model';
import { DataService, TypeService, SeriesService, MediaService, ArianeService } from 'app/service';
import { PopInService, UploadProgress } from '@kangaroo-and-rabbit/kar-cw';
import { Media, Season, Series, UUID } from 'app/back-api';
import { RESTAbort } from 'app/back-api/rest-tools';
export interface ElementList {
value?: number;
@ -24,27 +21,13 @@ class DataToSend {
name: string = '';
description: string = '';
episode?: number;
seriesId: number = null;
seasonId: number = null;
dataId: number = -1;
seriesId?: number;
seasonId?: number;
dataId?: UUID;
time?: number;
typeId: number = null;
typeId?: number;
covers: number[] = [];
generatedName: string = '';
clone() {
let tmp = new DataToSend();
tmp.name = this.name;
tmp.description = this.description;
tmp.episode = this.episode;
tmp.seriesId = this.seriesId;
tmp.seasonId = this.seasonId;
tmp.dataId = this.dataId;
tmp.time = this.time;
tmp.typeId = this.typeId;
tmp.covers = this.covers;
tmp.generatedName = this.generatedName;
return tmp;
}
}
@Component({
@ -74,7 +57,7 @@ export class VideoEditScene implements OnInit {
// --------------- confirm section ------------------
public confirmDeleteComment: string = null;
public confirmDeleteImageUrl: string = null;
private deleteCoverId: number = null;
private deleteCoverId: UUID = null;
private deleteMediaId: number = null;
deleteConfirmed() {
if (this.deleteCoverId !== null) {
@ -108,16 +91,22 @@ export class VideoEditScene implements OnInit {
listSeason: ElementList[] = [
{ value: undefined, label: '---' },
];
cancelHandle: RESTAbort = {};
constructor(
private typeService: TypeService,
private dataService: DataService,
private MediaService: MediaService,
private seriesService: SeriesService,
private videoService: VideoService,
private typeService: TypeService,
private arianeService: ArianeService,
private popInService: PopInService,
private dataService: DataService) {
) {
}
abortUpload(): void {
this.cancelHandle.abort();
}
updateNeedSend(): boolean {
this.needSend = false;
if (this.data.name !== this.dataOri.name) {
@ -152,7 +141,7 @@ export class VideoEditScene implements OnInit {
this.data.covers.push(covers[iii]);
this.coversDisplay.push({
id: covers[iii],
url: this.dataService.getCoverThumbnailUrl(covers[iii])
url: this.dataService.getThumbnailUrl(covers[iii])
});
}
} else {
@ -166,7 +155,7 @@ export class VideoEditScene implements OnInit {
this.listUniverse = [{ value: null, label: '---' }];
this.listSeries = [{ value: null, label: '---' }];
this.listSeason = [{ value: null, label: '---' }];
this.typeService.getData()
this.typeService.gets()
.then((response2) => {
for (let iii = 0; iii < response2.length; iii++) {
self.listType.push({ value: response2[iii].id, label: response2[iii].name });
@ -175,7 +164,7 @@ export class VideoEditScene implements OnInit {
console.log(`get response22 : ${JSON.stringify(response2, null, 2)}`);
});
// this.seriesService.getOrder()
this.seriesService.getData()
this.seriesService.gets()
.then((response3) => {
for (let iii = 0; iii < response3.length; iii++) {
self.listSeries.push({ value: response3[iii].id, label: response3[iii].name });
@ -184,7 +173,7 @@ export class VideoEditScene implements OnInit {
}).catch((response3) => {
console.log(`get response3 : ${JSON.stringify(response3, null, 2)}`);
});
this.videoService.get(this.idVideo)
this.MediaService.get(this.idVideo)
.then((response: Media) => {
console.log(`get response of video : ${JSON.stringify(response, null, 2)}`);
self.data.name = response.name;
@ -199,7 +188,7 @@ export class VideoEditScene implements OnInit {
if (self.data.seasonId === undefined) {
self.data.seasonId = null;
}
self.dataOri = self.data.clone();
self.dataOri = { ...self.data };
self.updateCoverList(response.covers);
self.updateNeedSend();
console.log(`coversList : ${JSON.stringify(self.coversDisplay, null, 2)}`);
@ -208,7 +197,7 @@ export class VideoEditScene implements OnInit {
self.error = 'Can not get the data';
self.data = new DataToSend();
self.coversDisplay = [];
self.dataOri = self.data.clone();
self.dataOri = { ...self.data };
self.updateNeedSend();
self.itemIsNotFound = true;
self.itemIsLoading = false;
@ -228,8 +217,8 @@ export class VideoEditScene implements OnInit {
let self = this;
this.updateNeedSend();
if (this.data.typeId !== undefined) {
self.typeService.getSubSeries(this.data.typeId)
.then((response2: NodeData[]) => {
self.seriesService.getSeriesWithType(this.data.typeId)
.then((response2: Series[]) => {
for (let iii = 0; iii < response2.length; iii++) {
self.listSeries.push({ value: response2[iii].id, label: response2[iii].name });
}
@ -249,7 +238,7 @@ export class VideoEditScene implements OnInit {
let self = this;
if (this.data.seriesId !== undefined) {
self.seriesService.getSeason(this.data.seriesId)
.then((response3: NodeData[]) => {
.then((response3: Season[]) => {
for (let iii = 0; iii < response3.length; iii++) {
self.listSeason.push({ value: response3[iii].id, label: `season ${response3[iii].name}` });
}
@ -338,9 +327,9 @@ export class VideoEditScene implements OnInit {
data.seasonId = this.data.seasonId;
}
}
let tmpp = this.data.clone();
const tmpp = { ...this.data };
let self = this;
this.videoService.patch(this.idVideo, data)
this.MediaService.patch(this.idVideo, data)
.then((response3) => {
self.dataOri = tmpp;
self.updateNeedSend();
@ -385,14 +374,14 @@ export class VideoEditScene implements OnInit {
this.upload.clear();
// display the upload pop-in
this.popInService.open('popin-upload-progress');
this.videoService.uploadCover(file, this.idVideo, (count, total) => {
this.MediaService.uploadCover(this.idVideo, file, (count, total) => {
self.upload.mediaSendSize = count;
self.upload.mediaSize = total;
})
}, this.cancelHandle)
.then((response: any) => {
console.log(`get response of cover : ${JSON.stringify(response, null, 2)}`);
self.upload.result = 'Cover added done';
// we retrive the whiole media ==> update data ...
// we retrieve the while media ==> update data ...
self.updateCoverList(response.covers);
}).catch((response: any) => {
// self.error = "Can not get the data";
@ -401,21 +390,21 @@ export class VideoEditScene implements OnInit {
});
}
removeCover(id: number) {
removeCover(id: UUID) {
this.cleanConfirm();
this.confirmDeleteComment = `Delete the cover ID: ${id}`;
this.confirmDeleteImageUrl = this.dataService.getCoverThumbnailUrl(id);
this.confirmDeleteImageUrl = this.dataService.getThumbnailUrl(id);
this.deleteCoverId = id;
this.popInService.open('popin-delete-confirm');
}
removeCoverAfterConfirm(id: number) {
removeCoverAfterConfirm(id: UUID) {
console.log(`Request remove cover: ${id}`);
let self = this;
this.videoService.deleteCover(this.idVideo, id)
this.MediaService.deleteCover(this.idVideo, id)
.then((response: any) => {
console.log(`get response of remove cover : ${JSON.stringify(response, null, 2)}`);
self.upload.result = 'Cover remove done';
// we retrive the whiole media ==> update data ...
// we retrieve the while media ==> update data ...
self.updateCoverList(response.covers);
}).catch((response: any) => {
// self.error = "Can not get the data";
@ -432,7 +421,7 @@ export class VideoEditScene implements OnInit {
}
removeItemAfterConfirm(id: number) {
let self = this;
this.videoService.delete(id)
this.MediaService.delete(id)
.then((response3) => {
// self.dataOri = tmpp;
// self.updateNeedSend();

View File

@ -1,69 +1,91 @@
<div class="main-reduce">
<div class="fill-all" *ngIf="mediaIsNotFound">
@if(mediaIsNotFound) {
<div class="fill-all">
<div class="title">
Play media<br /><br /><br /><br /><br />
The media does not exist
</div>
</div>
<div class="fill-all" *ngIf="mediaIsLoading">
}
@else if(mediaIsLoading) {
<div class="fill-all">
<div class="title">
Play media<br /><br /><br /><br /><br />
Loading ...<br />
Please wait.
</div>
</div>
<div class="fill-all" *ngIf="!mediaIsNotFound && !mediaIsLoading && !playVideo">
}
@else if(!playVideo) {
<div class="fill-all">
<div class="title">
{{name}}
</div>
<div class="cover-full">
<div class="cover">
<div class="cover-image" *ngIf="covers">
@if(covers) {
<div class="cover-image">
<img src="{{covers[0]}}" />
</div>
<div class="cover-no-image" *ngIf="covers"></div>
}
@else {
<div class="cover-no-image"></div>
}
<div class="cover-button">
<button (click)="onRequirePlay()">
<i class="material-icons big-button">play_circle_outline</i>
</button>
</div>
</div>
<div class="cover-button-next" *ngIf="haveNext !== null">
@if(haveNext) {
<div class="cover-button-next">
<button (click)="onRequireNext($event)" (auxclick)="onRequireNext($event)">
<i class="material-icons big-button">arrow_forward_ios</i>
</button>
</div>
<div class="cover-button-previous" *ngIf="havePrevious !== null">
}
@if(havePrevious) {
<div class="cover-button-previous">
<button (click)="onRequirePrevious($event)" (auxclick)="onRequirePrevious($event)">
<i class="material-icons big-button">arrow_back_ios</i>
</button>
</div>
}
</div>
<div class="clear"></div>
<div class="episode" *ngIf="seriesName!=null">
@if(seriesName) {
<div class="episode">
<b>Series:</b> {{seriesName}}
</div>
<div class="episode" *ngIf="seasonName!=null">
}
@if(seasonName) {
<div class="episode">
<b>Season:</b> {{seasonName}}
</div>
<div class="episode" *ngIf="episode!=null">
}
@if(episode) {
<div class="episode">
<b>Episode:</b> {{episode}}
</div>
}
<div class="episode">
<b>generatedName:</b> {{generatedName}}
</div>
<div class="episode" *ngIf="userMetaData">
@if(userMetaData) {
<div class="episode">
<b>Number of view:</b> {{userMetaData.count}}<br />
<b>Position:</b> {{userMetaData.percentDisplay}} % ==> {{convertIndisplayTime(userMetaData.time)}}
</div>
}
<div class="description">
{{description}}
</div>
</div>
<div class="fill-all bg-black" *ngIf="playVideo">
}
@else {
<div class="fill-all bg-black">
<div class="video" #globalVideoElement (mousemove)="startHideTimer()"
(fullscreenchange)="onFullscreenChange($event)">
(fullscreenchange)="onFullscreenChange()">
<div class="video-elem">
<video src="{{videoSource}}" #videoPlayer preload (play)="changeStateToPlay()"
(pause)="changeStateToPause()" (timeupdate)="changeTimeupdate($event.currentTime)"
@ -73,9 +95,14 @@
<!--<p>Your browser does not support HTML5 video player. download video: <a href="{{videoSource}}>link here</a>.</p>-->
</video>
</div>
<div class="controls" *ngIf="!displayNeedHide || !isPlaying">
<button (click)="onPlay()" *ngIf="!isPlaying"><i class="material-icons">play_arrow</i></button>
<button (click)="onPause()" *ngIf="isPlaying"><i class="material-icons">pause</i></button>
@if(!displayNeedHide || !isPlaying) {
<div class="controls">
@if(isPlaying) {
<button (click)="onPause()"><i class="material-icons">pause</i></button>
}
@else {
<button (click)="onPlay()"><i class="material-icons">play_arrow</i></button>
}
<button (click)="onStop()"><i class="material-icons">stop</i></button>
<div class="timer">
<div>
@ -91,41 +118,61 @@
<button (click)="onForward()"><i class="material-icons">fast_forward</i></button>
<!--<button (click)="onNext()"><i class="material-icons">navigate_next</i></button>-->
<!--<button (click)="onMore()" ><i class="material-icons">more_vert</i></button>-->
<button (click)="onFullscreen()" *ngIf="!isFullScreen"><i class="material-icons">fullscreen</i></button>
<button (click)="onFullscreenExit()" *ngIf="isFullScreen"><i
class="material-icons">fullscreen_exit</i></button>
@if(isFullScreen) {
<button (click)="onFullscreenExit()"><i class="material-icons">fullscreen_exit</i></button>
}
@else {
<button (click)="onFullscreen()"><i class="material-icons">fullscreen</i></button>
}
<!--<button (click)="onTakeScreenShoot()"><i class="material-icons">add_a_photo</i></button>-->
<button (click)="onVolumeMenu()"><i class="material-icons">volume_up</i></button>
<button class="bigPause" (click)="onPauseToggle()"><i *ngIf="!isPlaying"
class="material-icons">play_circle_outline</i></button>
<button class="bigRewind" (click)="onRewind()"><i *ngIf="!isPlaying"
class="material-icons">fast_rewind</i></button>
<button class="bigForward" (click)="onForward()"><i *ngIf="!isPlaying"
class="material-icons">fast_forward</i></button>
<button class="bigPause" (click)="onPauseToggle()">
@if(!isPlaying) {
<i class="material-icons">play_circle_outline</i>
}
</button>
<button class="bigRewind" (click)="onRewind()">
@if(!isPlaying) {
<i class="material-icons">fast_rewind</i>
}
</button>
<button class="bigForward" (click)="onForward()">
@if(!isPlaying) {
<i class="material-icons">fast_forward</i>
}
</button>
</div>
<div class="title-inline" *ngIf="!isFullScreen || !isPlaying">
}
@if(!isFullScreen || !isPlaying) {
<div class="title-inline">
{{generatedName}}
</div>
<div class="video-button" *ngIf="!isFullScreen || !isPlaying">
<div class="video-button">
<button (click)="onRequireStop()">
<i class="material-icons big-button">highlight_off</i>
</button>
</div>
<div class="volume" *ngIf="displayVolumeMenu && (!displayNeedHide || !isPlaying)">
}
@if(displayVolumeMenu && (!displayNeedHide || !isPlaying)) {
<div class="volume">
<div class="volume-menu">
<div class="slidecontainer">
<input type="range" min="0" max="100" class="slider" [value]="volumeValue"
(input)="onVolume($event.target)">
</div>
<button (click)="onVolumeMute()" *ngIf="!videoPlayer.muted"><i
class="material-icons">volume_mute</i></button>
<button (click)="onVolumeUnMute()" *ngIf="videoPlayer.muted"><i
@if(videoPlayer.muted) {
<button (click)="onVolumeUnMute()"><i
class="material-icons">volume_off</i></button>
}
@else {
<button (click)="onVolumeMute()"><i
class="material-icons">volume_mute</i></button>
}
</div>
</div>
}
</div>
</div>
}
<canvas #canvascreenshoot style="overflow:auto"></canvas>
</div>

View File

@ -5,10 +5,10 @@
*/
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { UserMediaAdvancement } from 'app/model/user-media-advancement';
import { DataService, VideoService, SeriesService, SeasonService, ArianeService, AdvancementService } from 'app/service';
import { HttpWrapperService } from 'common/service';
import { isNullOrUndefined } from 'common/utils';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { UUID, UserMediaAdvancement } from 'app/back-api';
import { DataService, MediaService, SeriesService, SeasonService, ArianeService, AdvancementService } from 'app/service';
@Component({
selector: 'app-video',
@ -53,7 +53,7 @@ export class VideoScene implements OnInit {
seriesName: string = undefined;
seasonId: number = undefined;
seasonName: string = undefined;
dataId: number = -1;
dataId: UUID;
time: number = undefined;
typeId: number = undefined;
generatedName: string = '';
@ -88,13 +88,13 @@ export class VideoScene implements OnInit {
registered: false
};
constructor(private videoService: VideoService,
private seriesService: SeriesService,
private seasonService: SeasonService,
private httpService: HttpWrapperService,
private arianeService: ArianeService,
constructor(
private dataService: DataService,
private advancementService: AdvancementService) {
private advancementService: AdvancementService,
private MediaService: MediaService,
private seasonService: SeasonService,
private seriesService: SeriesService,
private arianeService: ArianeService,) {
}
@ -154,10 +154,10 @@ export class VideoScene implements OnInit {
this.generatedName = this.generatedName.replace(new RegExp('&', 'g'), '_');
this.generatedName = this.generatedName.replace(new RegExp('/', 'g'), '_');
// update the path of the uri request
this.videoSource = this.httpService.createRESTCall2({
api: `data/${this.dataId}/${this.generatedName}`,
addURLToken: true,
});
this.videoSource = this.dataService.getUrl(
this.dataId,
this.generatedName,
);
}
myPeriodicCheckFunction() {
@ -209,7 +209,7 @@ export class VideoScene implements OnInit {
let self = this;
self.haveNext = null;
self.havePrevious = null;
this.videoService.get(this.idVideo)
this.MediaService.get(this.idVideo)
.then((response) => {
console.log(`get response of video : ${JSON.stringify(response, null, 2)}`);
self.error = '';
@ -221,15 +221,12 @@ export class VideoScene implements OnInit {
self.dataId = response.dataId;
self.time = response.time;
self.generatedName = "????????? TODO: ???????" //response.generatedName;
if (self.dataId !== -1) {
self.videoSource = self.httpService.createRESTCall2({
api: `data/${self.dataId}/${self.generatedName}`,
addURLToken: true,
});
if (!isNullOrUndefined(self.dataId)) {
self.videoSource = self.dataService.getUrl(self.dataId, self.generatedName);
} else {
self.videoSource = '';
}
self.covers = self.dataService.getCoverListUrl(response.covers);
self.covers = self.dataService.getListThumbnailUrl(response.covers);
self.generateName();
if (self.seriesId !== undefined && self.seriesId !== null) {
@ -271,7 +268,7 @@ export class VideoScene implements OnInit {
self.haveNext = response6[iii];
}
}
//self.covers.push(self.dataService.getCoverUrl(response6[iii]));
//self.covers.push(self.dataService.getThumbnailUrl(response6[iii]));
}
}).catch((response7: any) => {
@ -287,7 +284,7 @@ export class VideoScene implements OnInit {
self.episode = undefined;
self.seriesId = undefined;
self.seasonId = undefined;
self.dataId = -1;
self.dataId = undefined;
self.time = undefined;
self.generatedName = '';
self.videoSource = '';
@ -457,8 +454,7 @@ export class VideoScene implements OnInit {
onStop() {
console.log('stop');
this.startHideTimer();
if (this.videoPlayer === null ||
this.videoPlayer === undefined) {
if (isNullOrUndefined(this.videoPlayer)) {
console.log(`error element: ${this.videoPlayer}`);
return;
}
@ -479,8 +475,7 @@ export class VideoScene implements OnInit {
seek(newValue: any) {
console.log(`seek ${newValue.value}`);
this.startHideTimer();
if (this.videoPlayer === null ||
this.videoPlayer === undefined) {
if (isNullOrUndefined(this.videoPlayer)) {
console.log(`error element: ${this.videoPlayer}`);
return;
}
@ -490,8 +485,7 @@ export class VideoScene implements OnInit {
onRewind() {
console.log('rewind');
this.startHideTimer();
if (this.videoPlayer === null ||
this.videoPlayer === undefined) {
if (isNullOrUndefined(this.videoPlayer)) {
console.log(`error element: ${this.videoPlayer}`);
return;
}
@ -501,8 +495,7 @@ export class VideoScene implements OnInit {
onForward() {
console.log('forward');
this.startHideTimer();
if (this.videoPlayer === null ||
this.videoPlayer === undefined) {
if (isNullOrUndefined(this.videoPlayer)) {
console.log(`error element: ${this.videoPlayer}`);
return;
}
@ -512,8 +505,7 @@ export class VideoScene implements OnInit {
onMore() {
console.log('more');
this.startHideTimer();
if (this.videoPlayer === null ||
this.videoPlayer === undefined) {
if (isNullOrUndefined(this.videoPlayer)) {
console.log(`error element: ${this.videoPlayer}`);
return;
}
@ -521,8 +513,7 @@ export class VideoScene implements OnInit {
onFullscreen() {
console.log('fullscreen');
this.startHideTimer();
if (this.videoGlobal === null ||
this.videoGlobal === undefined) {
if (isNullOrUndefined(this.videoGlobal)) {
console.log(`error element: ${this.videoGlobal}`);
return;
}
@ -543,8 +534,7 @@ export class VideoScene implements OnInit {
onFullscreenExit22(docc: any) {
console.log('fullscreen EXIT');
this.startHideTimer();
if (this.videoGlobal === null ||
this.videoGlobal === undefined) {
if (isNullOrUndefined(this.videoGlobal)) {
console.log(`error element: ${this.videoGlobal}`);
return;
}
@ -620,17 +610,18 @@ export class VideoScene implements OnInit {
// canvas.crossorigin="anonymous"
// convert it and send it to the server
let self = this;
/*
this.videoCanva.toBlob((blob) => {
self.videoService.uploadCoverBlob(blob, this.idVideo);
self.MediaService.uploadCoverBlob(blob, this.idVideo);
}, 'image/jpeg', 0.95);
*/
console.log("not implement upload as row data...");
/*
let tmpUrl = this.videoCanva.toDataURL('image/jpeg', 0.95);
fetch(tmpUrl)
.then(res => res.blob()) // Gets the response and returns it as a blob
.then(blob => {
self.videoService.uploadCoverBlob(blob, this.idVideo);
self.MediaService.uploadCoverBlob(blob, this.idVideo);
});
*/
}

View File

@ -0,0 +1,123 @@
import { DataStore, DataTools, TypeCheck, isNullOrUndefined } from "@kangaroo-and-rabbit/kar-cw";
import { SelectModel } from "@kangaroo-and-rabbit/kar-cw/utils/data-tools";
export class GenericDataService<TYPE> {
protected dataStore: DataStore<TYPE>;
setStore(dataStore: DataStore<TYPE>) {
this.dataStore = dataStore;
}
gets(): Promise<TYPE[]> {
return this.dataStore.getData();
}
get(id: number): Promise<TYPE> {
let self = this;
return new Promise((resolve, reject) => {
self.gets()
.then((response: TYPE[]) => {
let data = DataTools.get(response, id);
if (isNullOrUndefined(data)) {
reject('Data does not exist in the local BDD');
return;
}
resolve(data);
return;
}).catch((response) => {
reject(response);
});
});
}
getAll(ids: number[]): Promise<TYPE[]> {
let self = this;
return new Promise((resolve, reject) => {
self.gets()
.then((response: TYPE[]) => {
let data = DataTools.getsWhere(response,
[
{
check: TypeCheck.EQUAL,
key: 'id',
value: ids,
},
]);
resolve(data);
return;
}).catch((response) => {
reject(response);
});
});
}
getFilter(filter: (subData: any) => boolean): Promise<TYPE[]> {
let self = this;
return new Promise((resolve, reject) => {
self.gets()
.then((data: TYPE[]) => {
if (isNullOrUndefined(data)) {
reject('Data does not exist in the local BDD');
return;
}
let out: TYPE[] = [];
for (const elem of data) {
if (filter(elem)) {
out.push(elem);
}
}
resolve(out);
return;
}).catch((response) => {
reject(response);
});
});
}
getLike(value: string): Promise<TYPE[]> {
let self = this;
return new Promise((resolve, reject) => {
self.gets()
.then((response: TYPE[]) => {
let data = DataTools.getNameLike(response, value);
if (isNullOrUndefined(data) || data.length === 0) {
reject('Data does not exist in the local BDD');
return;
}
resolve(data);
return;
}).catch((response) => {
reject(response);
});
});
}
getOrder(): Promise<TYPE[]> {
return this.getsWhere(
[
{
check: TypeCheck.NOT_EQUAL,
key: 'id',
value: [undefined, null],
},
],
["name", "id"],
);
}
getsWhere(select: SelectModel[], orderByData?: string[]): Promise<TYPE[]> {
let self = this;
return new Promise((resolve, reject) => {
self.gets()
.then((response: TYPE[]) => {
let data = DataTools.getsWhere(response, select, orderByData);
resolve(data);
}).catch((response) => {
console.log(`[E] ${self.constructor.name}: can not retrieve BDD values`);
reject(response);
});
});
}
}

Some files were not shown because too many files have changed in this diff Show More