ServiceMedia.java
/*
* Copyright (C) 2000 - 2025 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "https://www.silverpeas.org/legal/floss_exception.html"
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.silverpeas.mobile.server.services;
import jakarta.activation.MimetypesFileTypeMap;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import org.silverpeas.components.gallery.GalleryComponentSettings;
import org.silverpeas.components.gallery.constant.MediaResolution;
import org.silverpeas.components.gallery.constant.MediaType;
import org.silverpeas.components.gallery.delegate.MediaDataCreateDelegate;
import org.silverpeas.components.gallery.model.*;
import org.silverpeas.components.gallery.service.GalleryService;
import org.silverpeas.core.ResourceReference;
import org.silverpeas.core.admin.component.model.ComponentInstLight;
import org.silverpeas.core.admin.service.Administration;
import org.silverpeas.core.annotation.WebService;
import org.silverpeas.core.comment.service.CommentServiceProvider;
import org.silverpeas.core.node.model.NodePK;
import org.silverpeas.core.util.file.FileItem;
import org.silverpeas.core.web.rs.annotation.Authorized;
import org.silverpeas.core.webapi.media.streaming.StreamingProviderDataEntity;
import org.silverpeas.kernel.logging.SilverLogger;
import org.silverpeas.mobile.server.common.CommandCreateList;
import org.silverpeas.mobile.server.common.LocalDiskFileItem;
import org.silverpeas.mobile.shared.StreamingList;
import org.silverpeas.mobile.shared.dto.BaseDTO;
import org.silverpeas.mobile.shared.dto.comments.CommentDTO;
import org.silverpeas.mobile.shared.dto.media.*;
import org.silverpeas.mobile.shared.exceptions.AuthenticationException;
import org.silverpeas.mobile.shared.exceptions.MediaException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import static org.silverpeas.kernel.util.StringUtil.EMPTY;
/**
* Service de gestion des galleries d'images.
* @author svuillet
*/
@WebService
@Authorized
@Path(ServiceMedia.PATH + "/{appId}")
public class ServiceMedia extends AbstractRestWebService {
@Inject
private Administration admin;
@Inject
private GalleryService galleryService;
@PathParam("appId")
private String componentId;
static final String PATH = "mobile/medialib";
/**
* Importation d'une image dans un album.
*/
@POST
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Consumes(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("add/{name}/{data}/{idGallery}/{idAlbum}")
public void uploadPicture(@PathParam("name") String name, @PathParam("data") String data, @PathParam("idGallery") String idGallery, @PathParam("idAlbum") String idAlbum) {
String extension = "jpg";
if (data.contains("data:image/jpeg;base64,")) {
extension = "jpg";
}
try {
// stockage temporaire de la photo upload
String tempDir = System.getProperty("java.io.tmpdir");
String filename = tempDir + File.separator + name + "." + extension;
OutputStream outputStream = new FileOutputStream(filename);
outputStream.close();
File file = new File(filename);
// creation de la photo dans l'albums
createPhoto(idGallery, idAlbum, file);
Files.delete(file.toPath());
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
}
}
private void createPhoto(String componentId, String albumId, File file)
throws Exception {
// création de la photo
List<FileItem> parameters = new ArrayList<>();
String type = new MimetypesFileTypeMap().getContentType(file);
LocalDiskFileItem item = new LocalDiskFileItem(file, type);
parameters.add(item);
MediaDataCreateDelegate
delegate = new MediaDataCreateDelegate(MediaType.Photo, "fr", albumId, parameters);
Media newMedia = getGalleryService().createMedia(admin.getUserDetail(getUser().getId()), componentId, GalleryComponentSettings.getWatermark(componentId), delegate);
newMedia.getId();
}
/**
* Retourne la liste des albums d'une appli media.
*/
private List<AlbumDTO> getAlbums(String instanceId, String rootAlbumId) throws MediaException {
ArrayList<AlbumDTO> results = new ArrayList<>();
try {
if (rootAlbumId.equalsIgnoreCase("null")) rootAlbumId = null;
if (rootAlbumId == null) {
AlbumDTO rootAlbum = new AlbumDTO();
ComponentInstLight app = admin.getComponentInstLight(instanceId);
rootAlbum.setName(app.getLabel());
rootAlbum.setRoot(true);
results.add(rootAlbum);
Collection<AlbumDetail> albums = getGalleryService().getAllAlbums(instanceId);
for (AlbumDetail albumDetail : albums) {
if (albumDetail.getLevel() == 2) {
AlbumDTO album = populate(albumDetail);
results.add(album);
}
}
} else {
AlbumDetail rootAlbum = getGalleryService().getAlbum(new NodePK(rootAlbumId, instanceId),
MediaCriteria.VISIBILITY.VISIBLE_ONLY);
AlbumDTO rootAlbumDTO = populate(rootAlbum);
rootAlbumDTO.setRoot(true);
results.add(rootAlbumDTO);
Collection<AlbumDetail> albums = rootAlbum.getChildrenAlbumsDetails();
for (AlbumDetail albumDetail : albums) {
AlbumDTO album = populate(albumDetail);
results.add(album);
}
}
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw new MediaException(e);
}
return results;
}
private AlbumDTO populate(final AlbumDetail albumDetail) {
AlbumDTO album = new AlbumDTO();
album.setId(albumDetail.getId());
album.setName(albumDetail.getName());
int nbPhotos = countMedias(albumDetail);
album.setCountMedia(nbPhotos);
return album;
}
private int countMedias(final AlbumDetail albumDetail) {
int count;
Collection<Media> allMedias = getGalleryService().getAllMedia(albumDetail.getNodePK(),
MediaCriteria.VISIBILITY.VISIBLE_ONLY);
count = allMedias.size();
// browser all sub albums for count all medias
AlbumDetail thisAlbum = getGalleryService().getAlbum(albumDetail.getNodePK(),
MediaCriteria.VISIBILITY.VISIBLE_ONLY);
Collection<AlbumDetail> subAlbums = thisAlbum.getChildrenAlbumsDetails();
for (AlbumDetail oneSubAlbum : subAlbums) {
count = count + countMedias(oneSubAlbum);
}
return count;
}
private List<MediaDTO> getMedias(String instanceId, String albumId) throws MediaException {
ArrayList<MediaDTO> results = new ArrayList<>();
if (albumId.equalsIgnoreCase("null")) albumId = null;
if (albumId == null) return results;
try {
Collection<Media> medias = getGalleryService().getAllMedia(new NodePK(albumId, instanceId),
MediaCriteria.VISIBILITY.VISIBLE_ONLY);
for (Media media : medias) {
results.add(getMedia(media));
}
return results;
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw new MediaException(e);
}
}
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("media/{id}")
public MediaDTO getMedia(@PathParam("id") String id) {
MediaDTO dto;
try {
Media media = getGalleryService().getMedia(new MediaPK(id));
dto = getMedia(media);
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw new WebApplicationException(e);
}
return dto;
}
private MediaDTO getMedia(Media media) {
if (media.getType().isPhoto()) {
return getPhoto(media.getId(), MediaResolution.SMALL);
} else if (media.getType().isSound()) {
return getSound(media);
} else if (media.getType().isStreaming()) {
return getVideoStreaming(media);
} else if (media.getType().isVideo()) {
return getVideo(media);
}
return null;
}
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("albumsandpics/{rootAlbumId}/{callNumber}")
public StreamingList<BaseDTO> getAlbumsAndPictures(@PathParam("appId") String instanceId, @PathParam("rootAlbumId") String rootAlbumId, @PathParam("callNumber") int callNumber) {
int callSize = 25;
String cacheKey = instanceId+rootAlbumId;
CommandCreateList command = () -> {
List<BaseDTO> list = new ArrayList<>();
list.addAll(getAlbums(instanceId, rootAlbumId));
list.addAll(getMedias(instanceId, rootAlbumId));
return list;
};
StreamingList<BaseDTO> streamingList;
try {
streamingList = createStreamingList(command, callNumber, callSize, cacheKey);
} catch (AuthenticationException e) {
throw new NotAuthorizedException(e);
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw new WebApplicationException(e);
}
return streamingList;
}
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("sound/{id}")
public SoundDTO getSound(@PathParam("id") String soundId){
Media sound;
try {
sound = getGalleryService().getMedia(new MediaPK(soundId));
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw e;
}
return getSound(sound);
}
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("video/{videoId}")
public VideoDTO getVideo(@PathParam("videoId") String videoId) {
Media video;
try {
video = getGalleryService().getMedia(new MediaPK(videoId));
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw e;
}
return getVideo(video);
}
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("videostream/{videoId}")
public VideoStreamingDTO getVideoStreaming(@PathParam("videoId") String videoId) {
Media video;
try {
video = getGalleryService().getMedia(new MediaPK(videoId));
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw e;
}
return getVideoStreaming(video);
}
/**
* Retourne la photo preview.
*/
@GET
@Produces(jakarta.ws.rs.core.MediaType.APPLICATION_JSON)
@Path("photo/{pictureId}")
public PhotoDTO getPreviewPicture(@PathParam("pictureId") String pictureId) {
PhotoDTO picture;
try {
picture = getPhoto(pictureId, MediaResolution.PREVIEW);
} catch (Exception e) {
SilverLogger.getLogger(this).error(e);
throw e;
}
return picture;
}
private VideoStreamingDTO getVideoStreaming(Media media) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
VideoStreamingDTO video = new VideoStreamingDTO();
video.setName(media.getName());
video.setTitle(media.getTitle());
video.setId(media.getId());
video.setMimeType(media.getType().getMediaWebUriPart());
video.setInstance(media.getInstanceId());
video.setCreator(media.getCreatorName());
video.setCreationDate(sdf.format(media.getCreationDate()));
if (media.getLastUpdater() != null) {
video.setUpdater(media.getLastUpdaterName());
} else {
video.setUpdater(media.getCreatorName());
}
if (media.getLastUpdateDate() != null) {
video.setUpdateDate(sdf.format(media.getLastUpdateDate()));
} else {
video.setUpdateDate(sdf.format(media.getCreationDate()));
}
final String urlVideo = media.getStreaming().getHomepageUrl();
final Runnable noData = () -> {
video.setUrl(EMPTY);
video.setUrlPoster(EMPTY);
};
try {
StreamingProviderDataEntity.from(urlVideo).ifPresentOrElse(e -> {
video.setUrl(e.getSilverpeasEmbedUrl());
video.setUrlPoster(e.getThumbnailUrl().toString());
video.setDuration(e.getFormattedDurationHMS());
}, noData);
} catch (Exception e) {
SilverLogger.getLogger(this).error("ServiceMediaImpl.getVideoStreaming", "root.EX_NO_MESSAGE", e);
noData.run();
}
final ResourceReference ref = new ResourceReference(new MediaPK(video.getId()));
video.setCommentsNumber(CommentServiceProvider.getCommentService().getCommentsCountOnResource(
CommentDTO.TYPE_STREAMING, ref));
return video;
}
private VideoDTO getVideo(Media media) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
long d = media.getVideo().getDuration() / 1000;
Date duration = new Date(d*1000);
SimpleDateFormat durationFormat = getDurationFormat(d);
VideoDTO video = new VideoDTO();
video.setName(media.getName());
video.setTitle(media.getTitle());
video.setId(media.getId());
video.setMimeType(media.getVideo().getFileMimeType().getMimeType());
video.setInstance(media.getInstanceId());
video.setDownload(media.getVideo().isDownloadAuthorized());
video.setSize(media.getVideo().getFileSize());
video.setDuration(durationFormat.format(duration));
video.setCreator(media.getCreatorName());
video.setCreationDate(sdf.format(media.getCreationDate()));
if (media.getLastUpdater() != null) {
video.setUpdater(media.getLastUpdaterName());
} else {
video.setUpdater(media.getCreatorName());
}
if (media.getLastUpdateDate() != null) {
video.setUpdateDate(sdf.format(media.getLastUpdateDate()));
} else {
video.setUpdateDate(sdf.format(media.getCreationDate()));
}
final ResourceReference ref = new ResourceReference(new MediaPK(video.getId()));
video.setCommentsNumber(CommentServiceProvider.getCommentService().getCommentsCountOnResource(CommentDTO.TYPE_VIDEO, ref));
return video;
}
private SoundDTO getSound(Media media) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
long d = media.getSound().getDuration() / 1000;
Date duration = new Date(d*1000);
SimpleDateFormat durationFormat = getDurationFormat(d);
SoundDTO sound = new SoundDTO();
sound.setName(media.getName());
sound.setTitle(media.getTitle());
sound.setId(media.getId());
sound.setMimeType(media.getSound().getFileMimeType().getMimeType());
sound.setInstance(media.getInstanceId());
sound.setDownload(media.getSound().isDownloadAuthorized());
sound.setSize(media.getSound().getFileSize());
sound.setDuration(durationFormat.format(duration));
sound.setCreator(media.getCreatorName());
sound.setCreationDate(sdf.format(media.getCreationDate()));
if (media.getLastUpdater() != null) {
sound.setUpdater(media.getLastUpdaterName());
} else {
sound.setUpdater(media.getCreatorName());
}
if (media.getLastUpdateDate() != null) {
sound.setUpdateDate(sdf.format(media.getLastUpdateDate()));
} else {
sound.setUpdateDate(sdf.format(media.getCreationDate()));
}
final ResourceReference ref = new ResourceReference(new MediaPK(sound.getId()));
sound.setCommentsNumber(CommentServiceProvider.getCommentService().getCommentsCountOnResource(CommentDTO.TYPE_SOUND, ref));
return sound;
}
private SimpleDateFormat getDurationFormat(long d) {
SimpleDateFormat durationFormat = new SimpleDateFormat("HH:mm:ss");
if (d < 59) {
durationFormat = new SimpleDateFormat("ss");
} else if (d < (60 * 59)) {
durationFormat = new SimpleDateFormat("mm:ss");
}
return durationFormat;
}
private PhotoDTO getPhoto(String pictureId, MediaResolution size) {
PhotoDTO picture;
Photo photoDetail = getGalleryService().getPhoto(new MediaPK(pictureId));
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
picture = new PhotoDTO();
picture.setId(photoDetail.getId());
picture.setDownload(photoDetail.isDownloadAuthorized());
picture.setDataPhoto("/silverpeas/services/gallery/"+photoDetail.getInstanceId()+"/photos/"+photoDetail.getId()+"/content?resolution="+size.getName());
picture.setFormat(size.name());
picture.setTitle(photoDetail.getTitle());
picture.setName(photoDetail.getName());
picture.setSize(photoDetail.getFileSize());
picture.setSizeH(photoDetail.getDefinition().getHeight());
picture.setSizeL(photoDetail.getDefinition().getWidth());
picture.setMimeType(photoDetail.getFileMimeType().getMimeType());
picture.setInstance(photoDetail.getInstanceId());
picture.setCreator(photoDetail.getCreatorName());
picture.setCreationDate(sdf.format(photoDetail.getCreationDate()));
if (photoDetail.getLastUpdater() != null) {
picture.setUpdater(photoDetail.getLastUpdaterName());
} else {
picture.setUpdater(photoDetail.getCreatorName());
}
if (photoDetail.getLastUpdateDate() != null) {
picture.setUpdateDate(sdf.format(photoDetail.getLastUpdateDate()));
} else {
picture.setUpdateDate(sdf.format(photoDetail.getCreationDate()));
}
final ResourceReference ref = new ResourceReference(
new MediaPK(photoDetail.getId()));
picture.setCommentsNumber(CommentServiceProvider.getCommentService().getCommentsCountOnResource(CommentDTO.TYPE_PHOTO, ref));
return picture;
}
private GalleryService getGalleryService() {
return galleryService;
}
@Override
protected String getResourceBasePath() {
return PATH;
}
@Override
public String getComponentId() {
return this.componentId;
}
}