commit b1d25a7c9c2adfe70bf5cc5c7afbc4b0e8b65a7b
parent 3b8a8f50b49f54a78da911d85128f8fd01cf8a40
Author: breadcat <breadcat@users.noreply.github.com>
Date: Mon, 29 Sep 2025 16:14:20 +0100
Support m4v files and shuffle around /file.html layout
Diffstat:
6 files changed, 125 insertions(+), 89 deletions(-)
diff --git a/backfill.sh b/backfill.sh
@@ -42,7 +42,7 @@ if [[ -n "$target_file" ]]; then
generate_thumbnail "$file_path" "$thumb" "$override_time"
else
- find "$upload_dir" -maxdepth 1 -type f \( -iname "*.mp4" -o -iname "*.webm" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mkv" \) | while read -r file; do
+ find "$upload_dir" -maxdepth 1 -type f \( -iname "*.mp4" -o -iname "*.webm" -o -iname "*.mov" -o -iname "*.avi" -o -iname "*.mkv" -o -iname "*.m4v" \) | while read -r file; do
filename=$(basename "$file")
thumb="$thumb_dir/${filename}.jpg"
diff --git a/main.go b/main.go
@@ -1277,7 +1277,7 @@ func processVideoFile(tempPath, finalPath string) (string, string, error) {
}
ext := strings.ToLower(filepath.Ext(finalPath))
- if ext == ".mp4" || ext == ".mov" || ext == ".avi" || ext == ".mkv" || ext == ".webm" {
+ if ext == ".mp4" || ext == ".mov" || ext == ".avi" || ext == ".mkv" || ext == ".webm" || ext == ".m4v" {
if err := generateThumbnail(finalPath, config.UploadDir, filepath.Base(finalPath)); err != nil {
log.Printf("Warning: could not generate thumbnail: %v", err)
}
diff --git a/static/rename-file.js b/static/rename-file.js
@@ -0,0 +1,18 @@
+document.addEventListener("DOMContentLoaded", () => {
+ document.querySelectorAll(".rename-button").forEach(button => {
+ button.addEventListener("click", () => {
+ const fileID = button.dataset.fileId;
+ const currentName = button.dataset.currentName;
+
+ const newName = prompt("Enter new filename (include extension):", currentName);
+
+ if (!newName) {
+ return;
+ }
+
+ const form = document.getElementById(`renameForm-${fileID}`);
+ form.querySelector('input[name="newfilename"]').value = newName;
+ form.submit();
+ });
+ });
+});
diff --git a/static/style.css b/static/style.css
@@ -20,6 +20,7 @@ nav ul{flex:1 1 auto;list-style:none}
nav ul.sub-menu li a:first-letter{text-transform:capitalize}
/* search bar */
+div#search-container form {border-left:1px solid gray}
div#search-container form input{border:1px solid gray;margin: 8px;padding:8px}
div#search-container form input:focus{border:none;background-color:#3a3a3a}
@@ -38,4 +39,3 @@ button#edit-description-btn {background: #007cba; color: white; padding: 6px 12p
textarea#description-textarea {width: 100%; max-width: 600px; padding: 8px; border: 1px solid #ccc; border-radius: 3px; font-family: inherit; resize: vertical;}
input.description-save {background: #28a745; color: white; padding: 8px 16px; border: none; border-radius: 3px; cursor: pointer;}
button.description-cancel {background: #6c757d; color: white; padding: 8px 16px; border: none; border-radius: 3px; cursor: pointer;}
-button.delete-tag {border:none; background:none; color:red; cursor:pointer; padding:0; margin-left:2px;}
-\ No newline at end of file
diff --git a/templates/_gallery.html b/templates/_gallery.html
@@ -2,7 +2,7 @@
<div class="gallery-item">
<a href="/file/{{.ID}}" title="{{.Filename}}">
{{if hasAnySuffix .Filename ".jpg" ".jpeg" ".png" ".gif" ".webp"}}<img src="/uploads/{{.EscapedFilename}}" style="max-width:150px">
- {{else if hasAnySuffix .Filename ".mp4" ".webm" ".mov"}}<div class="gallery-video"><img src="/uploads/thumbnails/{{.EscapedFilename}}.jpg" style="width: 100%; display: block;" /><div class="play-button"></div></div>
+ {{else if hasAnySuffix .Filename ".mp4" ".webm" ".mov" ".m4v"}}<div class="gallery-video"><img src="/uploads/thumbnails/{{.EscapedFilename}}.jpg" style="width: 100%; display: block;" /><div class="play-button"></div></div>
{{end}}
<br>{{.Filename}}</a>
</div>
diff --git a/templates/file.html b/templates/file.html
@@ -1,101 +1,120 @@
{{template "_header" .}}
<h2>File: {{.Data.File.Filename}}</h2>
-{{if hasAnySuffix .Data.File.Filename ".jpg" ".jpeg" ".png" ".gif" ".webp"}}
- <a href="/uploads/{{.Data.EscapedFilename}}" target="_blank"><img src="/uploads/{{.Data.EscapedFilename}}" style="max-width:400px"></a><br>
-{{else if hasAnySuffix .Data.File.Filename ".mp4" ".webm" ".mov"}}
- <video id="videoPlayer" controls loop muted width="600">
- <source src="/uploads/{{.Data.EscapedFilename}}">
- </video><br>
-{{else}}
- <a href="/uploads/{{.Data.EscapedFilename}}">Download file</a><br>
-{{end}}
+<div class="file-container">
-<script src="/static/timestamps.js" defer></script>
+<div class="file-sidebar">
-<div class="description-section">
- <h3>Description</h3>
+ <details open>
+ <summary>Tags</summary>
+ <ul>
+ {{range $k, $vs := .Data.File.Tags}}
+ <li>
+ {{$k}}:
+ {{range $i, $v := $vs}}
+ {{if $i}}, {{end}}
+ <form method="post" action="/file/{{$.Data.File.ID}}/tag/{{$k}}/{{$v}}/delete" style="display:inline;"><button class="text-button" type="submit">[x]</button></form>
+ <a href="/tag/{{$k}}/{{$v}}">{{$v}}</a>
+ {{end}}
+ </li>
+ {{else}}
+ <li>No tags yet</li>
+ {{end}}
+ </ul>
+ </details>
- <!-- Display Mode -->
- <div id="description-display" data-original-description="{{.Data.File.Description}}">
- {{if .Data.File.Description}}
- <div id="current-description" >{{.Data.File.Description}}</div>
- {{else}}
- <div id="no-description">No description set</div>
- {{end}}
- <button id="edit-description-btn" onclick="toggleDescriptionEdit()">
- {{if .Data.File.Description}}Edit Description{{else}}Add Description{{end}}
- </button>
- </div>
+ <details>
+ <summary>Add Tags</summary>
+ <form method="post">
+ Category: <input type="text" name="category" list="categories"><br>
+ <datalist id="categories">
+ {{range .Data.Categories}}
+ <option value="{{.}}">
+ {{end}}
+ </datalist>
+ Value: <input type="text" name="value"><br>
+ <button type="submit">Add Tag</button>
+ </form>
+ </details>
- <!-- Edit Mode (initially hidden) -->
- <div id="description-edit" style="display: none;">
- <form method="post">
- <input type="hidden" name="action" value="update_description">
- <div>
- <textarea
- id="description-textarea"
- name="description"
- rows="6"
- maxlength="2048"
- placeholder="Enter description..."
- >{{.Data.File.Description}}</textarea>
- </div>
- <div style="margin-top: 8px; display: flex; align-items: center; gap: 10px;">
- <input class="description-save" type="submit" value="Save Description">
- <button class="description-cancel" type="button" onclick="cancelDescriptionEdit()">Cancel</button>
- </div>
- </form>
- </div>
-</div>
+ <details>
+ <summary>Raw URL</summary>
+ <input id="raw-url" value="http://{{.IP}}:{{.Port}}/uploads/{{.Data.EscapedFilename}}"><br>
+ <button id="copy-btn" style="margin-top: 5px;">Copy to Clipboard</button>
+ <span id="copy-status" style="margin-left: 10px;"></span>
+ <script src="/static/copy-link.js" defer></script>
+ </details>
+
+ <details>
+ <summary>File Actions</summary>
-<script src="/static/description.js" defer></script>
+ <script src="/static/rename-file.js" defer></script>
+ <form id="renameForm-{{.Data.File.ID}}" method="post" action="/file/{{.Data.File.ID}}/rename" style="display:inline;">
+ <input type="hidden" name="newfilename" value="{{.Data.File.Filename}}">
+ <button type="button" class="text-button rename-button"
+ data-file-id="{{.Data.File.ID}}"
+ data-current-name="{{.Data.File.Filename}}">
+ [ Rename File ]
+ </button>
+ </form>
+ <br />
+ <form method="post" action="/file/{{.Data.File.ID}}/delete" style="display:inline;">
+ <button type="submit" onclick="return confirm('Are you sure you want to delete this file? This cannot be undone!')" class="text-button">[ Delete File ]</button>
+ </form>
+ </details>
+</div>
-<h3>Raw URL</h3>
-<input id="raw-url" value="http://{{.IP}}:{{.Port}}/uploads/{{.Data.EscapedFilename}}">
-<button id="copy-btn" style="margin-top: 5px;">Copy to Clipboard</button>
-<span id="copy-status" style="margin-left: 10px;"></span>
-<script src="/static/copy-link.js" defer></script>
+<div class="file-content">
+ {{if hasAnySuffix .Data.File.Filename ".jpg" ".jpeg" ".png" ".gif" ".webp"}}
+ <a href="/uploads/{{.Data.EscapedFilename}}" target="_blank"><img src="/uploads/{{.Data.EscapedFilename}}" style="max-width:400px"></a><br>
+ {{else if hasAnySuffix .Data.File.Filename ".mp4" ".webm" ".mov" ".m4v"}}
+ <video id="videoPlayer" controls loop muted width="600">
+ <source src="/uploads/{{.Data.EscapedFilename}}">
+ </video><br>
+ {{else}}
+ <a href="/uploads/{{.Data.EscapedFilename}}">Download file</a><br>
+ {{end}}
+ <div class="description-section">
+ <h3>Description</h3>
-<h3>File Actions</h3>
-<form method="post" action="/file/{{.Data.File.ID}}/rename" style="display:inline-block; margin-right: 20px;">
- New filename: <input type="text" name="newfilename" value="{{.Data.File.Filename}}" required style="width:300px"><br>
- <small>Include file extension (e.g., "new-name.jpg")</small><br><br>
- <button type="submit" onclick="return confirm('Are you sure you want to rename this file?')">Rename File</button>
-</form>
+ <!-- Display Mode -->
+ <div id="description-display" data-original-description="{{.Data.File.Description}}">
+ {{if .Data.File.Description}}
+ <div id="current-description" >{{.Data.File.Description}}</div>
+ {{else}}
+ <div id="no-description">No description set</div>
+ {{end}}
+ <button id="edit-description-btn" onclick="toggleDescriptionEdit()">
+ {{if .Data.File.Description}}Edit Description{{else}}Add Description{{end}}
+ </button>
+ </div>
-<form method="post" action="/file/{{.Data.File.ID}}/delete" style="display:inline-block;">
- <button type="submit" onclick="return confirm('Are you sure you want to delete this file? This cannot be undone!')" style="background-color: #dc3545; color: white;">Delete File</button>
-</form>
+ <!-- Edit Mode (initially hidden) -->
+ <div id="description-edit" style="display: none;">
+ <form method="post">
+ <input type="hidden" name="action" value="update_description">
+ <div>
+ <textarea
+ id="description-textarea"
+ name="description"
+ rows="6"
+ maxlength="2048"
+ placeholder="Enter description..."
+ >{{.Data.File.Description}}</textarea>
+ </div>
+ <div style="margin-top: 8px; display: flex; align-items: center; gap: 10px;">
+ <input class="description-save" type="submit" value="Save Description">
+ <button class="description-cancel" type="button" onclick="cancelDescriptionEdit()">Cancel</button>
+ </div>
+ </form>
+ </div>
+ </div>
-<h3>Tags</h3>
-<ul>
-{{range $k, $vs := .Data.File.Tags}}
- <li>
- {{$k}}:
- {{range $i, $v := $vs}}
- {{if $i}}, {{end}}
- <form method="post" action="/file/{{$.Data.File.ID}}/tag/{{$k}}/{{$v}}/delete" style="display:inline;"><button class="delete-tag" type="submit">[x]</button></form>
- <a href="/tag/{{$k}}/{{$v}}">{{$v}}</a>
- {{end}}
- </li>
-{{else}}
- <li>No tags yet</li>
-{{end}}
-</ul>
+ <script src="/static/description.js" defer></script>
+ <script src="/static/timestamps.js" defer></script>
-<h3>Assign New Tag</h3>
-<form method="post">
- Category: <input type="text" name="category" list="categories"><br>
- <datalist id="categories">
- {{range .Data.Categories}}
- <option value="{{.}}">
- {{end}}
- </datalist>
- Value: <input type="text" name="value"><br>
- <button type="submit">Add Tag</button>
-</form>
+</div>
{{template "_footer"}}
\ No newline at end of file