reddit: fix image copy for existing should go to temp dir first

This commit is contained in:
Tigor Hutasuhut 2024-05-01 00:02:30 +07:00
parent c5c8058376
commit 65bb5732ca
5 changed files with 66 additions and 31 deletions

View file

@ -5,10 +5,12 @@ import (
"errors"
"image/jpeg"
"io"
"io/fs"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
@ -185,21 +187,32 @@ func (api *API) downloadSubredditImage(ctx context.Context, post reddit.Post, su
return api.saveImageToFSAndDatabase(ctx, tmpImageFile, subreddit, post, devices)
}
type stat interface {
Stat() (fs.FileInfo, error)
}
func (api *API) saveImageToFSAndDatabase(ctx context.Context, image io.ReadCloser, subreddit *models.Subreddit, post reddit.Post, devices models.DeviceSlice) (err error) {
ctx, span := tracer.Start(ctx, "*API.saveImageToFSAndDatabase")
defer span.End()
defer image.Close()
w, close, err := api.createDeviceImageWriters(post, devices)
defer close()
if err != nil {
return errs.Wrapw(err, "failed to create image files")
}
log.New(ctx).Debug("saving image files", "post_id", post.GetID(), "post_url", post.GetImageURL(), "devices", devices)
defer close()
_, err = io.Copy(w, image)
size, err := io.Copy(w, image)
if err != nil {
return errs.Wrapw(err, "failed to save image files")
}
if size == 0 {
if s, ok := image.(stat); ok {
if fi, err := s.Stat(); err == nil {
size = fi.Size()
}
}
}
var many []*models.ImageSetter
now := time.Now()
@ -209,10 +222,6 @@ func (api *API) saveImageToFSAndDatabase(ctx context.Context, image io.ReadClose
nsfw = 1
}
width, height := post.GetImageSize()
var size int64
if fi, err := os.Stat(post.GetImageTargetPath(api.config, device)); err == nil {
size = fi.Size()
}
many = append(many, &models.ImageSetter{
Subreddit: omit.From(subreddit.Name),
@ -300,14 +309,14 @@ func (api *API) isImageEntryExists(ctx context.Context, post reddit.Post, device
ctx, span := tracer.Start(ctx, "*API.IsImageExists")
defer span.End()
_, errQuery := models.Images.Query(ctx, api.db,
exist, errQuery := models.Images.Query(ctx, api.db,
models.SelectWhere.Images.Device.EQ(device.Slug),
models.SelectWhere.Images.PostName.EQ(post.GetName()),
).One()
).Exists()
_, errStat := os.Stat(post.GetImageTargetPath(api.config, device))
return errQuery == nil && errStat == nil
return exist && errQuery == nil && errStat == nil
}
// findImageFileForDevice finds if any of the image file exists for given devices.
@ -317,16 +326,39 @@ func (api *API) isImageEntryExists(ctx context.Context, post reddit.Post, device
// Return nil if no image file exists for the devices.
//
// Ensure to close the file after use.
func (api *API) findImageFileForDevices(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (file *os.File) {
func (api *API) findImageFileForDevices(ctx context.Context, post reddit.Post, devices models.DeviceSlice) (oldImageFile *os.File) {
for _, device := range devices {
_, err := os.Stat(post.GetImageTargetPath(api.config, device))
stat, err := os.Stat(post.GetImageTargetPath(api.config, device))
if err == nil {
file, err = os.Open(post.GetImageTargetPath(api.config, device))
oldImageFile, err = os.Open(post.GetImageTargetPath(api.config, device))
if err != nil {
log.New(ctx).Err(err).Error("failed to open image file", "filename", post.GetImageTargetPath(api.config, device))
return nil
}
return file
defer oldImageFile.Close()
tempFilename := filepath.Join(os.TempDir(), "redmage", stat.Name())
tempFileWrite, err := os.Create(tempFilename)
if err != nil {
log.New(ctx).Err(err).Error("failed to create temp file", "filename", post.GetImageTargetPath(api.config, device))
return nil
}
defer tempFileWrite.Close()
_, err = io.Copy(tempFileWrite, oldImageFile)
if err != nil {
log.New(ctx).Err(err).Error("failed to copy image file", "filename", post.GetImageTargetPath(api.config, device))
return nil
}
rf, err := os.Open(tempFilename)
if err != nil {
log.New(ctx).Err(err).Error("failed to open temp file", "filename", tempFileWrite.Name())
return nil
}
return rf
}
}

View file

@ -2,7 +2,7 @@ POST http://localhost:8080/api/v1/subreddits HTTP/1.1
Host: localhost:8080
{
"name": "wallpapers",
"name": "wallpaper",
"enable_schedule": 1,
"schedule": "@daily",
"countback": 300

View file

@ -3,5 +3,5 @@ Host: localhost:8080
Content-Type: application/json
{
"subreddit": "wallpapers"
"subreddit": "wallpaper"
}

View file

@ -39,8 +39,7 @@ templ HomeContent(c *views.Context, data Data) {
hx-push-url="true"
>
<h1 class="mb-4">
Recently Added -
{ strconv.FormatInt(data.TotalImages, 10) } Images
Recently Added
</h1>
@recentRangeInput(c)
@nsfwToggle(c, data)
@ -48,6 +47,10 @@ templ HomeContent(c *views.Context, data Data) {
<div id="recently-added-images">
if data.TotalImages == 0 {
<h2 class="mt-4">There are no recently added images in the current time range.</h2>
} else {
<h2 class="mt-4">
Added Images: { strconv.FormatInt(data.TotalImages, 10) }
</h2>
}
for _, recently := range data.RecentlyAddedImages {
<div class="divider"></div>

View file

@ -1,9 +1,10 @@
package homeview
import "github.com/tigorlazuardi/redmage/models"
import "github.com/tigorlazuardi/redmage/api/reddit"
import "fmt"
import "github.com/tigorlazuardi/redmage/views/utils"
import "strconv"
import "github.com/alecthomas/units"
type ImageCardOption uint
@ -19,14 +20,14 @@ const (
)
templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
<div class="not-prose card card-bordered bg-base-100 hover:bg-base-200 shadow-xl w-[256px] min-w-[256px] rounded-xl top-0 hover:-top-1 hover:drop-shadow-2xl transition-all">
<div class="not-prose card card-bordered bg-base-100 hover:bg-base-200 shadow-xl min-w-[256px] rounded-xl top-0 hover:-top-1 hover:drop-shadow-2xl transition-all">
<figure>
<a
href={ templ.URL(fmt.Sprintf("/img/%s", data.ImageRelativePath)) }
target="_blank"
>
<img
class="object-contain w-[256px] h-[256px]"
class="object-contain max-w-[256px] max-h-[256px]"
src={ fmt.Sprintf("/img/%s", data.ThumbnailRelativePath) }
alt={ data.PostTitle }
/>
@ -40,18 +41,17 @@ templ RecentlyAddedImageCard(data *models.Image, opts ImageCardOption) {
>{ truncateTitle(data.PostTitle) }</a>
}
<a class="text-primary underline" href={ templ.URL(data.PostAuthorURL) }>{ data.PostAuthor }</a>
<div>
@utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt)
<div class="flex-1"></div>
<div class="flex w-full justify-end">
@utils.RelativeTimeNode(fmt.Sprintf("relative-time-%s", data.PostName), data.CreatedAt, "text-sm")
</div>
<div class="card-actions justify-between">
if data.R.Subreddit != nil {
if !opts.Has(HideSubreddit) {
<a
class="badge badge-outline badge-primary"
href={ templ.URL(fmt.Sprintf("https://reddit.com/%s/%s", reddit.SubredditType(data.R.Subreddit.Subtype), data.R.Subreddit.Name)) }
>{ data.R.Subreddit.Name } </a>
}
}
<div class="grid grid-cols-2 gap-x-4">
<p class="text-xs">Width</p>
<p class="text-xs text-end">{ strconv.Itoa(int(data.ImageWidth)) } px</p>
<p class="text-xs">Height</p>
<p class="text-xs text-end">{ strconv.Itoa(int(data.ImageHeight)) } px</p>
<p class="text-xs">Size</p>
<p class="text-xs text-end">{ units.MetricBytes(data.ImageSize).Round(1).String() }</p>
</div>
</div>
</div>