diff options
| -rw-r--r-- | app.rb | 37 | ||||
| -rw-r--r-- | public/css/style.css | 3 | ||||
| -rw-r--r-- | views/admin/album.erb | 11 |
3 files changed, 51 insertions, 0 deletions
@@ -702,6 +702,43 @@ def save_edits(rel, dir) atomic_write(File.join(dir, 'album.json'), JSON.pretty_generate(data)) end +post '/admin/rotate/' do + require_admin! + rotate_file('', MEDIA_ROOT) +end + +post '/admin/rotate/*' do + require_admin! + rel = params[:splat].first.chomp('/') + rotate_file(rel, resolve_dir(rel)) +end + +def rotate_file(rel, dir) + name = File.basename(params[:file].to_s) + full = File.join(dir, name) + halt 404 unless File.file?(full) + halt 400 unless IMAGE_EXTS.include?(File.extname(name).downcase.delete_prefix('.')) + + degrees = params[:dir] == 'left' ? -90 : 90 + img = MiniMagick::Image.open(full) + img.combine_options do |c| + c.auto_orient + c.rotate degrees + end + img.write(full) + + data = load_album(dir) + data['files'] ||= {} + meta = (data['files'][name] ||= {}) + meta['width'], meta['height'] = meta['height'], meta['width'] if meta['width'] && meta['height'] + atomic_write(File.join(dir, 'album.json'), JSON.pretty_generate(data)) + + thumb = thumb_cache_path(rel.empty? ? name : "#{rel}/#{name}") + File.unlink(thumb) if File.exist?(thumb) + + redirect "/admin/edit/#{rel}" +end + # ── Background update ───────────────────────────────────────────────────────── post '/admin/update' do diff --git a/public/css/style.css b/public/css/style.css index 03d869f..edc9d90 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -355,6 +355,9 @@ legend { padding: 0 8px; color: var(--text-dim); font-size: .85rem; } .files-table tr:hover td { background: var(--bg2); } .filename { color: var(--text-dim); font-size: .8rem; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .visible-cell { text-align: center; } +.rotate-cell { text-align: center; white-space: nowrap; } +.rotate-form { display: inline-flex; gap: 4px; margin: 0; } +.rotate-form .btn-sm { padding: 2px 8px; font-size: 1rem; line-height: 1; } .files-table input[type=text] { width: 100%; background: var(--bg3); border: 1px solid transparent; diff --git a/views/admin/album.erb b/views/admin/album.erb index 8c9ebe1..d1b8cb8 100644 --- a/views/admin/album.erb +++ b/views/admin/album.erb @@ -63,6 +63,7 @@ <th>Caption</th> <th>Visible</th> <th>Cover</th> + <th>Rotate</th> <th>Delete</th> </tr> </thead> @@ -70,6 +71,7 @@ <% @files.each do |name| %> <% meta = (@data['files'] || {})[name] || {} %> <% file_rel = @rel.empty? ? name : "#{@rel}/#{name}" %> + <% is_image = IMAGE_EXTS.include?(File.extname(name).downcase.delete_prefix('.')) %> <tr<%= ' class="original-file"' if meta['transcoded_to'] %>> <td><img src="/thumb/<%= file_rel %>" width="60" height="60" loading="lazy" style="object-fit:cover"></td> <td class="filename"> @@ -84,6 +86,15 @@ <td class="cover-cell"> <input type="radio" name="album_cover_file" value="<%= name %>" class="cover-radio"<%= ' checked' if @data['cover'] == name %>> </td> + <td class="rotate-cell"> + <% if is_image %> + <form method="post" action="/admin/rotate/<%= @rel %>" class="rotate-form"> + <input type="hidden" name="file" value="<%= ERB::Util.html_escape(name) %>"> + <button type="submit" name="dir" value="left" class="btn btn-sm" title="Rotate left">⟲</button> + <button type="submit" name="dir" value="right" class="btn btn-sm" title="Rotate right">⟳</button> + </form> + <% end %> + </td> <td class="delete-cell"> <input type="checkbox" name="file_delete[<%= name %>]" value="1" class="delete-check"> </td> |
