Skip to content

Commit

Permalink
Open in album (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
SmilyOrg authored Oct 12, 2024
2 parents a059374 + 93da388 commit 6172d7b
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 30 deletions.
9 changes: 9 additions & 0 deletions api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,15 @@ paths:
type: number
example: 200

- name: closest
in: query
description: |
If true, return the closest region to the specified `x` and `y` coordinates.
The `w` and `h` parameters are ignored in this case.
schema:
type: boolean
example: false

- name: limit
in: query
schema:
Expand Down
5 changes: 5 additions & 0 deletions internal/image/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,11 @@ func (source *Database) listWithPrefixIds(prefixIds []int64, options ListOptions
},
}

if len(prefixIds) == 0 {
close(out)
return out, deps
}

go func() {
if options.Batch == 0 {
defer metrics.Elapsed("list infos sqlite")()
Expand Down
8 changes: 8 additions & 0 deletions internal/layout/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ func (regionSource PhotoRegionSource) GetRegionById(id int, scene *render.Scene,
return regionSource.getRegionFromPhoto(id, &photo, scene, regionConfig)
}

func (regionSource PhotoRegionSource) GetRegionClosestTo(p render.Point, scene *render.Scene, regionConfig render.RegionConfig) (region render.Region, ok bool) {
photo, ok := scene.GetClosestPhotoRef(p)
if !ok {
return render.Region{}, false
}
return regionSource.getRegionFromPhoto(1+photo.Index, photo.Photo, scene, regionConfig), true
}

func layoutFitRow(row []render.Photo, bounds render.Rect, imageSpacing float64) float64 {
count := len(row)
if count == 0 {
Expand Down
17 changes: 16 additions & 1 deletion internal/openapi/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/render/photo.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func (photo *Photo) Draw(config *Render, scene *Scene, c *canvas.Context, scales
}

if !drawn {
if len(errs) > 0 {
if config.DebugThumbnails && len(errs) > 0 {
log.Printf("Unable to draw photo %v: %v", photo.Id, errs)
}

Expand Down
27 changes: 27 additions & 0 deletions internal/render/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type RegionSource interface {
GetRegionsFromImageId(image.ImageId, *Scene, RegionConfig) []Region
GetRegionChanFromBounds(Rect, *Scene, RegionConfig) <-chan Region
GetRegionById(int, *Scene, RegionConfig) Region
GetRegionClosestTo(Point, *Scene, RegionConfig) (region Region, ok bool)
}

type SceneId = string
Expand Down Expand Up @@ -267,6 +268,28 @@ func (scene *Scene) GetVisiblePhotoRefs(view Rect, maxCount int) <-chan PhotoRef
return out
}

func (s *Scene) GetClosestPhotoRef(p Point) (ref PhotoRef, ok bool) {
minIndex := -1
minDistSq := math.MaxFloat64
for i := range s.Photos {
photo := &s.Photos[i]
dx := photo.Sprite.Rect.X - p.X
dy := photo.Sprite.Rect.Y - p.Y
distSq := dx*dx + dy*dy
if distSq < minDistSq {
minDistSq = distSq
minIndex = i
}
}
if minIndex == -1 {
return PhotoRef{}, false
}
return PhotoRef{
Index: minIndex,
Photo: &s.Photos[minIndex],
}, true
}

func (scene *Scene) GetVisiblePhotos(view Rect) <-chan Photo {
out := make(chan Photo, 100)
go func() {
Expand Down Expand Up @@ -316,6 +339,10 @@ func (scene *Scene) GetRegionsByImageId(id image.ImageId, limit int) []Region {
return scene.RegionSource.GetRegionsFromImageId(id, scene, query)
}

func (scene *Scene) GetRegionClosestTo(p Point) (region Region, ok bool) {
return scene.RegionSource.GetRegionClosestTo(p, scene, RegionConfig{})
}

func (scene *Scene) GetRegionChan(bounds Rect) <-chan Region {
if scene.RegionSource == nil {
return nil
Expand Down
21 changes: 21 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,27 @@ func (*Api) GetScenesSceneIdRegions(w http.ResponseWriter, r *http.Request, scen
return
}
regions = scene.GetRegionsByImageId(image.ImageId(*params.FileId), limit)
} else if params.Closest != nil && *params.Closest {
if params.X == nil || params.Y == nil {
problem(w, r, http.StatusBadRequest, "x and y required")
return
}
if params.Limit == nil || *params.Limit != 1 {
problem(w, r, http.StatusBadRequest, "limit must be set to 1")
return
}

p := render.Point{
X: float64(*params.X),
Y: float64(*params.Y),
}

region, ok := scene.GetRegionClosestTo(p)
if !ok {
regions = []render.Region{}
} else {
regions = []render.Region{region}
}
} else {
if params.X == nil || params.Y == nil || params.W == nil || params.H == nil {
problem(w, r, http.StatusBadRequest, "bounds or file_id required")
Expand Down
14 changes: 7 additions & 7 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"fast-deep-equal": "^3.1.3",
"kalmanjs": "^1.1.0",
"ol": "^6.15.1",
"overlayscrollbars": "^1.13.2",
"overlayscrollbars": "^1.13.3",
"overlayscrollbars-vue": "^0.3.0",
"plyr": "^3.7.2",
"qs": "^6.11.0",
Expand Down
7 changes: 7 additions & 0 deletions ui/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ export async function getRegionsWithFileId(sceneId, id) {
return response.items;
}

export async function getRegionClosestTo(sceneId, x, y) {
if (!sceneId) return null;
const response = await get(`/scenes/${sceneId}/regions?x=${x}&y=${y}&closest=true&limit=1`);
if (!response.items?.length) return null;
return response.items[0];
}

export async function getRegion(sceneId, id) {
return get(`/scenes/${sceneId}/regions/${id}`);
}
Expand Down
25 changes: 20 additions & 5 deletions ui/src/components/CollectionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
:interactive="interactive"
:collectionId="collection && collectionId"
:regionId="regionId"
:focusFileId="focusFileId"
:layout="layout"
:sort="sort"
:imageHeight="imageHeight"
Expand All @@ -50,6 +51,7 @@
:scrollbar="scrollbar"
:selectTag="selectTagId && selectTag"
@selectTag="onSelectTag"
@focusFileId="onFocusFileId"
@region="onRegion"
@elementView="lastView = $event"
@scene="scrollScene = $event"
Expand Down Expand Up @@ -213,6 +215,19 @@ const selectTagId = computed(() => {
return route.query.select_tag || undefined;
})
const focusFileId = computed(() => {
return route.query.f || undefined;
})
async function onFocusFileId(id) {
await router.replace({
query: {
...route.query,
f: id || undefined,
}
});
}
const {
data: selectTag,
mutate: selectTagMutate,
Expand Down Expand Up @@ -313,8 +328,8 @@ const onRegion = async (region) => {
}
.controls, .viewer {
transition: transform 0.2s;
transform: translateY(0);
transition: top 0.2s;
top: 0;
}
.showDetails .controls, .showDetails .viewer {
Expand All @@ -341,13 +356,13 @@ const onRegion = async (region) => {
@media (max-width: 700px) {
.controls, .viewer {
transition: transform 0.2s;
transform: translateY(0);
transition: top 0.2s;
top: 0;
}
.showDetails .controls, .showDetails .viewer {
max-width: 100vw;
transform: translateY(-100vh);
top: -100vh;
}
.details {
Expand Down
25 changes: 23 additions & 2 deletions ui/src/components/RegionMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
>
Open Image in New Tab
</ui-nav-item>
<ui-nav-item
v-if="albumUrl"
:href="albumUrl"
target="_blank"
@click="$emit('close')"
>
Open Image in Album
</ui-nav-item>
<ui-item @click="copyImage()">
Copy Image
</ui-item>
Expand Down Expand Up @@ -69,8 +77,9 @@ import copyImg from 'copy-image-clipboard';
import TileViewer from './TileViewer.vue';
import ExpandButton from './ExpandButton.vue';
import { getFileUrl, getThumbnailUrl } from '../api';
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { useViewport } from '../use';
import { useRoute } from 'vue-router';
export default {
props: ["region", "scene", "flipX", "flipY", "tileSize"],
Expand All @@ -82,12 +91,24 @@ export default {
expanded: false,
}
},
setup() {
setup(props) {
const viewer = ref(null);
const viewport = useViewport(viewer);
const route = useRoute();
const albumUrl = computed(() => {
const fileId = props.region?.data?.id;
if (!fileId) return null;
const url = new URL(route.fullPath, window.location.origin);
url.searchParams.set("f", fileId);
url.searchParams.set("layout", "ALBUM");
url.searchParams.delete("search");
url.pathname = url.pathname.replace(/(.*\/collections\/[^/]+)\/.*$/, "$1");
return url.toString();
});
return {
viewer,
viewport,
albumUrl,
}
},
computed: {
Expand Down
Loading

0 comments on commit 6172d7b

Please sign in to comment.