1
0
mirror of https://github.com/openeggbert/youtubedl-frontend.git synced 2025-03-14 21:23:27 +01:00

Refactoring II

This commit is contained in:
Robert Vokac 2024-07-22 19:30:44 +02:00
parent 76291ea038
commit c45092a52c
No known key found for this signature in database
GPG Key ID: C459E1E4B4A986BB
9 changed files with 354 additions and 331 deletions

View File

@ -192,7 +192,7 @@
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
<!-- <dependency>
<!-- <dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId>
<version>1.4.0</version>

View File

@ -25,10 +25,10 @@ import lombok.Data;
*
* @author robertvokac
*/
@Data
@AllArgsConstructor
public class Arg {
private ArgType argType;
private String value;
}

View File

@ -36,9 +36,10 @@ public enum ArgType {
private String name;
@Getter
private String defaultValue;
ArgType(String name, String defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
}

View File

@ -46,13 +46,12 @@ public class Main {
public static int THUMBNAIL_WIDTH = 250;
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("youtubedlfrontend - HTML generator\n");
if (args.length < 1) {
//System.err.println("At least one argument is expected, but the count of arguments is: " + args.length + ".");
String argsS = "/rv/blupi/archivebox --_video UDpsz1yIwiw --always-generate-metadata 0 "
String argsS = "/rv/blupi/archivebox --_video UDpsz1yIwiw --always-generate-metadata 0"
+ " --always-generate-html-files 0 --videos-per-row 4 --thumbnail-links-to-youtube 1"
+ " --thumbnail-as-base64 1";
args = argsS.split(" ");
@ -64,67 +63,8 @@ public class Main {
File archiveBoxRootDirectory = new File(workingDirectory);
File archiveBoxArchiveDirectory = new File(archiveBoxRootDirectory, "archive");
int i = 0;
List<YoutubeVideo> youtubeVideos = new ArrayList<>();
for (File snapshotDirectory : archiveBoxArchiveDirectory.listFiles()) {
//if(i> 10)break;
File mediaDirectory = new File(snapshotDirectory, "media");
if (!mediaDirectory.exists()) {
//nothing to do
continue;
}
YoutubeVideo youtubeVideo = new YoutubeVideo(mediaDirectory, argsInstance.getBoolean(ArgType.ALWAYS_GENERATE_METADATA).get(), argsInstance.getString(ArgType.VIDEO).orElse(""));
if (argsInstance.getString(ArgType.VIDEO).isPresent() && !argsInstance.getString(ArgType.VIDEO).equals(youtubeVideo.getId())) {
continue;
}
if (argsInstance.getString(ArgType.CHANNEL).isPresent() && !argsInstance.getString(ArgType.CHANNEL).equals(youtubeVideo.getChannelId())) {
continue;
}
i++;
System.out.println("\n\nFound video #" + i);
for (File f : new File(archiveBoxArchiveDirectory + "/" + youtubeVideo.getSnapshot() + "/media/" + youtubeVideo.getVideoFileName()).getParentFile().listFiles()) {
if (f.getName().endsWith(".webm")) {
//mkv file was manually converted to webm
youtubeVideo.setVideoFileName(f.getName());
break;
}
}
System.out.println("id = " + youtubeVideo.getId());
System.out.println("snapshot = " + youtubeVideo.getSnapshot());
System.out.println("title = " + youtubeVideo.getTitle());
System.out.println("videoFileName = " + youtubeVideo.getVideoFileName());
System.out.println("videoFileSizeInBytes = " + youtubeVideo.getVideoFileSizeInBytes());
System.out.println("videoFileSha512HashSum = " + youtubeVideo.getVideoFileSha512HashSum());
System.out.println("videoDuration = " + youtubeVideo.getVideoDuration());
System.out.println("channelName = " + youtubeVideo.getChannelName());
System.out.println("channelUrl = " + youtubeVideo.getChannelUrl());
System.out.println("uploadDate = " + youtubeVideo.getUploadDate());
System.out.println("description = " + youtubeVideo.getDescription());
System.out.println("thumbnail = " + youtubeVideo.getThumbnail());
System.out.println("miniThumbnail = " + youtubeVideo.getMiniThumbnail());
System.out.println("comments = " + youtubeVideo.getComments());
youtubeVideos.add(youtubeVideo);
}
Collections.sort(youtubeVideos);
YoutubeVideo previousVideo = null;
YoutubeVideo nextVideo = null;
YoutubeVideo currentVideo = null;
for (int j = 0; j < youtubeVideos.size(); j++) {
previousVideo = currentVideo;
currentVideo = youtubeVideos.get(j);
if (j < (youtubeVideos.size() - 1)) {
nextVideo = youtubeVideos.get(j + 1);
}
if (previousVideo != null) {
currentVideo.setPreviousVideoId(previousVideo.getId());
}
if (nextVideo != null) {
currentVideo.setNextVideoId(nextVideo.getId());
}
}
List<YoutubeVideo> youtubeVideos = YoutubeVideo.loadYoutubeVideos(archiveBoxArchiveDirectory, argsInstance);
Map<String, String> channelUrls = new HashMap<>();
List<String> channels = new ArrayList<>();
youtubeVideos.stream().forEach(c -> {
@ -136,65 +76,85 @@ public class Main {
}
});
StringBuilder sb = new StringBuilder();
File videosHtmlFile = new File(archiveBoxRootDirectory, "videos.html");
File videosDirectory = new File(archiveBoxRootDirectory, "videos");
File channelsDirectory = new File(archiveBoxRootDirectory, "channels");
if (!videosDirectory.exists()) {
videosDirectory.mkdir();
}
sb.append("""
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="favicon.ico" sizes="16x16">
<title>Youtube videos</title>
<!-- Generated by: https://code.nanoboot.org/nanoboot/youtubedl-frontend -->
<style>
body {padding:20px;}
* {
font-family:Arial;
}
.videos {
/*box-sizing: border-box;*/
}
.box {
/*float: left;
width: 20.0%;*/
padding: 10px;
}
</style>
</head>
<body>
""");
NumberFormat formatter = new DecimalFormat("#0.00");
channels.forEach(c -> {
sb.append("<h1>").append(c).append("</h1>\n");
sb.append("<div style=\"max-width:").append((Main.THUMBNAIL_WIDTH + 20) * argsInstance.getInteger(ArgType.VIDEOS_PER_ROW).get()).append("px\"><a href =\"").append(channelUrls.get(c)).append("\">").append(channelUrls.get(c)).append("</a><div class=\"videos\">");
if (!channelsDirectory.exists()) {
channelsDirectory.mkdir();
}
channels.stream().forEach(c -> {
StringBuilder oneChannelStringBuilder = createChannelHtml(c, channels, argsInstance, channelUrls, youtubeVideos, archiveBoxRootDirectory, videosDirectory, archiveBoxArchiveDirectory);
Utils.writeTextToFile(oneChannelStringBuilder.toString(), new File(channelsDirectory, channelUrls.get(c).split("/channel/")[1] + ".html"));
});
StringBuilder oneChannelStringBuilder = createChannelHtml(null, channels, argsInstance, channelUrls, youtubeVideos, archiveBoxRootDirectory, videosDirectory, archiveBoxArchiveDirectory);
Utils.writeTextToFile(oneChannelStringBuilder.toString(), videosHtmlFile);
System.out.println("[Warning] Snapshots without videos:");
YoutubeVideo.missingYoutubeVideos.forEach(s -> System.out.println(s));
}
private static StringBuilder createChannelHtml(String wantedChannelName, List<String> channels, Args argsInstance, Map<String, String> channelUrls, List<YoutubeVideo> youtubeVideos, File archiveBoxRootDirectory, File videosDirectory, File archiveBoxArchiveDirectory) {
StringBuilder oneChannelStringBuilder = new StringBuilder();
oneChannelStringBuilder.append("""
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="favicon.ico" sizes="16x16">
<title>Youtube videos</title>
<!-- Generated by: https://code.nanoboot.org/nanoboot/youtubedl-frontend -->
<style>
body {padding:20px;}
* {
font-family:Arial;
}
.videos {
/*box-sizing: border-box;*/
}
.box {
/*float: left;
width: 20.0%;*/
padding: 10px;
}
</style>
</head>
<body>
""");
channels.stream().filter(c -> wantedChannelName == null ? true : c.equals(wantedChannelName)).forEach(channel -> {
oneChannelStringBuilder.append("<h1>").append(channel).append("</h1>\n");
oneChannelStringBuilder.append("<div style=\"max-width:").append((Main.THUMBNAIL_WIDTH + 20) * argsInstance.getInteger(ArgType.VIDEOS_PER_ROW).get()).append("px\">");
oneChannelStringBuilder.append("<a target=\"_blank\" href =\"channels/").append(channelUrls.get(channel).split("/channel/")[1]).append(".html").append("\">").append("Videos").append("</a>");
oneChannelStringBuilder.append("&nbsp;&nbsp;&nbsp;( <a href =\"").append(channelUrls.get(channel)).append("\">").append(channelUrls.get(channel)).append("</a> )");
if(wantedChannelName != null) {
oneChannelStringBuilder.append("<div class=\"videos\">");
iii = 0;
internalStaticVariableVideoNumberPerRow = 0;
sb.append("<table>\n");
youtubeVideos.stream().filter(v -> c.equals(v.getChannelName())).forEach(youtubeVideo -> {
oneChannelStringBuilder.append("<table>\n");
youtubeVideos.stream().filter(v -> channel.equals(v.getChannelName())).forEach(youtubeVideo -> {
iii++;
if (internalStaticVariableVideoNumberPerRow == 0) {
sb.append("<tr>");
oneChannelStringBuilder.append("<tr>");
}
internalStaticVariableVideoNumberPerRow++;
sb.append("<td><div class=\"box\"><table style=\"margin:5px;max-width:")
oneChannelStringBuilder.append("<td><div class=\"box\"><table style=\"margin:5px;max-width:")
.append(THUMBNAIL_WIDTH)
.append("px;\">\n<tr><td><a href=\"");
if (argsInstance.getBoolean(ArgType.THUMBNAIL_LINKS_TO_YOUTUBE).get()) {
sb.append("https://www.youtube.com/watch?v=").append(youtubeVideo.getId());
oneChannelStringBuilder.append("https://www.youtube.com/watch?v=").append(youtubeVideo.getId());
} else {
sb.append("videos/" + youtubeVideo.getId() + ".html");
oneChannelStringBuilder.append("videos/" + youtubeVideo.getId() + ".html");
}
sb.append("\" target=\"_blank\"><img src=\"");
String thumbnailPath = new StringBuilder()
.append("archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/mini-thumbnail.")
.append(youtubeVideo.getMiniThumbnailFormat()).toString();
oneChannelStringBuilder.append("\" target=\"_blank\"><img src=\"");
String thumbnailPath = new StringBuilder()
.append("archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/mini-thumbnail.")
.append(youtubeVideo.getMiniThumbnailFormat()).toString();
if (argsInstance.getBoolean(ArgType.THUMBNAIL_AS_BASE64).get()) {
try {
byte[] bytes = Files.readAllBytes(new File(archiveBoxRootDirectory + "/" + thumbnailPath).toPath());
@ -205,208 +165,55 @@ public class Main {
} catch (Exception e) {
//bytes = Utils.resizeImage(bais, 125, (int) (9d / 16d * 125d), "webp");
}
String bytesS = "data:image/jpg;base64, " + org.nanoboot.powerframework.io.bit.base64.Base64Coder.encode(bytes);
sb.append(bytesS);
oneChannelStringBuilder.append(bytesS);
} catch (IOException ex) {
throw new YoutubedlFrontendException(ex.getMessage());
}
} else {
sb.append(thumbnailPath);
oneChannelStringBuilder.append(thumbnailPath);
}
sb.append("\" width=\"")
oneChannelStringBuilder.append("\" width=\"")
.append(THUMBNAIL_WIDTH)
.append("\"></a></td></tr>\n");
sb.append("<tr><td><b style=\"font-size:90%;\">").append(youtubeVideo.getTitle()).append("</b></td></tr>\n");
oneChannelStringBuilder.append("<tr><td><b style=\"font-size:90%;\">").append(youtubeVideo.getTitle()).append("</b></td></tr>\n");
String uploadDate = youtubeVideo.getUploadDate();
uploadDate = uploadDate.substring(0, 4) + "-" + uploadDate.substring(4, 6) + "-" + uploadDate.substring(6, 8);
sb.append("<tr><td style=\"font-size:80%;color:grey;\">").append(uploadDate).append(" •︎ ").append(youtubeVideo.getVideoDuration())
oneChannelStringBuilder.append("<tr><td style=\"font-size:80%;color:grey;\">").append(uploadDate).append(" •︎ ").append(youtubeVideo.getVideoDuration())
.append(" •︎ ")
.append("#").append(iii)
.append("</td></tr>\n");
youtubeVideo.setNumber(iii);
sb.append("</table></div></td>\n");
oneChannelStringBuilder.append("</table></div></td>\n");
if (internalStaticVariableVideoNumberPerRow == argsInstance.getInteger(ArgType.VIDEOS_PER_ROW).get()) {
sb.append("<tr>");
oneChannelStringBuilder.append("<tr>");
internalStaticVariableVideoNumberPerRow = 0;
}
File videoHtmlFile = new File(videosDirectory, youtubeVideo.getId() + ".html");
if (!videoHtmlFile.exists() || argsInstance.getBoolean(ArgType.ALWAYS_GENERATE_HTML_FILES).get()) {
{
StringBuilder videoHtml = new StringBuilder("""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/x-icon" href="../favicon.ico" sizes="16x16">
<title>"""
+ youtubeVideo.getTitle()
+ """
</title>
<style>
body {padding:20px;}
* {
font-family:Arial;
}
</style>
</head>
<body>
"""
);
String finalUrl = "https://www.youtube.com/watch?v=" + youtubeVideo.getId();
videoHtml.append("<input type=\"text\" id=\"youtube_url\" name=\"youtube_url\" size=\"60\" width=\"60\" style=\"margint-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"" + finalUrl + "\"><br>\n<br>\n");
videoHtml.append("<a target=\"_blank\" href=\"").append(finalUrl).append("\">");
videoHtml.append(finalUrl).append("</a>").append("<br>\n");
String videoLocalUrl = "";
try {
videoLocalUrl = "file:///" + archiveBoxRootDirectory.getAbsolutePath() + "/archive/" + youtubeVideo.getSnapshot() + "/media/" + URLEncoder.encode(youtubeVideo.getVideoFileName(), StandardCharsets.UTF_8.toString()).replace("+", "%20");
} catch (UnsupportedEncodingException ex) {
throw new YoutubedlFrontendException(ex.getMessage());
}
if (!youtubeVideo.getVideoFileName().endsWith(".mkv")) {
//try {
videoHtml.append("<video src=\"");
videoHtml.append("../archive/").append(youtubeVideo.getSnapshot()).append("/media/").append(// URLEncoder.encode(
youtubeVideo.getVideoFileName());
videoHtml.append("""
" controls height=\"500px\">
Your browser does not support the video tag.
</video><br>
""");
// } catch (UnsupportedEncodingException ex) {
// throw new YoutubedlFrontendException(ex.getMessage());
// }
}
else
{
videoHtml.append("<a target=\"_blank\" href=\"").append(videoLocalUrl).append("\">");
videoHtml.append("<img style=\"margin:10px;height:500px;\" src=\"../archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/thumbnail.")
.append(youtubeVideo.getThumbnailFormat())
.append("\"></a><br>\n");
}
videoHtml.append("<span style=\"font-size:160%;font-weight:bold;\">").append(youtubeVideo.getTitle()).append("</span>");
videoHtml.append("<br>\n<br>\n");
videoHtml.append("#").append(youtubeVideo.getNumber()).append("&nbsp;&nbsp;&nbsp;");
if (youtubeVideo.getPreviousVideoId() != null) {
// videoHtml.append("<a href=\"./").append(youtubeVideo.getPreviousVideoId()).append(".html\">");
videoHtml.append("<button style=\"font-size:150%;\" onclick=\"window.location ='").append("./").append(youtubeVideo.getPreviousVideoId()).append(".html'\">");
//<button class="link" onclick="alert(1)">Click</button>
}
videoHtml.append("Back");
if (youtubeVideo.getPreviousVideoId() != null) {
//videoHtml.append("</a>");
videoHtml.append("</button>");
}
videoHtml.append("&nbsp;&nbsp;&nbsp;");
if (youtubeVideo.getNextVideoId() != null) {
//videoHtml.append("<a href=\"./").append(youtubeVideo.getNextVideoId()).append(".html\">");
videoHtml.append("<button style=\"font-size:150%;\" onclick=\"window.location ='").append("./").append(youtubeVideo.getNextVideoId()).append(".html'\">");
}
videoHtml.append("Next");
if (youtubeVideo.getNextVideoId() != null) {
//videoHtml.append("</a>");
videoHtml.append("</button>");
}
videoHtml.append(" ");
videoHtml
.append("<br><br><a href=\"../archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/")
.append(youtubeVideo.getVideoFileName())
.append("\">Download</a> ");
videoHtml.append(formatter.format(((double) youtubeVideo.getVideoFileSizeInBytes()) / 1024d / 1024d)).append(" MB ");
if (youtubeVideo.getVideoFileName().endsWith(".mkv")) {
String v = youtubeVideo.getVideoFileName().replaceAll(" ", "\\\\ ");
v = v.replace("(", "\\(");
v = v.replace(")", "\\)");
var vWebm = v.substring(0, v.length() - 3) + "webm";
videoHtml.append("<input type=\"text\" id=\"archiveBoxArchiveDirectory\" name=\"archiveBoxArchiveDirectory\" size=\"100\" width=\"100\" style=\"margin-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"");
videoHtml.append("cd ").append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media/");
videoHtml.append(" && ffmpeg -i ").append(v).append(" -preset slow -crf 18 ").append(vWebm) ;
videoHtml.append("\"><br>");
} else {
videoHtml.append("<input type=\"text\" id=\"archiveBoxArchiveDirectory\" name=\"archiveBoxArchiveDirectory\" size=\"100\" width=\"100\" style=\"margin-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"");
videoHtml.append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media/");
videoHtml.append("\"><br>");
}
videoHtml.append("<a target=\"_blank\" href=\"file://").append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media\">Directory</a>").append("<br>");
videoHtml.append("<br>\n<br>\n");
videoHtml.append("<br>\n");
videoHtml.append("<pre style=\"white-space: pre-wrap; border:1px solid black;max-width:600px;padding:10px;min-height:50px;\">");
videoHtml.append(youtubeVideo.getDescription().isBlank() ? "No description" : youtubeVideo.getDescription());
videoHtml.append("</pre>");
videoHtml.append("<h2>Comments</h2>");
youtubeVideo.getComments().forEach(co -> {
// private String id, parentId, text, author;
// private int timestamp;
videoHtml.append("<div style=\"margin-left:")
.append(co.dotCount() * 50)
.append("px;\">");
videoHtml.append("<h3>").append(co.getAuthor()).append("</h3>");
videoHtml.append("<span style=\"color:grey;font-size:80%;\">")
.append(Utils.DATE_FORMAT.format(new Date(co.getTimestamp() * 1000))).append("</span><br>\n");
videoHtml.append("<span style=\"color:grey;font-size:80%;\">").append(co.getId()).append(" ")
.append(co.getParentId()).append("</span><br>\n");
videoHtml.append("<pre style=\"white-space: pre-wrap;border:1px solid black;max-width:600px;padding:10px;min-height:50px;\">").append(co.getText()).append("</pre>");
videoHtml.append("</div>");
});
// private String id;
//
// private String title;
// private String videoFileName = "";
// private long videoFileSizeInBytes = 0;
// private String videoFileSha512HashSum = "";
// private String videoDuration = "";
// private String channelName;
// private String channelUrl;
// private String channelId;
// private String uploadDate;
// private String description;
// private String thumbnail;
// private List<YoutubeComment> comments = new ArrayList<>();
videoHtml.append("</body></html>");
String singleVideo = videoHtml.toString();
//singleVideo.replace("<br>\n", "<br>\n\n");
String singleVideo = new YoutubeVideoHtml(youtubeVideo, archiveBoxRootDirectory, archiveBoxArchiveDirectory).toString();
Utils.writeTextToFile(singleVideo, videoHtmlFile);
}
}
});
if (internalStaticVariableVideoNumberPerRow < argsInstance.getInteger(ArgType.VIDEOS_PER_ROW).get()) {
sb.append("<tr>");
oneChannelStringBuilder.append("<tr>");
}
sb.append("</table>\n");
sb.append("</div></div>");
oneChannelStringBuilder.append("</table>\n");
oneChannelStringBuilder.append("</div>");
}
oneChannelStringBuilder.append("</div>");
});
sb.append("""
</body>
</html>
""");
Utils.writeTextToFile(sb.toString(), videosHtmlFile);
System.out.println("[Warning] Snapshots without videos:");
YoutubeVideo.missingYoutubeVideos.forEach(s -> System.out.println(s));
oneChannelStringBuilder.append("""
</body>
</html>
""");
return oneChannelStringBuilder;
}
}

View File

@ -24,7 +24,6 @@ import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@ -35,6 +34,8 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.imageio.ImageIO;
@ -48,6 +49,7 @@ public class Utils {
private static final String UNDERSCORE = "_";
public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static final NumberFormat TWO_DECIMAL_POINTS_FORMATTER = new DecimalFormat("#0.00");
private Utils() {
//Not meant to be instantiated.
}
@ -187,6 +189,7 @@ public class Utils {
throw new YoutubedlFrontendException("Could not create boolean from String: " + s);
}
}
public static byte[] resizeImage(InputStream inputStream, int width, int height, String formatName) throws IOException {
BufferedImage sourceImage = ImageIO.read(inputStream);
Image thumbnail = sourceImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);

View File

@ -43,13 +43,15 @@ public class YoutubeComment implements Comparable<YoutubeComment> {
author = jsonObject.getString("author");
timestamp = jsonObject.getInt("timestamp");
}
public static List<YoutubeComment> sort(List<YoutubeComment> list) {
List<YoutubeComment> root = getChildren(list, "root");
Collections.sort(root);
return list;
//return sort(list, new ArrayList<>(), "root");
}
private static List<YoutubeComment> getChildren(List<YoutubeComment> all, String parentId) {
final List<YoutubeComment> children = all.stream().filter(c -> c.getParentId().equals(parentId)).sorted().toList();
List<YoutubeComment> result = new ArrayList<>();
@ -59,7 +61,7 @@ public class YoutubeComment implements Comparable<YoutubeComment> {
});
return result;
}
@Override
public int compareTo(YoutubeComment o) {
//if(this.timestamp != o.timestamp) {

View File

@ -20,6 +20,7 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
@ -63,7 +64,7 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
public YoutubeVideo(File mediaDirectory, boolean argAlwaysGenerateMetadata, String argVideo) throws InterruptedException, IOException {
File metadataFile = new File(mediaDirectory, "metadata");
if (argAlwaysGenerateMetadata && metadataFile.exists()) {
if (!argAlwaysGenerateMetadata && metadataFile.exists()) {
YoutubeVideo yv = new YoutubeVideo();
//new ObjectMapper().readValue(Utils.readTextFromFile(metadataFile), YoutubeVideo.class);
@ -131,7 +132,7 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
continue;
} else {
int width = o.getInt("width");
if (width < (((double)Main.THUMBNAIL_WIDTH) * 0.8d)) {
if (width < (((double) Main.THUMBNAIL_WIDTH) * 0.8d)) {
continue;
}
miniThumbnail = o.getString("url");
@ -147,7 +148,6 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
// new File(mediaDirectory, "mini-thumbnail.jpg").delete();
// new File(mediaDirectory, "thumbnail.webp").delete();
// new File(mediaDirectory, "mini-thumbnail.webp").delete();
if (thumbnail != null) {
if (!thumbnailFile.exists()) {
try (BufferedInputStream in = new BufferedInputStream(new URL(thumbnail).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(thumbnailFile.getAbsolutePath())) {
@ -171,35 +171,7 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
System.out.println(e.getMessage());
}
}
// for (String s : ImageIO.getReaderFormatNames()) {
// System.out.println(s);
// }
//if(!miniThumbnailFile.exists()) {
//String miniThumbnailFileAbsolutePath = miniThumbnailFile.getAbsolutePath();
// String formatName = miniThumbnailFileAbsolutePath.substring(miniThumbnailFileAbsolutePath
// .lastIndexOf(".") + 1);
// BufferedImage inputImage = ImageIO.read(thumbnailFile);
// if(inputImage == null) {
//
// }
// int thumbnailWidth = inputImage.getWidth();
// int thumbnailHeight = inputImage.getHeight();
// double heightWidthRatio = ((double) thumbnailHeight) / ((double) thumbnailWidth);
// int miniThumbnailWidth = Main.THUMBNAIL_WIDTH;
// int miniThumbnailHeight = (int) (heightWidthRatio * ((double) Main.THUMBNAIL_WIDTH));
//
// BufferedImage outputImage = new BufferedImage(miniThumbnailWidth,
// miniThumbnailHeight, inputImage.getType());
//
// Graphics2D g2d = outputImage.createGraphics();
// g2d.drawImage(inputImage, 0, 0, miniThumbnailWidth, miniThumbnailHeight, null);
// g2d.dispose();
//
//
//
// ImageIO.write(outputImage, formatName, new File(miniThumbnailFileAbsolutePath));
//}
}
//
Optional<File> descriptionFile = files.stream().filter(f -> f.getName().endsWith(".description")).findFirst();
@ -211,14 +183,9 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
.filter(f
-> (f.getName().endsWith("." + ext))
|| (f.getName().endsWith(".mp4"))
|| (f.getName().endsWith(".mkv"))|| (f.getName().endsWith(".webm"))
|| (f.getName().endsWith(".mkv")) || (f.getName().endsWith(".webm"))
)
// .filter(
// f -> !f.getName().endsWith(".description")
// && !f.getName().endsWith(".json")
// && !f.getName().equals("metadata")
// && !f.getName().endsWith(thumbnail)
// )
.findFirst();
snapshot = mediaDirectory.getParentFile().getName();
@ -303,23 +270,24 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
public String getMiniThumbnailFormat() {
return getExtensionFromUrl(miniThumbnail);
}
private String getExtensionFromUrl(String url) {
String result = url.substring(url
String result = url.substring(url
.lastIndexOf(".") + 1);
int questionMarkIndex = 0;
for(int i = 0;i<result.length();i++) {
for (int i = 0; i < result.length(); i++) {
char ch = result.charAt(i);
if(ch != '?') {
if (ch != '?') {
continue;
} else {
questionMarkIndex = i;
}
}
if(questionMarkIndex > 0) {
if (questionMarkIndex > 0) {
result = result.substring(0, questionMarkIndex);
}
return result;
}
/**
@ -359,4 +327,69 @@ public class YoutubeVideo implements Comparable<YoutubeVideo> {
}
}
public static List<YoutubeVideo> loadYoutubeVideos(File archiveBoxArchiveDirectory, Args argsInstance) throws IOException, InterruptedException {
int i = 0;
List<YoutubeVideo> youtubeVideos = new ArrayList<>();
for (File snapshotDirectory : archiveBoxArchiveDirectory.listFiles()) {
//if(i> 40)break;//only for tests
File mediaDirectory = new File(snapshotDirectory, "media");
if (!mediaDirectory.exists()) {
//nothing to do
continue;
}
YoutubeVideo youtubeVideo = new YoutubeVideo(mediaDirectory, argsInstance.getBoolean(ArgType.ALWAYS_GENERATE_METADATA).get(), argsInstance.getString(ArgType.VIDEO).orElse(""));
if (argsInstance.getString(ArgType.VIDEO).isPresent() && !argsInstance.getString(ArgType.VIDEO).equals(youtubeVideo.getId())) {
continue;
}
if (argsInstance.getString(ArgType.CHANNEL).isPresent() && !argsInstance.getString(ArgType.CHANNEL).equals(youtubeVideo.getChannelId())) {
continue;
}
i++;
System.out.println("\n\nFound video #" + i);
for (File f : new File(archiveBoxArchiveDirectory + "/" + youtubeVideo.getSnapshot() + "/media/" + youtubeVideo.getVideoFileName()).getParentFile().listFiles()) {
if (f.getName().endsWith(".webm")) {
//mkv file was manually converted to webm
youtubeVideo.setVideoFileName(f.getName());
break;
}
}
System.out.println("id = " + youtubeVideo.getId());
System.out.println("snapshot = " + youtubeVideo.getSnapshot());
System.out.println("title = " + youtubeVideo.getTitle());
System.out.println("videoFileName = " + youtubeVideo.getVideoFileName());
System.out.println("videoFileSizeInBytes = " + youtubeVideo.getVideoFileSizeInBytes());
System.out.println("videoFileSha512HashSum = " + youtubeVideo.getVideoFileSha512HashSum());
System.out.println("videoDuration = " + youtubeVideo.getVideoDuration());
System.out.println("channelName = " + youtubeVideo.getChannelName());
System.out.println("channelUrl = " + youtubeVideo.getChannelUrl());
System.out.println("uploadDate = " + youtubeVideo.getUploadDate());
System.out.println("description = " + youtubeVideo.getDescription());
System.out.println("thumbnail = " + youtubeVideo.getThumbnail());
System.out.println("miniThumbnail = " + youtubeVideo.getMiniThumbnail());
System.out.println("comments = " + youtubeVideo.getComments());
youtubeVideos.add(youtubeVideo);
}
Collections.sort(youtubeVideos);
YoutubeVideo previousVideo = null;
YoutubeVideo nextVideo = null;
YoutubeVideo currentVideo = null;
for (int j = 0; j < youtubeVideos.size(); j++) {
previousVideo = currentVideo;
currentVideo = youtubeVideos.get(j);
if (j < (youtubeVideos.size() - 1)) {
nextVideo = youtubeVideos.get(j + 1);
}
if (previousVideo != null) {
currentVideo.setPreviousVideoId(previousVideo.getId());
}
if (nextVideo != null) {
currentVideo.setNextVideoId(nextVideo.getId());
}
}
return youtubeVideos;
}
}

View File

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// youtubedl-frontend: Tool generating html pages for Archive Box.
// Copyright (C) 2024 the original author or authors.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License only.
//
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
///////////////////////////////////////////////////////////////////////////////////////////////
package org.nanoboot.youtubedlfrontend;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.util.Date;
import lombok.Getter;
/**
*
* @author robertvokac
*/
public class YoutubeVideoHtml {
@Getter
private String singleVideo;
public YoutubeVideoHtml(YoutubeVideo youtubeVideo, File archiveBoxRootDirectory, File archiveBoxArchiveDirectory) {
StringBuilder videoHtml = new StringBuilder("""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/x-icon" href="../favicon.ico" sizes="16x16">
<title>"""
+ youtubeVideo.getTitle()
+ """
</title>
<style>
body {padding:20px;}
* {
font-family:Arial;
}
</style>
</head>
<body>
"""
);
String finalUrl = "https://www.youtube.com/watch?v=" + youtubeVideo.getId();
videoHtml.append("<input type=\"text\" id=\"youtube_url\" name=\"youtube_url\" size=\"60\" width=\"60\" style=\"margint-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"" + finalUrl + "\"><br>\n<br>\n");
videoHtml.append("<a target=\"_blank\" href=\"").append(finalUrl).append("\">");
videoHtml.append(finalUrl).append("</a>").append("<br>\n");
String videoLocalUrl = "";
try {
videoLocalUrl = "file:///" + archiveBoxRootDirectory.getAbsolutePath() + "/archive/" + youtubeVideo.getSnapshot() + "/media/" + URLEncoder.encode(youtubeVideo.getVideoFileName(), StandardCharsets.UTF_8.toString()).replace("+", "%20");
} catch (UnsupportedEncodingException ex) {
throw new YoutubedlFrontendException(ex.getMessage());
}
if (!youtubeVideo.getVideoFileName().endsWith(".mkv")) {
//try {
videoHtml.append("<video src=\"");
videoHtml.append("../archive/").append(youtubeVideo.getSnapshot()).append("/media/").append(// URLEncoder.encode(
youtubeVideo.getVideoFileName());
videoHtml.append("""
" controls height=\"500px\">
Your browser does not support the video tag.
</video><br>
""");
// } catch (UnsupportedEncodingException ex) {
// throw new YoutubedlFrontendException(ex.getMessage());
// }
} else {
videoHtml.append("<a target=\"_blank\" href=\"").append(videoLocalUrl).append("\">");
videoHtml.append("<img style=\"margin:10px;height:500px;\" src=\"../archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/thumbnail.")
.append(youtubeVideo.getThumbnailFormat())
.append("\"></a><br>\n");
}
videoHtml.append("<span style=\"font-size:160%;font-weight:bold;\">").append(youtubeVideo.getTitle()).append("</span>");
videoHtml.append("<br>\n<br>\n");
videoHtml.append("#").append(youtubeVideo.getNumber()).append("&nbsp;&nbsp;&nbsp;");
if (youtubeVideo.getPreviousVideoId() != null) {
// videoHtml.append("<a href=\"./").append(youtubeVideo.getPreviousVideoId()).append(".html\">");
videoHtml.append("<button style=\"font-size:150%;\" onclick=\"window.location ='").append("./").append(youtubeVideo.getPreviousVideoId()).append(".html'\">");
//<button class="link" onclick="alert(1)">Click</button>
}
videoHtml.append("Back");
if (youtubeVideo.getPreviousVideoId() != null) {
//videoHtml.append("</a>");
videoHtml.append("</button>");
}
videoHtml.append("&nbsp;&nbsp;&nbsp;");
if (youtubeVideo.getNextVideoId() != null) {
//videoHtml.append("<a href=\"./").append(youtubeVideo.getNextVideoId()).append(".html\">");
videoHtml.append("<button style=\"font-size:150%;\" onclick=\"window.location ='").append("./").append(youtubeVideo.getNextVideoId()).append(".html'\">");
}
videoHtml.append("Next");
if (youtubeVideo.getNextVideoId() != null) {
//videoHtml.append("</a>");
videoHtml.append("</button>");
}
videoHtml.append(" ");
videoHtml
.append("<br><br><a href=\"../archive/")
.append(youtubeVideo.getSnapshot())
.append("/media/")
.append(youtubeVideo.getVideoFileName())
.append("\">Download</a> ");
videoHtml.append(Utils.TWO_DECIMAL_POINTS_FORMATTER.format(((double) youtubeVideo.getVideoFileSizeInBytes()) / 1024d / 1024d)).append(" MB ");
if (youtubeVideo.getVideoFileName().endsWith(".mkv")) {
String v = youtubeVideo.getVideoFileName().replaceAll(" ", "\\\\ ");
v = v.replace("(", "\\(");
v = v.replace(")", "\\)");
var vWebm = v.substring(0, v.length() - 3) + "webm";
videoHtml.append("<input type=\"text\" id=\"archiveBoxArchiveDirectory\" name=\"archiveBoxArchiveDirectory\" size=\"100\" width=\"100\" style=\"margin-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"");
videoHtml.append("cd ").append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media/");
videoHtml.append(" && ffmpeg -i ").append(v).append(" -preset slow -crf 18 ").append(vWebm);
videoHtml.append("\"><br>");
} else {
videoHtml.append("<input type=\"text\" id=\"archiveBoxArchiveDirectory\" name=\"archiveBoxArchiveDirectory\" size=\"100\" width=\"100\" style=\"margin-bottom:20px;margin-right:10px;font-size:110%;padding:5px;\" value=\"");
videoHtml.append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media/");
videoHtml.append("\"><br>");
}
videoHtml.append("<a target=\"_blank\" href=\"file://").append(archiveBoxArchiveDirectory).append("/").append(youtubeVideo.getSnapshot()).append("/media\">Directory</a>").append("<br>");
videoHtml.append("<br>\n<br>\n");
videoHtml.append("<br>\n");
videoHtml.append("<pre style=\"white-space: pre-wrap; border:1px solid black;max-width:600px;padding:10px;min-height:50px;\">");
videoHtml.append(youtubeVideo.getDescription().isBlank() ? "No description" : youtubeVideo.getDescription());
videoHtml.append("</pre>");
videoHtml.append("<h2>Comments</h2>");
youtubeVideo.getComments().forEach(co -> {
// private String id, parentId, text, author;
// private int timestamp;
videoHtml.append("<div style=\"margin-left:")
.append(co.dotCount() * 50)
.append("px;\">");
videoHtml.append("<h3>").append(co.getAuthor()).append("</h3>");
videoHtml.append("<span style=\"color:grey;font-size:80%;\">")
.append(Utils.DATE_FORMAT.format(new Date(co.getTimestamp() * 1000))).append("</span><br>\n");
videoHtml.append("<span style=\"color:grey;font-size:80%;\">").append(co.getId()).append(" ")
.append(co.getParentId()).append("</span><br>\n");
videoHtml.append("<pre style=\"white-space: pre-wrap;border:1px solid black;max-width:600px;padding:10px;min-height:50px;\">").append(co.getText()).append("</pre>");
videoHtml.append("</div>");
});
videoHtml.append("</body></html>");
singleVideo = videoHtml.toString();
}
@Override
public String toString() {
return this.singleVideo;
}
}

View File

@ -16,7 +16,6 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
///////////////////////////////////////////////////////////////////////////////////////////////
package org.nanoboot.youtubedlfrontend;
/**
@ -32,5 +31,5 @@ public class YoutubedlFrontendException extends RuntimeException {
public YoutubedlFrontendException(String msg, Exception e) {
super(msg, e);
}
}
}