reviw 0.13.2 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.cjs +72 -5
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -149,6 +149,13 @@ marked.use({
|
|
|
149
149
|
}
|
|
150
150
|
var titleAttr = title ? ' title="' + escapeHtmlForXss(title) + '"' : "";
|
|
151
151
|
var altAttr = text ? ' alt="' + escapeHtmlForXss(text) + '"' : "";
|
|
152
|
+
// Check if this is a video file - wrap in <a> tag for video player functionality
|
|
153
|
+
var videoExtensions = /\.(mp4|mov|webm|avi|mkv|m4v|ogv)$/i;
|
|
154
|
+
if (videoExtensions.test(href)) {
|
|
155
|
+
// For videos, return a clickable link with video icon
|
|
156
|
+
var displayText = text || href.split('/').pop();
|
|
157
|
+
return '<a href="' + escapeHtmlForXss(href) + '"' + titleAttr + ' class="video-link">📹' + escapeHtmlForXss(displayText) + '</a>';
|
|
158
|
+
}
|
|
152
159
|
return '<img src="' + escapeHtmlForXss(href) + '"' + altAttr + titleAttr + '>';
|
|
153
160
|
}
|
|
154
161
|
}
|
|
@@ -5740,7 +5747,7 @@ function createFileServer(filePath, fileIndex = 0) {
|
|
|
5740
5747
|
}
|
|
5741
5748
|
|
|
5742
5749
|
// Static file serving for images and other assets
|
|
5743
|
-
if (req.method === "GET") {
|
|
5750
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
5744
5751
|
const MIME_TYPES = {
|
|
5745
5752
|
".png": "image/png",
|
|
5746
5753
|
".jpg": "image/jpeg",
|
|
@@ -5753,9 +5760,21 @@ function createFileServer(filePath, fileIndex = 0) {
|
|
|
5753
5760
|
".js": "application/javascript",
|
|
5754
5761
|
".json": "application/json",
|
|
5755
5762
|
".pdf": "application/pdf",
|
|
5763
|
+
// Video formats
|
|
5764
|
+
".mp4": "video/mp4",
|
|
5765
|
+
".webm": "video/webm",
|
|
5766
|
+
".mov": "video/quicktime",
|
|
5767
|
+
".avi": "video/x-msvideo",
|
|
5768
|
+
".mkv": "video/x-matroska",
|
|
5769
|
+
".m4v": "video/x-m4v",
|
|
5770
|
+
".ogv": "video/ogg",
|
|
5756
5771
|
};
|
|
5757
5772
|
try {
|
|
5758
|
-
|
|
5773
|
+
let urlPath = decodeURIComponent(req.url.split("?")[0]);
|
|
5774
|
+
// Remove leading slash so path.join works correctly with relative baseDir
|
|
5775
|
+
if (urlPath.startsWith("/")) {
|
|
5776
|
+
urlPath = urlPath.slice(1);
|
|
5777
|
+
}
|
|
5759
5778
|
if (urlPath.includes("..")) {
|
|
5760
5779
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
5761
5780
|
res.end("forbidden");
|
|
@@ -5770,9 +5789,57 @@ function createFileServer(filePath, fileIndex = 0) {
|
|
|
5770
5789
|
if (fs.existsSync(staticPath) && fs.statSync(staticPath).isFile()) {
|
|
5771
5790
|
const ext = path.extname(staticPath).toLowerCase();
|
|
5772
5791
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
5773
|
-
const
|
|
5774
|
-
|
|
5775
|
-
|
|
5792
|
+
const stat = fs.statSync(staticPath);
|
|
5793
|
+
const fileSize = stat.size;
|
|
5794
|
+
|
|
5795
|
+
// Check if this is a video file that needs Range Request support
|
|
5796
|
+
const isVideo = contentType.startsWith("video/");
|
|
5797
|
+
const rangeHeader = req.headers.range;
|
|
5798
|
+
|
|
5799
|
+
if (isVideo && rangeHeader) {
|
|
5800
|
+
// Parse Range header (e.g., "bytes=0-1023")
|
|
5801
|
+
const parts = rangeHeader.replace(/bytes=/, "").split("-");
|
|
5802
|
+
const start = parseInt(parts[0], 10);
|
|
5803
|
+
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
5804
|
+
const chunkSize = end - start + 1;
|
|
5805
|
+
|
|
5806
|
+
res.writeHead(206, {
|
|
5807
|
+
"Content-Range": `bytes ${start}-${end}/${fileSize}`,
|
|
5808
|
+
"Accept-Ranges": "bytes",
|
|
5809
|
+
"Content-Length": chunkSize,
|
|
5810
|
+
"Content-Type": contentType,
|
|
5811
|
+
});
|
|
5812
|
+
|
|
5813
|
+
if (req.method === "HEAD") {
|
|
5814
|
+
res.end();
|
|
5815
|
+
} else {
|
|
5816
|
+
const stream = fs.createReadStream(staticPath, { start, end });
|
|
5817
|
+
stream.pipe(res);
|
|
5818
|
+
}
|
|
5819
|
+
} else {
|
|
5820
|
+
// Non-range request or non-video file
|
|
5821
|
+
const headers = {
|
|
5822
|
+
"Content-Type": contentType,
|
|
5823
|
+
"Content-Length": fileSize,
|
|
5824
|
+
};
|
|
5825
|
+
// Add Accept-Ranges for video files so browser knows it can seek
|
|
5826
|
+
if (isVideo) {
|
|
5827
|
+
headers["Accept-Ranges"] = "bytes";
|
|
5828
|
+
}
|
|
5829
|
+
res.writeHead(200, headers);
|
|
5830
|
+
|
|
5831
|
+
// HEAD requests don't need body
|
|
5832
|
+
if (req.method === "HEAD") {
|
|
5833
|
+
res.end();
|
|
5834
|
+
} else if (fileSize > 1024 * 1024) {
|
|
5835
|
+
// Use streaming for large files (> 1MB)
|
|
5836
|
+
const stream = fs.createReadStream(staticPath);
|
|
5837
|
+
stream.pipe(res);
|
|
5838
|
+
} else {
|
|
5839
|
+
const content = fs.readFileSync(staticPath);
|
|
5840
|
+
res.end(content);
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5776
5843
|
return;
|
|
5777
5844
|
}
|
|
5778
5845
|
} catch (err) {
|