diff --git a/src/main/java/sh/rhiobet/lalafin/LalafinConfiguration.java b/src/main/java/sh/rhiobet/lalafin/LalafinConfiguration.java new file mode 100644 index 0000000..2e0a010 --- /dev/null +++ b/src/main/java/sh/rhiobet/lalafin/LalafinConfiguration.java @@ -0,0 +1,11 @@ +package sh.rhiobet.lalafin; + +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "lalafin") +public interface LalafinConfiguration { + + public String base_url(); + public String user_management_url(); + +} \ No newline at end of file diff --git a/src/main/java/sh/rhiobet/lalafin/api/FilePublicAPI.java b/src/main/java/sh/rhiobet/lalafin/api/FilePublicAPI.java index a7ef91f..f35444c 100644 --- a/src/main/java/sh/rhiobet/lalafin/api/FilePublicAPI.java +++ b/src/main/java/sh/rhiobet/lalafin/api/FilePublicAPI.java @@ -70,7 +70,7 @@ public class FilePublicAPI { if (fileInfoBase instanceof FileInfo) { return fileServeService.serveFile((FileInfo) fileInfoBase, range); } else if (fileInfoBase instanceof FolderInfo) { - return fileServeService.serveFolder((FolderInfo) fileInfoBase); + return fileServeService.serveFolder((FolderInfo) fileInfoBase, null); } else { return Response.status(Response.Status.NOT_FOUND).build(); } diff --git a/src/main/java/sh/rhiobet/lalafin/api/internal/FileTokenProvider.java b/src/main/java/sh/rhiobet/lalafin/api/internal/FileTokenProvider.java index 4526131..eb8412a 100644 --- a/src/main/java/sh/rhiobet/lalafin/api/internal/FileTokenProvider.java +++ b/src/main/java/sh/rhiobet/lalafin/api/internal/FileTokenProvider.java @@ -27,4 +27,8 @@ public class FileTokenProvider { } } + public String getUsername() { + return this.username; + } + } diff --git a/src/main/java/sh/rhiobet/lalafin/api/model/FolderInfo.java b/src/main/java/sh/rhiobet/lalafin/api/model/FolderInfo.java index afc3be2..4b4cd89 100644 --- a/src/main/java/sh/rhiobet/lalafin/api/model/FolderInfo.java +++ b/src/main/java/sh/rhiobet/lalafin/api/model/FolderInfo.java @@ -11,6 +11,7 @@ public class FolderInfo extends FileInfoBase { public String publicPersistentUrl; public Set content; + public FileInfo playlist; public FolderInfo(String filename, String thumbnailUrl, String directUrl, String viewUrl, String publicPersistentUrl) { diff --git a/src/main/java/sh/rhiobet/lalafin/file/FileInfoService.java b/src/main/java/sh/rhiobet/lalafin/file/FileInfoService.java index 43a83b2..ef6d4e6 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/FileInfoService.java +++ b/src/main/java/sh/rhiobet/lalafin/file/FileInfoService.java @@ -11,6 +11,7 @@ import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.ws.rs.core.PathSegment; +import sh.rhiobet.lalafin.LalafinConfiguration; import sh.rhiobet.lalafin.api.advent.AdventAccessService; import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration; import sh.rhiobet.lalafin.api.internal.FileTokenProvider; @@ -20,6 +21,9 @@ import sh.rhiobet.lalafin.api.model.FolderInfo; @ApplicationScoped public class FileInfoService { + @Inject + LalafinConfiguration lalafinConfiguration; + @Inject FileApiConfiguration fileApiConfiguration; @@ -96,6 +100,12 @@ public class FileInfoService { } FolderInfo folderInfo = new FolderInfo(requestedFilename, requestedThumbUrl, "/file" + requestedUri + "/", "/view" + requestedUri + "/1"); + Path playlistPath = null; + if (fileTokenProvider != null) { + playlistPath = Paths.get("/lalafin/file" + requestedPath + + "/.playlists/" + fileTokenProvider.getUsername() + ".m3u"); + } + StringBuilder playlistContent = new StringBuilder(); try { Files.list(path).forEach(p -> { String fileName = p.getFileName().toString(); @@ -130,6 +140,8 @@ public class FileInfoService { + fileTokenProvider .getFileToken(requestedUri + "/" + fileUri) + "/" + fileUri; + playlistContent.append(lalafinConfiguration.base_url() + + ((FileInfo) contentInfo).publicApiUrl + "\n"); } if (fileName.endsWith(".zip")) { contentInfo.viewUrl = "/view" + requestedUri + "/" + fileUri + "/1"; @@ -171,6 +183,19 @@ public class FileInfoService { } folderInfo.content.add(contentInfo); }); + if (playlistPath != null) { + playlistPath.getParent().toFile().mkdirs(); + Files.writeString(playlistPath, playlistContent.toString()); + String playlistFileUri = URLEncoder.encode(fileTokenProvider.getUsername() + + ".m3u", StandardCharsets.UTF_8).replace("+", "%20"); + FileInfo playlistInfo = new FileInfo( + fileTokenProvider.getUsername() + ".m3u", + requestedUri + "/.playlists/" + playlistFileUri); + playlistInfo.publicApiUrl = "/api/public/file/token/" + + fileTokenProvider.getFileToken(requestedUri + "/.playlists/" + + playlistFileUri) + "/" + playlistFileUri; + folderInfo.playlist = playlistInfo; + } } catch (IOException ignored) { } return folderInfo; diff --git a/src/main/java/sh/rhiobet/lalafin/file/FileResource.java b/src/main/java/sh/rhiobet/lalafin/file/FileResource.java index 19cb150..f50e79c 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/FileResource.java +++ b/src/main/java/sh/rhiobet/lalafin/file/FileResource.java @@ -71,7 +71,8 @@ public class FileResource { return Response.status(Response.Status.MOVED_PERMANENTLY) .location(URI.create(uriInfo.getRequestUri().getRawPath() + "/")).build(); } - return fileServeService.serveFolder((FolderInfo) fileInfoBase); + return fileServeService.serveFolder((FolderInfo) fileInfoBase, + securityIdentity.getPrincipal().getName()); } else if (fileInfoBase instanceof FileInfo) { return fileServeService.serveFile((FileInfo) fileInfoBase, range); } diff --git a/src/main/java/sh/rhiobet/lalafin/file/FileServeService.java b/src/main/java/sh/rhiobet/lalafin/file/FileServeService.java index 520777b..16bdfda 100644 --- a/src/main/java/sh/rhiobet/lalafin/file/FileServeService.java +++ b/src/main/java/sh/rhiobet/lalafin/file/FileServeService.java @@ -14,6 +14,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import io.quarkus.qute.Location; import io.quarkus.qute.Template; +import sh.rhiobet.lalafin.LalafinConfiguration; import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration; import sh.rhiobet.lalafin.api.model.FileInfo; import sh.rhiobet.lalafin.api.model.FileInfoBase; @@ -21,20 +22,25 @@ import sh.rhiobet.lalafin.api.model.FolderInfo; @ApplicationScoped public class FileServeService { + @Inject + LalafinConfiguration lalafinConfiguration; + @Inject FileApiConfiguration fileApiConfiguration; @Location("directory-index.html") Template directoryTemplate; - public Response serveFolder(FolderInfo folderInfo) { + public Response serveFolder(FolderInfo folderInfo, String username) { // Look for index file for (FileInfoBase content : folderInfo.content) { if (content instanceof FileInfo && content.filename.startsWith("index.")) { return this.serveFile((FileInfo) content, null); } } - ResponseBuilder response = Response.ok(directoryTemplate.data("info", folderInfo).render()); + ResponseBuilder response = Response.ok(directoryTemplate.data("info", folderInfo, + "username", username, "user_url", lalafinConfiguration.user_management_url()) + .render()); response.header("Content-Type", "text/html"); return response.build(); } @@ -47,19 +53,34 @@ public class FileServeService { ResponseBuilder response; long fileSize = channel.size(); if (range != null) { - long rangeStart = Long.parseLong(range.substring(6, range.length() - 1)); + range = range.substring(6); + String[] rangeSplitted = range.split("-"); + long rangeStart = 0; + if (!range.startsWith("-")) { + rangeStart = Long.parseLong(rangeSplitted[0]); + } + long rangeEnd = fileSize - 1; + if (rangeSplitted.length == 2) { + rangeEnd = Long.parseLong(rangeSplitted[1]); + } + if (rangeEnd + 1 > fileSize) { + return Response.status( + Response.Status.REQUESTED_RANGE_NOT_SATISFIABLE).build(); + } InputStream is = Channels.newInputStream(channel); is.skip(rangeStart); response = Response.ok(path.toFile()); - response.entity(is); - response.header("Content-Length", Long.toString(fileSize - rangeStart)); + response.entity(is.readNBytes((int) (rangeEnd + 1 - rangeStart))); + response.header("Content-Length", + Long.toString(rangeEnd + 1 - rangeStart)); response.status(Response.Status.PARTIAL_CONTENT); response.header("Content-Range", - "bytes " + rangeStart + "-" + (fileSize - 1) + "/" + fileSize); + "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize); } else { response = Response.ok(path.toFile()); response.header("Content-Length", Long.toString(fileSize)); } + response.header("Accept-Ranges", "bytes"); response.header("Content-Disposition", "inline; filename=\"" + fileInfo.filename + "\""); response.header("Content-Type", FileHelper.getMimeType(fileInfo.filename)); diff --git a/src/main/resources/templates/directory-index.html b/src/main/resources/templates/directory-index.html index 3124167..50029a6 100644 --- a/src/main/resources/templates/directory-index.html +++ b/src/main/resources/templates/directory-index.html @@ -4,16 +4,25 @@ + {info.filename}

{info.filename}

- {#if !info.filename is '/'} - back - - viewer - + {#if info.filename ne '/'} + + {#else} +   {/if} + + {#if info.playlist} + + {/if} + + {#if username and user_url} + | {username} + {/if} +
{#each info.content}