Add first metadata values and vastly improve gallery tiling

This commit is contained in:
wisplite
2025-11-23 02:30:07 -06:00
parent 2b7268b3f6
commit 8759783ebb
2 changed files with 72 additions and 19 deletions
+25 -1
View File
@@ -1,11 +1,17 @@
package services package services
import ( import (
"encoding/json"
"fmt" "fmt"
"image"
_ "image/jpeg"
_ "image/png"
"mime/multipart"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/wisplite/raster/internal/db" "github.com/wisplite/raster/internal/db"
"github.com/wisplite/raster/internal/models" "github.com/wisplite/raster/internal/models"
"mime/multipart" "gorm.io/datatypes"
) )
func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string) (models.Media, error) { func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string) (models.Media, error) {
@@ -24,6 +30,23 @@ func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string)
if albumID == "" { if albumID == "" {
albumPath = "root" albumPath = "root"
} }
// Extract metadata (dimensions)
var meta datatypes.JSON
src, err := file.Open()
if err == nil {
defer src.Close()
cfg, _, err := image.DecodeConfig(src)
if err == nil {
m := map[string]int{
"width": cfg.Width,
"height": cfg.Height,
}
b, _ := json.Marshal(m)
meta = datatypes.JSON(b)
}
}
mediaID := uuid.New().String() mediaID := uuid.New().String()
mediaPath := fmt.Sprintf("media/%s/%s.%s", albumPath, mediaID, file.Filename) mediaPath := fmt.Sprintf("media/%s/%s.%s", albumPath, mediaID, file.Filename)
media := models.Media{ media := models.Media{
@@ -31,6 +54,7 @@ func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string)
AlbumID: albumID, AlbumID: albumID,
Path: mediaPath, Path: mediaPath,
Type: file.Header.Get("Content-Type"), Type: file.Header.Get("Content-Type"),
Metadata: meta,
} }
result := db.GetDB().Create(&media) result := db.GetDB().Create(&media)
if result.Error != nil { if result.Error != nil {
+38 -9
View File
@@ -10,13 +10,12 @@ export default function MediaList({ albumId, albumName }) {
const { getAccessToken } = useAccount() const { getAccessToken } = useAccount()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [media, setMedia] = useState([]) const [media, setMedia] = useState([])
const [aspectRatios, setAspectRatios] = useState({})
const { showError } = useNotifier() const { showError } = useNotifier()
useEffect(() => { useEffect(() => {
let ignore = false; let ignore = false;
const getMedia = async () => { const getMedia = async () => {
// TODO: Implement media fetching from API
// const response = await fetch(...)
const response = await fetch(`${getServerUrl()}/api/media/getAllMediaInAlbum?albumId=${albumId}`, { const response = await fetch(`${getServerUrl()}/api/media/getAllMediaInAlbum?albumId=${albumId}`, {
method: 'GET', method: 'GET',
headers: { headers: {
@@ -28,7 +27,6 @@ export default function MediaList({ albumId, albumName }) {
if (data.error) { if (data.error) {
showError(data.error) showError(data.error)
} else { } else {
console.log("data.media", data.media)
setMedia(data.media) setMedia(data.media)
} }
} }
@@ -36,6 +34,16 @@ export default function MediaList({ albumId, albumName }) {
return () => { ignore = true; } return () => { ignore = true; }
}, [albumId]) }, [albumId])
const handleImageLoad = (id, event) => {
const { naturalWidth, naturalHeight } = event.target
if (naturalWidth && naturalHeight) {
setAspectRatios(prev => ({
...prev,
[id]: naturalWidth / naturalHeight
}))
}
}
return ( return (
<div className="flex flex-col items-center justify-start w-full bg-[#141414]"> <div className="flex flex-col items-center justify-start w-full bg-[#141414]">
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4"> <div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
@@ -44,20 +52,41 @@ export default function MediaList({ albumId, albumName }) {
</div> </div>
{/* Media Grid */} {/* Media Grid */}
<div className="flex flex-row items-center justify-start gap-2 w-full px-6 flex-wrap"> <div className="flex flex-wrap justify-start gap-2 w-full px-6">
{media.length === 0 && ( {media.length === 0 && (
<p className="text-gray-500 red-hat-text">No media in this album</p> <p className="text-gray-500 red-hat-text">No media in this album</p>
)} )}
{media.map((media) => ( {media.map((item) => {
<div key={media.id} className="flex flex-col items-center justify-center w-24 h-24 bg-[#1A1A1A] rounded-md border border-[#2B2B2B]"> let ar = 1;
// Try to get aspect ratio from metadata (new uploads) or fallback to loaded state (old uploads)
if (item.Metadata && item.Metadata.width && item.Metadata.height) {
ar = item.Metadata.width / item.Metadata.height;
} else if (aspectRatios[item.ID]) {
ar = aspectRatios[item.ID];
}
return (
<div
key={item.ID}
style={{
height: '220px',
flexGrow: ar,
flexBasis: `${220 * ar}px`,
}}
className="relative bg-[#1A1A1A] rounded-md overflow-hidden border border-[#2B2B2B] min-w-[100px]"
>
<AuthImage <AuthImage
src={`${getServerUrl()}/api/media/${albumId ? albumId : 'root'}/${media.ID}`} src={`${getServerUrl()}/api/media/${albumId ? albumId : 'root'}/${item.ID}`}
token={getAccessToken()} token={getAccessToken()}
alt={media.Title} alt={item.Title}
className="w-full h-full object-cover" className="w-full h-full object-cover"
onLoad={(e) => handleImageLoad(item.ID, e)}
/> />
</div> </div>
))} )
})}
{/* Spacer to prevent the last row from expanding to fill width if it has few items */}
<div style={{ flexGrow: 9999, flexBasis: '50%' }}></div>
</div> </div>
<MediaUploadModal <MediaUploadModal