Merge branch 'master' of github.com:viktorstrate/photoview into favorites-checkobox-on-photos-and-album-page-viktorstrate/photoview#6
This commit is contained in:
commit
75e43aae80
|
@ -1,42 +0,0 @@
|
||||||
name: API
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: api
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: ^1.13
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Get dependencies
|
|
||||||
run: |
|
|
||||||
go get -v -t -d ./...
|
|
||||||
if [ -f Gopkg.toml ]; then
|
|
||||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
|
||||||
dep ensure
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: go build -v .
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: go test -v ./...
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
name: Docker builds
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 10 * * *" # everyday at 10am
|
||||||
|
pull_request:
|
||||||
|
branches: master
|
||||||
|
push:
|
||||||
|
branches: master
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and deploy docker images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Cache Docker layers
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
id: prepare
|
||||||
|
run: |
|
||||||
|
DOCKER_USERNAME=viktorstrate
|
||||||
|
DOCKER_IMAGE=viktorstrate/photoview
|
||||||
|
DOCKER_PLATFORMS=linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
|
VERSION=edge
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
fi
|
||||||
|
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||||
|
VERSION=nightly
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}"
|
||||||
|
if [[ $VERSION =~ ^(([0-9]{1,3})\.[0-9]{1,3})\.[0-9]{1,3}$ ]]; then
|
||||||
|
VERSION_MINOR=${BASH_REMATCH[1]}
|
||||||
|
VERSION_MAJOR=${BASH_REMATCH[2]}
|
||||||
|
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest --tag ${DOCKER_IMAGE}:${VERSION_MINOR} --tag ${DOCKER_IMAGE}:${VERSION_MAJOR}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ::set-output name=docker_username::${DOCKER_USERNAME}
|
||||||
|
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
||||||
|
echo ::set-output name=version::${VERSION}
|
||||||
|
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \
|
||||||
|
--cache-from "type=local,src=/tmp/.buildx-cache" \
|
||||||
|
--cache-to "type=local,dest=/tmp/.buildx-cache" \
|
||||||
|
--build-arg VERSION=${VERSION} \
|
||||||
|
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
|
||||||
|
--build-arg VCS_REF=${GITHUB_SHA::8} \
|
||||||
|
${TAGS} --file Dockerfile .
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: crazy-max/ghaction-docker-buildx@v3
|
||||||
|
|
||||||
|
- name: Docker Buildx (build)
|
||||||
|
run: |
|
||||||
|
docker buildx build --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }}
|
||||||
|
|
||||||
|
- name: Docker Login
|
||||||
|
if: success() && github.event_name != 'pull_request'
|
||||||
|
env:
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "${DOCKER_PASSWORD}" | docker login --username "${{ steps.prepare.outputs.docker_username }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Docker Buildx (push)
|
||||||
|
if: success() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }}
|
||||||
|
|
||||||
|
- name: Docker Check Manifest
|
||||||
|
if: always() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
|
||||||
|
|
||||||
|
- name: Clear
|
||||||
|
if: always() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
rm -f ${HOME}/.docker/config.json
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-api:
|
||||||
|
name: Test API
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: api
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.13
|
||||||
|
id: go
|
||||||
|
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Get dependencies
|
||||||
|
run: |
|
||||||
|
go get -v -t -d ./...
|
||||||
|
if [ -f Gopkg.toml ]; then
|
||||||
|
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||||
|
dep ensure
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build -v .
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -v ./...
|
|
@ -1,5 +1,3 @@
|
||||||
{
|
{
|
||||||
"eslint.workingDirectories": [
|
"eslint.workingDirectories": ["ui"]
|
||||||
"ui", "api"
|
|
||||||
]
|
|
||||||
}
|
}
|
21
Dockerfile
21
Dockerfile
|
@ -1,5 +1,5 @@
|
||||||
# Build UI
|
# Build UI
|
||||||
FROM node:10 as ui
|
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:10 as ui
|
||||||
|
|
||||||
ARG API_ENDPOINT
|
ARG API_ENDPOINT
|
||||||
ENV API_ENDPOINT=${API_ENDPOINT}
|
ENV API_ENDPOINT=${API_ENDPOINT}
|
||||||
|
@ -20,7 +20,8 @@ COPY ui /app
|
||||||
RUN npm run build -- --public-url $UI_PUBLIC_URL
|
RUN npm run build -- --public-url $UI_PUBLIC_URL
|
||||||
|
|
||||||
# Build API
|
# Build API
|
||||||
FROM golang:alpine AS api
|
FROM --platform=${BUILDPLATFORM:-linux/amd64} tonistiigi/xx:golang AS xgo
|
||||||
|
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.14-alpine AS api
|
||||||
|
|
||||||
RUN mkdir -p /app
|
RUN mkdir -p /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -32,16 +33,20 @@ RUN go mod download
|
||||||
# Copy api source
|
# Copy api source
|
||||||
COPY api /app
|
COPY api /app
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o photoview .
|
ARG TARGETPLATFORM
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
RUN go env
|
||||||
|
RUN go build -v -o photoview .
|
||||||
|
|
||||||
# Copy api and ui to production environment
|
# Copy api and ui to production environment
|
||||||
FROM alpine:3.12
|
FROM alpine:3.12
|
||||||
|
|
||||||
# Install darktable for converting RAW images
|
# Install darktable for converting RAW images, and ffmpeg for encoding videos
|
||||||
RUN apk --no-cache add darktable
|
# Ignore errors if packages are not supported for the specified platform
|
||||||
|
RUN apk --no-cache add darktable; exit 0
|
||||||
# Install ffmpeg for encoding videos
|
RUN apk --no-cache add ffmpeg; exit 0
|
||||||
RUN apk --no-cache add ffmpeg
|
|
||||||
|
|
||||||
COPY --from=ui /app/dist /ui
|
COPY --from=ui /app/dist /ui
|
||||||
COPY --from=api /app/database/migrations /database/migrations
|
COPY --from=api /app/database/migrations /database/migrations
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
[![License](https://img.shields.io/github/license/viktorstrate/photoview)](./LICENSE.md)
|
[![License](https://img.shields.io/github/license/viktorstrate/photoview)](./LICENSE.md)
|
||||||
[![GitHub contributors](https://img.shields.io/github/contributors/viktorstrate/photoview)](https://github.com/viktorstrate/photoview/graphs/contributors)
|
[![GitHub contributors](https://img.shields.io/github/contributors/viktorstrate/photoview)](https://github.com/viktorstrate/photoview/graphs/contributors)
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/viktorstrate/photoview)](https://hub.docker.com/r/viktorstrate/photoview)
|
[![Docker Pulls](https://img.shields.io/docker/pulls/viktorstrate/photoview)](https://hub.docker.com/r/viktorstrate/photoview)
|
||||||
[![Docker Build Status](https://img.shields.io/docker/cloud/build/viktorstrate/photoview)](https://hub.docker.com/r/viktorstrate/photoview/builds)
|
[![Docker Build Status](https://img.shields.io/github/workflow/status/viktorstrate/photoview/Docker%20builds?label=docker%20build)](https://hub.docker.com/r/viktorstrate/photoview/)
|
||||||
|
|
||||||
![screenshot](./screenshots/main-window.png)
|
![screenshot](./screenshots/main-window.png)
|
||||||
|
|
||||||
|
@ -30,9 +30,11 @@ Password: **demo**
|
||||||
|
|
||||||
- **Closely tied to the file system**. The website presents the images found on the local filesystem of the server, directories are mapped to albums.
|
- **Closely tied to the file system**. The website presents the images found on the local filesystem of the server, directories are mapped to albums.
|
||||||
- **User management**. Each user is created along with a path on the local filesystem, photos within that path can be accessed by that user.
|
- **User management**. Each user is created along with a path on the local filesystem, photos within that path can be accessed by that user.
|
||||||
- **Photo sharing**. Photos and albums can easily be shared with other users or publicly with a unique URL.
|
- **Sharing**. Albums, as well as individual media, can easily be shared with a public link, the link can optinally be password protected.
|
||||||
- **Made for photography**. The website is ment as a way to present photographies, and thus supports **RAW** file formats, and **EXIF** parsing.
|
- **Made for photography**. Photoview is built with photographers in mind, and thus supports **RAW** file formats, and **EXIF** parsing.
|
||||||
|
- **Video support**. Many common video formats are supported. Videos will automatically be optimized for web.
|
||||||
- **Performant**. Thumbnails are automatically generated and photos first load when they are visible on the screen. In full screen, thumbnails are displayed until the high resolution image has been fully loaded.
|
- **Performant**. Thumbnails are automatically generated and photos first load when they are visible on the screen. In full screen, thumbnails are displayed until the high resolution image has been fully loaded.
|
||||||
|
- **Secure**. All media resources are protected with a cookie-token, all passwords are properly hashed, and the API uses a strict [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
|
||||||
|
|
||||||
## Why yet another self-hosted photo gallery
|
## Why yet another self-hosted photo gallery
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
DROP TABLE IF EXISTS site_info;
|
||||||
|
DROP TABLE IF EXISTS access_token;
|
||||||
|
DROP TABLE IF EXISTS media_url;
|
||||||
|
DROP TABLE IF EXISTS share_token;
|
||||||
|
DROP TABLE IF EXISTS media;
|
||||||
|
DROP TABLE IF EXISTS video_metadata;
|
||||||
|
DROP TABLE IF EXISTS media_exif;
|
||||||
|
DROP TABLE IF EXISTS album;
|
||||||
|
DROP TABLE IF EXISTS user;
|
|
@ -0,0 +1,120 @@
|
||||||
|
-- Users and authentication
|
||||||
|
CREATE TABLE IF NOT EXISTS user (
|
||||||
|
user_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
username varchar(256) NOT NULL UNIQUE,
|
||||||
|
password varchar(256),
|
||||||
|
root_path varchar(512),
|
||||||
|
admin boolean NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
PRIMARY KEY (user_id)
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS access_token (
|
||||||
|
token_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
user_id int NOT NULL,
|
||||||
|
value char(24) NOT NULL UNIQUE,
|
||||||
|
expire timestamp NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (token_id),
|
||||||
|
FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS site_info (
|
||||||
|
initial_setup boolean NOT NULL DEFAULT TRUE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Video related
|
||||||
|
CREATE TABLE IF NOT EXISTS video_metadata (
|
||||||
|
metadata_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
|
||||||
|
width int(6) NOT NULL,
|
||||||
|
height int(6) NOT NULL,
|
||||||
|
duration double NOT NULL,
|
||||||
|
codec varchar(128),
|
||||||
|
framerate double,
|
||||||
|
bitrate int(24),
|
||||||
|
color_profile varchar(128),
|
||||||
|
audio varchar(128),
|
||||||
|
|
||||||
|
PRIMARY KEY (metadata_id)
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Media related
|
||||||
|
CREATE TABLE IF NOT EXISTS album (
|
||||||
|
album_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
title varchar(256) NOT NULL,
|
||||||
|
parent_album int,
|
||||||
|
owner_id int NOT NULL,
|
||||||
|
path varchar(1024) NOT NULL,
|
||||||
|
path_hash varchar(32) NOT NULL UNIQUE,
|
||||||
|
|
||||||
|
PRIMARY KEY (album_id),
|
||||||
|
FOREIGN KEY (parent_album) REFERENCES album(album_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (owner_id) REFERENCES user(user_id) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS media_exif (
|
||||||
|
exif_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
camera varchar(256),
|
||||||
|
maker varchar(256),
|
||||||
|
lens varchar(256),
|
||||||
|
date_shot timestamp NULL,
|
||||||
|
exposure varchar(256),
|
||||||
|
aperture float,
|
||||||
|
iso int(6),
|
||||||
|
focal_length float,
|
||||||
|
flash varchar(256),
|
||||||
|
orientation int(1),
|
||||||
|
exposure_program int(1),
|
||||||
|
gps_latitude float,
|
||||||
|
gps_longitude float,
|
||||||
|
|
||||||
|
PRIMARY KEY (exif_id)
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS media (
|
||||||
|
media_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
title varchar(256) NOT NULL,
|
||||||
|
path varchar(1024) NOT NULL,
|
||||||
|
path_hash varchar(32) NOT NULL UNIQUE,
|
||||||
|
album_id int NOT NULL,
|
||||||
|
exif_id int,
|
||||||
|
date_shot datetime NOT NULL,
|
||||||
|
date_imported datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
favorite boolean DEFAULT FALSE,
|
||||||
|
media_type varchar(64) NOT NULL,
|
||||||
|
video_metadata_id int,
|
||||||
|
|
||||||
|
PRIMARY KEY (media_id),
|
||||||
|
FOREIGN KEY (album_id) REFERENCES album(album_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (exif_id) REFERENCES media_exif(exif_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (video_metadata_id) REFERENCES video_metadata(metadata_id) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS media_url (
|
||||||
|
url_id int NOT NULL AUTO_INCREMENT,
|
||||||
|
media_id int NOT NULL,
|
||||||
|
media_name varchar(512) NOT NULL,
|
||||||
|
width int NOT NULL,
|
||||||
|
height int NOT NULL,
|
||||||
|
purpose varchar(64) NOT NULL,
|
||||||
|
content_type varchar(64) NOT NULL,
|
||||||
|
file_size int NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (url_id),
|
||||||
|
FOREIGN KEY (media_id) REFERENCES media(media_id) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Public shares
|
||||||
|
CREATE TABLE IF NOT EXISTS share_token (
|
||||||
|
token_id int AUTO_INCREMENT,
|
||||||
|
value char(24) NOT NULL UNIQUE,
|
||||||
|
owner_id int NOT NULL,
|
||||||
|
expire timestamp NULL DEFAULT NULL,
|
||||||
|
password varchar(256),
|
||||||
|
album_id int,
|
||||||
|
media_id int,
|
||||||
|
|
||||||
|
PRIMARY KEY (token_id)
|
||||||
|
-- CHECK (album_id IS NOT NULL OR media_id IS NOT NULL)
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
@ -1,2 +0,0 @@
|
||||||
DROP TABLE IF EXISTS user;
|
|
||||||
DROP TABLE IF NOT EXISTS access_token;
|
|
|
@ -1,19 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS user (
|
|
||||||
user_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
username varchar(256) NOT NULL UNIQUE,
|
|
||||||
password varchar(256),
|
|
||||||
root_path varchar(512),
|
|
||||||
admin boolean NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
PRIMARY KEY (user_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS access_token (
|
|
||||||
token_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
user_id int NOT NULL,
|
|
||||||
value char(24) NOT NULL UNIQUE,
|
|
||||||
expire timestamp NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (token_id),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE CASCADE
|
|
||||||
);
|
|
|
@ -1,4 +0,0 @@
|
||||||
DROP TABLE IF EXISTS photo;
|
|
||||||
DROP TABLE IF EXISTS album;
|
|
||||||
DROP TABLE IF EXISTS photo_url;
|
|
||||||
DROP TABLE IF EXISTS photo_exif;
|
|
|
@ -1,55 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS photo_exif (
|
|
||||||
exif_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
camera varchar(256),
|
|
||||||
maker varchar(256),
|
|
||||||
lens varchar(256),
|
|
||||||
dateShot timestamp NULL,
|
|
||||||
exposure varchar(256),
|
|
||||||
aperture float,
|
|
||||||
iso int(6),
|
|
||||||
focal_length float,
|
|
||||||
flash varchar(256),
|
|
||||||
orientation int(1),
|
|
||||||
exposure_program int(1),
|
|
||||||
|
|
||||||
PRIMARY KEY (exif_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS album (
|
|
||||||
album_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
title varchar(256) NOT NULL,
|
|
||||||
parent_album int,
|
|
||||||
owner_id int NOT NULL,
|
|
||||||
path varchar(1024) NOT NULL,
|
|
||||||
path_hash varchar(32) NOT NULL UNIQUE,
|
|
||||||
|
|
||||||
PRIMARY KEY (album_id),
|
|
||||||
FOREIGN KEY (parent_album) REFERENCES album(album_id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (owner_id) REFERENCES user(user_id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS photo (
|
|
||||||
photo_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
title varchar(256) NOT NULL,
|
|
||||||
path varchar(1024) NOT NULL,
|
|
||||||
path_hash varchar(32) NOT NULL UNIQUE,
|
|
||||||
album_id int NOT NULL,
|
|
||||||
exif_id int,
|
|
||||||
|
|
||||||
PRIMARY KEY (photo_id),
|
|
||||||
FOREIGN KEY (album_id) REFERENCES album(album_id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (exif_id) REFERENCES photo_exif(exif_id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS photo_url (
|
|
||||||
url_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
photo_id int NOT NULL,
|
|
||||||
photo_name varchar(512) NOT NULL,
|
|
||||||
width int NOT NULL,
|
|
||||||
height int NOT NULL,
|
|
||||||
purpose varchar(64) NOT NULL,
|
|
||||||
content_type varchar(64) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (url_id),
|
|
||||||
FOREIGN KEY (photo_id) REFERENCES photo(photo_id) ON DELETE CASCADE
|
|
||||||
);
|
|
|
@ -1 +0,0 @@
|
||||||
DROP TABLE IF EXISTS site_info;
|
|
|
@ -1,3 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS site_info (
|
|
||||||
initial_setup boolean NOT NULL DEFAULT TRUE
|
|
||||||
);
|
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
DROP TABLE IF EXISTS share_token;
|
|
|
@ -1,12 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS share_token (
|
|
||||||
token_id int AUTO_INCREMENT,
|
|
||||||
value char(24) NOT NULL UNIQUE,
|
|
||||||
owner_id int NOT NULL,
|
|
||||||
expire timestamp NULL DEFAULT NULL,
|
|
||||||
password varchar(256),
|
|
||||||
album_id int,
|
|
||||||
photo_id int,
|
|
||||||
|
|
||||||
PRIMARY KEY (token_id)
|
|
||||||
-- CHECK (album_id IS NOT NULL OR photo_id IS NOT NULL)
|
|
||||||
);
|
|
|
@ -1,9 +0,0 @@
|
||||||
-- Migrate all tables in database to use utf8 for better language support
|
|
||||||
ALTER TABLE access_token CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE album CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE photo CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE photo_exif CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE photo_url CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE share_token CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE site_info CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
||||||
ALTER TABLE user CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
|
|
@ -1,9 +0,0 @@
|
||||||
-- Migrate all tables in database to use utf8 for better language support
|
|
||||||
ALTER TABLE access_token CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE album CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE photo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE photo_exif CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE photo_url CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE share_token CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE site_info CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
|
@ -1,2 +0,0 @@
|
||||||
-- Add favorite attribute to photos
|
|
||||||
ALTER TABLE photo DROP favorite
|
|
|
@ -1,2 +0,0 @@
|
||||||
-- Add favorite attribute to photos
|
|
||||||
ALTER TABLE photo ADD favorite BOOL DEFAULT false
|
|
|
@ -1,43 +0,0 @@
|
||||||
-- Update database to hash indexed paths
|
|
||||||
|
|
||||||
CREATE PROCEDURE MigratePathHashIfNeeded()
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
-- Add path hash for photo table if it doesn't exist
|
|
||||||
IF NOT EXISTS( SELECT *
|
|
||||||
FROM INFORMATION_SCHEMA.COLUMNS
|
|
||||||
WHERE table_name = 'photo'
|
|
||||||
AND table_schema = DATABASE()
|
|
||||||
AND column_name = 'path_hash') THEN
|
|
||||||
|
|
||||||
-- Remove unique index from photo.path
|
|
||||||
ALTER TABLE photo DROP INDEX path;
|
|
||||||
|
|
||||||
-- Add path_hash and set it to the md5 hash based of the path attribute
|
|
||||||
ALTER TABLE photo ADD path_hash varchar(32) AFTER path;
|
|
||||||
UPDATE photo p SET path_hash = md5(p.path);
|
|
||||||
ALTER TABLE photo MODIFY path_hash varchar(32) NOT NULL UNIQUE;
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- Add path hash for album table if it doesn't exist
|
|
||||||
IF NOT EXISTS( SELECT *
|
|
||||||
FROM INFORMATION_SCHEMA.COLUMNS
|
|
||||||
WHERE table_name = 'album'
|
|
||||||
AND table_schema = DATABASE()
|
|
||||||
AND column_name = 'path_hash') THEN
|
|
||||||
|
|
||||||
-- Remove unique index from album.path
|
|
||||||
ALTER TABLE album DROP INDEX path;
|
|
||||||
|
|
||||||
-- Add path_hash and set it to the md5 hash based of the path attribute
|
|
||||||
ALTER TABLE album ADD path_hash varchar(32) AFTER path;
|
|
||||||
UPDATE album a SET path_hash = md5(a.path);
|
|
||||||
ALTER TABLE album MODIFY path_hash varchar(32) NOT NULL UNIQUE;
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END; -- MigratePathHashIfNeeded procedure end
|
|
||||||
|
|
||||||
CALL MigratePathHashIfNeeded();
|
|
||||||
DROP PROCEDURE MigratePathHashIfNeeded;
|
|
|
@ -1,17 +0,0 @@
|
||||||
|
|
||||||
ALTER TABLE media RENAME TO photo;
|
|
||||||
ALTER TABLE media_url RENAME TO photo_url;
|
|
||||||
ALTER TABLE media_exif RENAME TO photo_exif;
|
|
||||||
|
|
||||||
ALTER TABLE photo CHANGE COLUMN media_id photo_id int NOT NULL AUTO_INCREMENT;
|
|
||||||
ALTER TABLE photo_url CHANGE COLUMN media_id photo_id int NOT NULL;
|
|
||||||
ALTER TABLE photo_url CHANGE COLUMN media_name photo_name varchar(512) NOT NULL;
|
|
||||||
ALTER TABLE share_token CHANGE COLUMN media_id photo_id int;
|
|
||||||
|
|
||||||
ALTER TABLE photo DROP COLUMN media_type;
|
|
||||||
|
|
||||||
ALTER TABLE photo
|
|
||||||
DROP FOREIGN KEY photo_ibfk_3,
|
|
||||||
DROP COLUMN video_metadata_id;
|
|
||||||
|
|
||||||
DROP TABLE video_metadata;
|
|
|
@ -1,31 +0,0 @@
|
||||||
ALTER TABLE photo RENAME TO media;
|
|
||||||
ALTER TABLE photo_url RENAME TO media_url;
|
|
||||||
ALTER TABLE photo_exif RENAME TO media_exif;
|
|
||||||
|
|
||||||
ALTER TABLE media RENAME COLUMN photo_id TO media_id;
|
|
||||||
|
|
||||||
ALTER TABLE media_url
|
|
||||||
RENAME COLUMN photo_id TO media_id,
|
|
||||||
RENAME COLUMN photo_name TO media_name;
|
|
||||||
|
|
||||||
ALTER TABLE share_token RENAME COLUMN photo_id TO media_id;
|
|
||||||
|
|
||||||
CREATE TABLE video_metadata (
|
|
||||||
metadata_id int NOT NULL AUTO_INCREMENT,
|
|
||||||
|
|
||||||
width int(6) NOT NULL,
|
|
||||||
height int(6) NOT NULL,
|
|
||||||
duration double NOT NULL,
|
|
||||||
codec varchar(128),
|
|
||||||
framerate double,
|
|
||||||
bitrate int(24),
|
|
||||||
color_profile varchar(128),
|
|
||||||
audio varchar(128),
|
|
||||||
|
|
||||||
PRIMARY KEY (metadata_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE media
|
|
||||||
ADD COLUMN media_type varchar(64) NOT NULL DEFAULT "photo",
|
|
||||||
ADD COLUMN video_metadata_id int,
|
|
||||||
ADD FOREIGN KEY (video_metadata_id) REFERENCES video_metadata(metadata_id);
|
|
18
api/go.mod
18
api/go.mod
|
@ -3,9 +3,9 @@ module github.com/viktorstrate/photoview/api
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.11.3
|
github.com/99designs/gqlgen v0.12.1
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/agnivade/levenshtein v1.1.0 // indirect
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
github.com/docker/docker v1.13.1 // indirect
|
github.com/docker/docker v1.13.1 // indirect
|
||||||
|
@ -18,21 +18,15 @@ require (
|
||||||
github.com/h2non/filetype v1.1.0
|
github.com/h2non/filetype v1.1.0
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/matryer/moq v0.0.0-20200607124540-4638a53893e6 // indirect
|
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.3.2 // indirect
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/urfave/cli v1.22.4 // indirect
|
|
||||||
github.com/urfave/cli/v2 v2.2.0 // indirect
|
|
||||||
github.com/vektah/dataloaden v0.3.0 // indirect
|
|
||||||
github.com/vektah/gqlparser v1.3.1 // indirect
|
|
||||||
github.com/vektah/gqlparser/v2 v2.0.1
|
github.com/vektah/gqlparser/v2 v2.0.1
|
||||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
|
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0
|
||||||
github.com/xor-gate/goexif2 v1.1.0
|
github.com/xor-gate/goexif2 v1.1.0
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||||
golang.org/x/image v0.0.0-20200618115811-c13761719519
|
golang.org/x/image v0.0.0-20200801110659-972c09e46d76
|
||||||
golang.org/x/mod v0.3.0 // indirect
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc // indirect
|
||||||
golang.org/x/tools v0.0.0-20200622192924-4fd1c64487bf // indirect
|
|
||||||
gopkg.in/vansante/go-ffprobe.v2 v2.0.2
|
gopkg.in/vansante/go-ffprobe.v2 v2.0.2
|
||||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
81
api/go.sum
81
api/go.sum
|
@ -1,9 +1,5 @@
|
||||||
github.com/99designs/gqlgen v0.10.2 h1:FfjCqIWejHDJeLpQTI0neoZo5vDO3sdo5oNCucet3A0=
|
github.com/99designs/gqlgen v0.12.1 h1:Qfi6HDi6uDxGVKvz5kg8/5iP9YF2XqhwIoBKAt+Nt6M=
|
||||||
github.com/99designs/gqlgen v0.10.2/go.mod h1:aDB7oabSAyZ4kUHLEySsLxnWrBy3lA0A2gWKU+qoHwI=
|
github.com/99designs/gqlgen v0.12.1/go.mod h1:7zdGo6ry9u1YBp/qlb2uxSU5Mt2jQKLcBETQiKk+Bxo=
|
||||||
github.com/99designs/gqlgen v0.11.2 h1:qatIx2DY7YyaUIBd47ORY3Aj/+pJsPLoL7tyuuISJR0=
|
|
||||||
github.com/99designs/gqlgen v0.11.2/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4=
|
|
||||||
github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4=
|
|
||||||
github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
|
@ -11,19 +7,21 @@ github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+s
|
||||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||||
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
|
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
|
||||||
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
|
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
|
||||||
|
github.com/agnivade/levenshtein v1.1.0 h1:n6qGwyHG61v3ABce1rPVZklEYRT8NFpCMrpZdBUbYGM=
|
||||||
|
github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
|
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM=
|
||||||
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||||
|
@ -46,16 +44,8 @@ github.com/gorilla/mux v1.6.1 h1:KOwqsTYZdeuMacU7CxjMNYEKeBvLbxW+psodrbcEa3A=
|
||||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
|
||||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/h2non/filetype v1.0.10 h1:z+SJfnL6thYJ9kAST+6nPRXp1lMxnOVbMZHNYHMar0s=
|
|
||||||
github.com/h2non/filetype v1.0.10/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU=
|
|
||||||
github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
|
|
||||||
github.com/h2non/filetype v1.0.12/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
|
||||||
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
|
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
|
||||||
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||||
|
@ -73,19 +63,13 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg=
|
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg=
|
||||||
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||||
github.com/matryer/moq v0.0.0-20200607124540-4638a53893e6 h1:Cx1ZvZ3SQTli1nKee9qvJ/NJP3vt11s+ilM7NF3QSL8=
|
|
||||||
github.com/matryer/moq v0.0.0-20200607124540-4638a53893e6/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
|
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed h1:QP63yO3XEt8tJ1DgBsNjbOyBGjy2eHy9ITK4Eisr9rg=
|
|
||||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed/go.mod h1:HazDB3gS/i//QXMMRmTAV7Ni9gAi4mDNTH2HjZ5aVgU=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
|
@ -117,22 +101,10 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
|
||||||
github.com/urfave/cli v1.22.3 h1:FpNT6zq26xNpHZy08emi755QwzLPs6Pukqjlc7RfOMU=
|
|
||||||
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
|
||||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
|
||||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
|
||||||
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
|
||||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
|
|
||||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg=
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
||||||
github.com/vektah/dataloaden v0.3.0 h1:ZfVN2QD6swgvp+tDqdH/OIT/wu3Dhu0cus0k5gIZS84=
|
|
||||||
github.com/vektah/dataloaden v0.3.0/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
|
|
||||||
github.com/vektah/gqlparser v1.2.0 h1:ntkSCX7F5ZJKl+HIVnmLaO269MruasVpNiMOjX9kgo0=
|
|
||||||
github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
|
|
||||||
github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
|
github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
|
||||||
github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
|
github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
|
||||||
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
|
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
|
||||||
|
@ -141,59 +113,44 @@ github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlV
|
||||||
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM=
|
||||||
github.com/xor-gate/goexif2 v1.1.0 h1:OvTZ5iEvsDhRWFjV5xY3wT7uHFna28nSSP7ucau+cXQ=
|
github.com/xor-gate/goexif2 v1.1.0 h1:OvTZ5iEvsDhRWFjV5xY3wT7uHFna28nSSP7ucau+cXQ=
|
||||||
github.com/xor-gate/goexif2 v1.1.0/go.mod h1:eRjn3VSkAwpNpxEx/CGmd0zg0JFGL3akrSMxnJ581AY=
|
github.com/xor-gate/goexif2 v1.1.0/go.mod h1:eRjn3VSkAwpNpxEx/CGmd0zg0JFGL3akrSMxnJ581AY=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
|
golang.org/x/image v0.0.0-20200801110659-972c09e46d76 h1:U7GPaoQyQmX+CBRWXKrvRzWTbd+slqeSh8uARsIyhAw=
|
||||||
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
|
|
||||||
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
|
||||||
|
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
|
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
|
||||||
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
|
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
|
||||||
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200622192924-4fd1c64487bf h1:nXhK+swoyjE2slxjyxxa36VklQeCrnFyGuIZQGUsuxY=
|
|
||||||
golang.org/x/tools v0.0.0-20200622192924-4fd1c64487bf/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/vansante/go-ffprobe.v2 v2.0.2 h1:DdxSfFnlqeawPIVbIQEI6LR6OQHQNR7tNgWb2mWuC4w=
|
gopkg.in/vansante/go-ffprobe.v2 v2.0.2 h1:DdxSfFnlqeawPIVbIQEI6LR6OQHQNR7tNgWb2mWuC4w=
|
||||||
|
|
|
@ -87,10 +87,8 @@ type ComplexityRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaDownload struct {
|
MediaDownload struct {
|
||||||
Height func(childComplexity int) int
|
MediaURL func(childComplexity int) int
|
||||||
Title func(childComplexity int) int
|
Title func(childComplexity int) int
|
||||||
URL func(childComplexity int) int
|
|
||||||
Width func(childComplexity int) int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaExif struct {
|
MediaExif struct {
|
||||||
|
@ -109,9 +107,10 @@ type ComplexityRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaURL struct {
|
MediaURL struct {
|
||||||
Height func(childComplexity int) int
|
FileSize func(childComplexity int) int
|
||||||
URL func(childComplexity int) int
|
Height func(childComplexity int) int
|
||||||
Width func(childComplexity int) int
|
URL func(childComplexity int) int
|
||||||
|
Width func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutation struct {
|
Mutation struct {
|
||||||
|
@ -472,12 +471,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Media.VideoWeb(childComplexity), true
|
return e.complexity.Media.VideoWeb(childComplexity), true
|
||||||
|
|
||||||
case "MediaDownload.height":
|
case "MediaDownload.mediaUrl":
|
||||||
if e.complexity.MediaDownload.Height == nil {
|
if e.complexity.MediaDownload.MediaURL == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.MediaDownload.Height(childComplexity), true
|
return e.complexity.MediaDownload.MediaURL(childComplexity), true
|
||||||
|
|
||||||
case "MediaDownload.title":
|
case "MediaDownload.title":
|
||||||
if e.complexity.MediaDownload.Title == nil {
|
if e.complexity.MediaDownload.Title == nil {
|
||||||
|
@ -486,20 +485,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.MediaDownload.Title(childComplexity), true
|
return e.complexity.MediaDownload.Title(childComplexity), true
|
||||||
|
|
||||||
case "MediaDownload.url":
|
|
||||||
if e.complexity.MediaDownload.URL == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.MediaDownload.URL(childComplexity), true
|
|
||||||
|
|
||||||
case "MediaDownload.width":
|
|
||||||
if e.complexity.MediaDownload.Width == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.MediaDownload.Width(childComplexity), true
|
|
||||||
|
|
||||||
case "MediaEXIF.aperture":
|
case "MediaEXIF.aperture":
|
||||||
if e.complexity.MediaExif.Aperture == nil {
|
if e.complexity.MediaExif.Aperture == nil {
|
||||||
break
|
break
|
||||||
|
@ -584,6 +569,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.MediaExif.Media(childComplexity), true
|
return e.complexity.MediaExif.Media(childComplexity), true
|
||||||
|
|
||||||
|
case "MediaURL.fileSize":
|
||||||
|
if e.complexity.MediaURL.FileSize == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.MediaURL.FileSize(childComplexity), true
|
||||||
|
|
||||||
case "MediaURL.height":
|
case "MediaURL.height":
|
||||||
if e.complexity.MediaURL.Height == nil {
|
if e.complexity.MediaURL.Height == nil {
|
||||||
break
|
break
|
||||||
|
@ -1410,13 +1402,13 @@ type MediaURL {
|
||||||
width: Int!
|
width: Int!
|
||||||
"Height of the image in pixels"
|
"Height of the image in pixels"
|
||||||
height: Int!
|
height: Int!
|
||||||
|
"The file size of the resource in bytes"
|
||||||
|
fileSize: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaDownload {
|
type MediaDownload {
|
||||||
title: String!
|
title: String!
|
||||||
width: Int!
|
mediaUrl: MediaURL!
|
||||||
height: Int!
|
|
||||||
url: String!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaType {
|
enum MediaType {
|
||||||
|
@ -2971,7 +2963,7 @@ func (ec *executionContext) _MediaDownload_title(ctx context.Context, field grap
|
||||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _MediaDownload_width(ctx context.Context, field graphql.CollectedField, obj *models.MediaDownload) (ret graphql.Marshaler) {
|
func (ec *executionContext) _MediaDownload_mediaUrl(ctx context.Context, field graphql.CollectedField, obj *models.MediaDownload) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
@ -2988,7 +2980,7 @@ func (ec *executionContext) _MediaDownload_width(ctx context.Context, field grap
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.Width, nil
|
return obj.MediaURL, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -3000,77 +2992,9 @@ func (ec *executionContext) _MediaDownload_width(ctx context.Context, field grap
|
||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(int)
|
res := resTmp.(*models.MediaURL)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
return ec.marshalNMediaURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaURL(ctx, field.Selections, res)
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _MediaDownload_height(ctx context.Context, field graphql.CollectedField, obj *models.MediaDownload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "MediaDownload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.Height, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(int)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) _MediaDownload_url(ctx context.Context, field graphql.CollectedField, obj *models.MediaDownload) (ret graphql.Marshaler) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
|
||||||
ret = graphql.Null
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
fc := &graphql.FieldContext{
|
|
||||||
Object: "MediaDownload",
|
|
||||||
Field: field,
|
|
||||||
Args: nil,
|
|
||||||
IsMethod: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
|
||||||
ctx = rctx // use context from middleware stack in children
|
|
||||||
return obj.URL, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ec.Error(ctx, err)
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
if resTmp == nil {
|
|
||||||
if !graphql.HasFieldError(ctx, fc) {
|
|
||||||
ec.Errorf(ctx, "must not be null")
|
|
||||||
}
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
res := resTmp.(string)
|
|
||||||
fc.Result = res
|
|
||||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _MediaEXIF_id(ctx context.Context, field graphql.CollectedField, obj *models.MediaEXIF) (ret graphql.Marshaler) {
|
func (ec *executionContext) _MediaEXIF_id(ctx context.Context, field graphql.CollectedField, obj *models.MediaEXIF) (ret graphql.Marshaler) {
|
||||||
|
@ -3553,6 +3477,40 @@ func (ec *executionContext) _MediaURL_height(ctx context.Context, field graphql.
|
||||||
return ec.marshalNInt2int(ctx, field.Selections, res)
|
return ec.marshalNInt2int(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _MediaURL_fileSize(ctx context.Context, field graphql.CollectedField, obj *models.MediaURL) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "MediaURL",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.FileSize, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(int)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNInt2int(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_authorizeUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_authorizeUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
@ -7325,18 +7283,8 @@ func (ec *executionContext) _MediaDownload(ctx context.Context, sel ast.Selectio
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "width":
|
case "mediaUrl":
|
||||||
out.Values[i] = ec._MediaDownload_width(ctx, field, obj)
|
out.Values[i] = ec._MediaDownload_mediaUrl(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
case "height":
|
|
||||||
out.Values[i] = ec._MediaDownload_height(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
invalids++
|
|
||||||
}
|
|
||||||
case "url":
|
|
||||||
out.Values[i] = ec._MediaDownload_url(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
@ -7429,6 +7377,11 @@ func (ec *executionContext) _MediaURL(ctx context.Context, sel ast.SelectionSet,
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "fileSize":
|
||||||
|
out.Values[i] = ec._MediaURL_fileSize(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,8 @@ type Filter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaDownload struct {
|
type MediaDownload struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Width int `json:"width"`
|
MediaURL *MediaURL `json:"mediaUrl"`
|
||||||
Height int `json:"height"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package models
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"path"
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/viktorstrate/photoview/api/utils"
|
"github.com/viktorstrate/photoview/api/utils"
|
||||||
)
|
)
|
||||||
|
@ -14,6 +15,8 @@ type Media struct {
|
||||||
PathHash string
|
PathHash string
|
||||||
AlbumId int
|
AlbumId int
|
||||||
ExifId *int
|
ExifId *int
|
||||||
|
DateShot time.Time
|
||||||
|
DateImported time.Time
|
||||||
Favorite bool
|
Favorite bool
|
||||||
Type MediaType
|
Type MediaType
|
||||||
VideoMetadataId *int
|
VideoMetadataId *int
|
||||||
|
@ -41,12 +44,13 @@ type MediaURL struct {
|
||||||
Height int
|
Height int
|
||||||
Purpose MediaPurpose
|
Purpose MediaPurpose
|
||||||
ContentType string
|
ContentType string
|
||||||
|
FileSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMediaFromRow(row *sql.Row) (*Media, error) {
|
func NewMediaFromRow(row *sql.Row) (*Media, error) {
|
||||||
media := Media{}
|
media := Media{}
|
||||||
|
|
||||||
if err := row.Scan(&media.MediaID, &media.Title, &media.Path, &media.PathHash, &media.AlbumId, &media.ExifId, &media.Favorite, &media.Type, &media.VideoMetadataId); err != nil {
|
if err := row.Scan(&media.MediaID, &media.Title, &media.Path, &media.PathHash, &media.AlbumId, &media.ExifId, &media.DateShot, &media.DateImported, &media.Favorite, &media.Type, &media.VideoMetadataId); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +62,7 @@ func NewMediaFromRows(rows *sql.Rows) ([]*Media, error) {
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var media Media
|
var media Media
|
||||||
if err := rows.Scan(&media.MediaID, &media.Title, &media.Path, &media.PathHash, &media.AlbumId, &media.ExifId, &media.Favorite, &media.Type, &media.VideoMetadataId); err != nil {
|
if err := rows.Scan(&media.MediaID, &media.Title, &media.Path, &media.PathHash, &media.AlbumId, &media.ExifId, &media.DateShot, &media.DateImported, &media.Favorite, &media.Type, &media.VideoMetadataId); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
medias = append(medias, &media)
|
medias = append(medias, &media)
|
||||||
|
@ -84,7 +88,7 @@ func (p *MediaURL) URL() string {
|
||||||
func NewMediaURLFromRow(row *sql.Row) (*MediaURL, error) {
|
func NewMediaURLFromRow(row *sql.Row) (*MediaURL, error) {
|
||||||
url := MediaURL{}
|
url := MediaURL{}
|
||||||
|
|
||||||
if err := row.Scan(&url.UrlID, &url.MediaId, &url.MediaName, &url.Width, &url.Height, &url.Purpose, &url.ContentType); err != nil {
|
if err := row.Scan(&url.UrlID, &url.MediaId, &url.MediaName, &url.Width, &url.Height, &url.Purpose, &url.ContentType, &url.FileSize); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +100,7 @@ func NewMediaURLFromRows(rows *sql.Rows) ([]*MediaURL, error) {
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var url MediaURL
|
var url MediaURL
|
||||||
if err := rows.Scan(&url.UrlID, &url.MediaId, &url.MediaName, &url.Width, &url.Height, &url.Purpose, &url.ContentType); err != nil {
|
if err := rows.Scan(&url.UrlID, &url.MediaId, &url.MediaName, &url.Width, &url.Height, &url.Purpose, &url.ContentType, &url.FileSize); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
urls = append(urls, &url)
|
urls = append(urls, &url)
|
||||||
|
|
|
@ -18,6 +18,8 @@ type MediaEXIF struct {
|
||||||
Flash *string
|
Flash *string
|
||||||
Orientation *int
|
Orientation *int
|
||||||
ExposureProgram *int
|
ExposureProgram *int
|
||||||
|
GPSLatitude *float64
|
||||||
|
GPSLonitude *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (exif *MediaEXIF) Media() *Media {
|
func (exif *MediaEXIF) Media() *Media {
|
||||||
|
@ -31,7 +33,7 @@ func (exif *MediaEXIF) ID() int {
|
||||||
func NewMediaExifFromRow(row *sql.Row) (*MediaEXIF, error) {
|
func NewMediaExifFromRow(row *sql.Row) (*MediaEXIF, error) {
|
||||||
exif := MediaEXIF{}
|
exif := MediaEXIF{}
|
||||||
|
|
||||||
if err := row.Scan(&exif.ExifID, &exif.Camera, &exif.Maker, &exif.Lens, &exif.DateShot, &exif.Exposure, &exif.Aperture, &exif.Iso, &exif.FocalLength, &exif.Flash, &exif.Orientation, &exif.ExposureProgram); err != nil {
|
if err := row.Scan(&exif.ExifID, &exif.Camera, &exif.Maker, &exif.Lens, &exif.DateShot, &exif.Exposure, &exif.Aperture, &exif.Iso, &exif.FocalLength, &exif.Flash, &exif.Orientation, &exif.ExposureProgram, &exif.GPSLatitude, &exif.GPSLonitude); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,10 +108,8 @@ func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*mo
|
||||||
}
|
}
|
||||||
|
|
||||||
downloads = append(downloads, &models.MediaDownload{
|
downloads = append(downloads, &models.MediaDownload{
|
||||||
Title: title,
|
Title: title,
|
||||||
Width: url.Width,
|
MediaURL: url,
|
||||||
Height: url.Height,
|
|
||||||
URL: url.URL(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,13 +195,13 @@ type MediaURL {
|
||||||
width: Int!
|
width: Int!
|
||||||
"Height of the image in pixels"
|
"Height of the image in pixels"
|
||||||
height: Int!
|
height: Int!
|
||||||
|
"The file size of the resource in bytes"
|
||||||
|
fileSize: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaDownload {
|
type MediaDownload {
|
||||||
title: String!
|
title: String!
|
||||||
width: Int!
|
mediaUrl: MediaURL!
|
||||||
height: Int!
|
|
||||||
url: String!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaType {
|
enum MediaType {
|
||||||
|
|
|
@ -11,27 +11,33 @@ import (
|
||||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CleanupMedia(db *sql.DB, albumId int, albumPhotos []*models.Media) []error {
|
func CleanupMedia(db *sql.DB, albumId int, albumMedia []*models.Media) []error {
|
||||||
if len(albumPhotos) == 0 {
|
albumMediaIds := make([]interface{}, len(albumMedia))
|
||||||
return nil
|
for i, photo := range albumMedia {
|
||||||
|
albumMediaIds[i] = photo.MediaID
|
||||||
}
|
}
|
||||||
|
|
||||||
albumPhotoIds := make([]interface{}, len(albumPhotos))
|
// Delete missing media
|
||||||
for i, photo := range albumPhotos {
|
var rows *sql.Rows
|
||||||
albumPhotoIds[i] = photo.MediaID
|
var err error
|
||||||
|
|
||||||
|
// Select media from database that was not found on hard disk
|
||||||
|
if len(albumMedia) > 0 {
|
||||||
|
media_args := make([]interface{}, 0)
|
||||||
|
media_args = append(media_args, albumId)
|
||||||
|
media_args = append(media_args, albumMediaIds...)
|
||||||
|
|
||||||
|
media_questions := strings.Repeat("?,", len(albumMediaIds))[:len(albumMediaIds)*2-1]
|
||||||
|
rows, err = db.Query(
|
||||||
|
"SELECT media_id FROM media WHERE album_id = ? AND media_id NOT IN ("+media_questions+")",
|
||||||
|
media_args...,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
rows, err = db.Query(
|
||||||
|
"SELECT media_id FROM media WHERE album_id = ?",
|
||||||
|
albumId,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete missing photos
|
|
||||||
media_args := make([]interface{}, 0)
|
|
||||||
media_args = append(media_args, albumId)
|
|
||||||
media_args = append(media_args, albumPhotoIds...)
|
|
||||||
|
|
||||||
media_questions := strings.Repeat("?,", len(albumPhotoIds))[:len(albumPhotoIds)*2-1]
|
|
||||||
|
|
||||||
rows, err := db.Query(
|
|
||||||
"SELECT media_id FROM media WHERE album_id = ? AND media_id NOT IN ("+media_questions+")",
|
|
||||||
media_args...,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{errors.Wrap(err, "get media files to be deleted from database")}
|
return []error{errors.Wrap(err, "get media files to be deleted from database")}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +62,7 @@ func CleanupMedia(db *sql.DB, albumId int, albumPhotos []*models.Media) []error
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(deleted_media_ids) > 0 {
|
if len(deleted_media_ids) > 0 {
|
||||||
media_questions = strings.Repeat("?,", len(deleted_media_ids))[:len(deleted_media_ids)*2-1]
|
media_questions := strings.Repeat("?,", len(deleted_media_ids))[:len(deleted_media_ids)*2-1]
|
||||||
|
|
||||||
if _, err := db.Exec("DELETE FROM media WHERE media_id IN ("+media_questions+")", deleted_media_ids...); err != nil {
|
if _, err := db.Exec("DELETE FROM media WHERE media_id IN ("+media_questions+")", deleted_media_ids...); err != nil {
|
||||||
deleteErrors = append(deleteErrors, errors.Wrap(err, "delete old media from database"))
|
deleteErrors = append(deleteErrors, errors.Wrap(err, "delete old media from database"))
|
||||||
|
|
|
@ -18,6 +18,21 @@ type PhotoDimensions struct {
|
||||||
Height int
|
Height int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeImage(imagePath string) (image.Image, error) {
|
||||||
|
file, err := os.Open(imagePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to open file to decode image (%s)", imagePath)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
image, err := imaging.Decode(file, imaging.AutoOrientation(true))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to decode image (%s)", imagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return image, nil
|
||||||
|
}
|
||||||
|
|
||||||
func PhotoDimensionsFromRect(rect image.Rectangle) PhotoDimensions {
|
func PhotoDimensionsFromRect(rect image.Rectangle) PhotoDimensions {
|
||||||
return PhotoDimensions{
|
return PhotoDimensions{
|
||||||
Width: rect.Bounds().Max.X,
|
Width: rect.Bounds().Max.X,
|
||||||
|
@ -133,13 +148,7 @@ func (img *EncodeMediaData) EncodeHighRes(tx *sql.Tx, outputPath string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeThumbnail(inputPath string, outputPath string) (*PhotoDimensions, error) {
|
func EncodeThumbnail(inputPath string, outputPath string) (*PhotoDimensions, error) {
|
||||||
inputFile, err := os.Open(inputPath)
|
inputImage, err := DecodeImage(inputPath)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer inputFile.Close()
|
|
||||||
|
|
||||||
inputImage, _, err := image.Decode(inputFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -161,13 +170,7 @@ func (img *EncodeMediaData) photoImage(tx *sql.Tx) (image.Image, error) {
|
||||||
return img._photoImage, nil
|
return img._photoImage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
photoFile, err := os.Open(img.media.Path)
|
photoImg, err := DecodeImage(img.media.Path)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer photoFile.Close()
|
|
||||||
|
|
||||||
photoImg, _, err := image.Decode(photoFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, utils.HandleError("image decoding", err)
|
return nil, utils.HandleError("image decoding", err)
|
||||||
}
|
}
|
||||||
|
@ -184,37 +187,6 @@ func (img *EncodeMediaData) photoImage(tx *sql.Tx) (image.Image, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if orientation == nil {
|
|
||||||
defaultOrientation := 0
|
|
||||||
orientation = &defaultOrientation
|
|
||||||
}
|
|
||||||
|
|
||||||
switch *orientation {
|
|
||||||
case 2:
|
|
||||||
photoImg = imaging.FlipH(photoImg)
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
photoImg = imaging.Rotate180(photoImg)
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
photoImg = imaging.FlipV(photoImg)
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
photoImg = imaging.Transpose(photoImg)
|
|
||||||
break
|
|
||||||
case 6:
|
|
||||||
photoImg = imaging.Rotate270(photoImg)
|
|
||||||
break
|
|
||||||
case 7:
|
|
||||||
photoImg = imaging.Transverse(photoImg)
|
|
||||||
break
|
|
||||||
case 8:
|
|
||||||
photoImg = imaging.Rotate90(photoImg)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
img._photoImage = photoImg
|
img._photoImage = photoImg
|
||||||
return img._photoImage, nil
|
return img._photoImage, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,13 @@ func ScanEXIF(tx *sql.Tx, media *models.Media) (returnExif *models.MediaEXIF, re
|
||||||
|
|
||||||
date, err := exifTags.DateTime()
|
date, err := exifTags.DateTime()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
valueNames = append(valueNames, "dateShot")
|
valueNames = append(valueNames, "date_shot")
|
||||||
exifValues = append(exifValues, date)
|
exifValues = append(exifValues, date)
|
||||||
|
|
||||||
|
_, err := tx.Exec("UPDATE media SET date_shot = ? WHERE media_id = ?", date, media.MediaID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("WARN: Failed to update date_shot for media %s: %s", media.Title, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exposure, err := readRationalTag(exifTags, exif.ExposureTime, media)
|
exposure, err := readRationalTag(exifTags, exif.ExposureTime, media)
|
||||||
|
@ -148,6 +153,15 @@ func ScanEXIF(tx *sql.Tx, media *models.Media) (returnExif *models.MediaEXIF, re
|
||||||
exifValues = append(exifValues, *exposureProgram)
|
exifValues = append(exifValues, *exposureProgram)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lat, long, err := exifTags.LatLong()
|
||||||
|
if err == nil {
|
||||||
|
valueNames = append(valueNames, "gps_latitude")
|
||||||
|
exifValues = append(exifValues, lat)
|
||||||
|
|
||||||
|
valueNames = append(valueNames, "gps_longitude")
|
||||||
|
exifValues = append(exifValues, long)
|
||||||
|
}
|
||||||
|
|
||||||
if len(valueNames) == 0 {
|
if len(valueNames) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -265,16 +266,21 @@ func getMediaType(path string) (*MediaType, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPathMedia(path string, cache *AlbumScannerCache) bool {
|
func isPathMedia(mediaPath string, cache *AlbumScannerCache) bool {
|
||||||
mediaType, err := cache.GetMediaType(path)
|
mediaType, err := cache.GetMediaType(mediaPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ScannerError("%s (%s)", err, path)
|
ScannerError("%s (%s)", err, mediaPath)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore hidden files
|
||||||
|
if path.Base(mediaPath)[0:1] == "." {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if mediaType != nil {
|
if mediaType != nil {
|
||||||
// Make sure file isn't empty
|
// Make sure file isn't empty
|
||||||
fileStats, err := os.Stat(path)
|
fileStats, err := os.Stat(mediaPath)
|
||||||
if err != nil || fileStats.Size() == 0 {
|
if err != nil || fileStats.Size() == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -282,6 +288,6 @@ func isPathMedia(path string, cache *AlbumScannerCache) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("File is not a supported media %s\n", path)
|
log.Printf("File is not a supported media %s\n", mediaPath)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,8 +128,13 @@ func processPhoto(tx *sql.Tx, imageData *EncodeMediaData, photoCachePath *string
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)",
|
fileStats, err := os.Stat(baseImagePath)
|
||||||
photo.MediaID, highres_name, photoDimensions.Width, photoDimensions.Height, models.PhotoHighRes, "image/jpeg")
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "reading file stats of highres photo")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
photo.MediaID, highres_name, photoDimensions.Width, photoDimensions.Height, models.PhotoHighRes, "image/jpeg", fileStats.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrapf(err, "could not insert highres media url (%d, %s)", photo.MediaID, photo.Title)
|
return false, errors.Wrapf(err, "could not insert highres media url (%d, %s)", photo.MediaID, photo.Title)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +192,12 @@ func processPhoto(tx *sql.Tx, imageData *EncodeMediaData, photoCachePath *string
|
||||||
return false, errors.Wrap(err, "could not create thumbnail cached image")
|
return false, errors.Wrap(err, "could not create thumbnail cached image")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)", photo.MediaID, thumbnail_name, thumbSize.Width, thumbSize.Height, models.PhotoThumbnail, "image/jpeg")
|
fileStats, err := os.Stat(thumbOutputPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "reading file stats of thumbnail photo")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)", photo.MediaID, thumbnail_name, thumbSize.Width, thumbSize.Height, models.PhotoThumbnail, "image/jpeg", fileStats.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -250,7 +260,12 @@ func saveOriginalPhotoToDB(tx *sql.Tx, photo *models.Media, imageData *EncodeMed
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)", photo.MediaID, original_image_name, photoDimensions.Width, photoDimensions.Height, models.MediaOriginal, contentType)
|
fileStats, err := os.Stat(photo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "reading file stats of original photo")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)", photo.MediaID, original_image_name, photoDimensions.Width, photoDimensions.Height, models.MediaOriginal, contentType, fileStats.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Could not insert original photo url: %d, %s\n", photo.MediaID, photoName)
|
log.Printf("Could not insert original photo url: %d, %s\n", photo.MediaID, photoName)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -56,8 +57,13 @@ func processVideo(tx *sql.Tx, mediaData *EncodeMediaData, videoCachePath *string
|
||||||
return false, errors.Wrapf(err, "failed to read metadata for encoded web-video (%s)", video.Title)
|
return false, errors.Wrapf(err, "failed to read metadata for encoded web-video (%s)", video.Title)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)",
|
fileStats, err := os.Stat(webVideoPath)
|
||||||
video.MediaID, web_video_name, webMetadata.Width, webMetadata.Height, models.VideoWeb, "video/mp4")
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "reading file stats of web-optimized video")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
video.MediaID, web_video_name, webMetadata.Width, webMetadata.Height, models.VideoWeb, "video/mp4", fileStats.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrapf(err, "failed to insert encoded web-video into database (%s)", video.Title)
|
return false, errors.Wrapf(err, "failed to insert encoded web-video into database (%s)", video.Title)
|
||||||
}
|
}
|
||||||
|
@ -83,8 +89,13 @@ func processVideo(tx *sql.Tx, mediaData *EncodeMediaData, videoCachePath *string
|
||||||
return false, errors.Wrap(err, "get dimensions of video thumbnail image")
|
return false, errors.Wrap(err, "get dimensions of video thumbnail image")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type) VALUES (?, ?, ?, ?, ?, ?)",
|
fileStats, err := os.Stat(thumbImagePath)
|
||||||
video.MediaID, video_thumb_name, thumbDimensions.Width, thumbDimensions.Height, models.VideoThumbnail, "image/jpeg")
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "reading file stats of video thumbnail")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec("INSERT INTO media_url (media_id, media_name, width, height, purpose, content_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
video.MediaID, video_thumb_name, thumbDimensions.Width, thumbDimensions.Height, models.VideoThumbnail, "image/jpeg", fileStats.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrapf(err, "failed to insert video thumbnail image into database (%s)", video.Title)
|
return false, errors.Wrapf(err, "failed to insert video thumbnail image into database (%s)", video.Title)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package scanner
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -40,7 +41,12 @@ func ScanMedia(tx *sql.Tx, mediaPath string, albumId int, cache *AlbumScannerCac
|
||||||
mediaTypeText = "photo"
|
mediaTypeText = "photo"
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := tx.Exec("INSERT INTO media (title, path, path_hash, album_id, media_type) VALUES (?, ?, MD5(path), ?, ?)", mediaName, mediaPath, albumId, mediaTypeText)
|
stat, err := os.Stat(mediaPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := tx.Exec("INSERT INTO media (title, path, path_hash, album_id, media_type, date_shot) VALUES (?, ?, MD5(path), ?, ?, ?)", mediaName, mediaPath, albumId, mediaTypeText, stat.ModTime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, errors.Wrap(err, "could not insert media into database")
|
return nil, false, errors.Wrap(err, "could not insert media into database")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Chrome",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"url": "http://localhost:3000",
|
|
||||||
"webRoot": "${workspaceRoot}/src"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"description": "UI app for Photoview",
|
"description": "UI app for Photoview",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-env": "^7.10.2",
|
"@babel/preset-env": "^7.11.0",
|
||||||
"apollo-cache-inmemory": "^1.6.6",
|
"apollo-cache-inmemory": "^1.6.6",
|
||||||
"apollo-client": "^2.6.10",
|
"apollo-client": "^2.6.10",
|
||||||
"apollo-link": "^1.2.14",
|
"apollo-link": "^1.2.14",
|
||||||
|
@ -12,24 +12,24 @@
|
||||||
"apollo-link-error": "^1.1.13",
|
"apollo-link-error": "^1.1.13",
|
||||||
"apollo-link-http": "^1.5.17",
|
"apollo-link-http": "^1.5.17",
|
||||||
"apollo-link-ws": "^1.0.20",
|
"apollo-link-ws": "^1.0.20",
|
||||||
"babel-plugin-styled-components": "^1.10.7",
|
"babel-plugin-styled-components": "^1.11.1",
|
||||||
"copy-to-clipboard": "^3.3.1",
|
"copy-to-clipboard": "^3.3.1",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"graphql": "^15.1.0",
|
"graphql": "^15.3.0",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.11.0",
|
||||||
"parcel-bundler": "^1.12.4",
|
"parcel-bundler": "^1.12.4",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-apollo": "^3.1.5",
|
"react-apollo": "^3.1.5",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-lazyload": "^2.6.8",
|
"react-lazyload": "^2.6.9",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"semantic-ui-css": "^2.4.1",
|
"semantic-ui-css": "^2.4.1",
|
||||||
"semantic-ui-react": "^0.88.2",
|
"semantic-ui-react": "^1.2.0",
|
||||||
"styled-components": "^5.1.1",
|
"styled-components": "^5.1.1",
|
||||||
"subscriptions-transport-ws": "^0.9.16",
|
"subscriptions-transport-ws": "^0.9.18",
|
||||||
"url-join": "^4.0.1"
|
"url-join": "^4.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -37,16 +37,16 @@
|
||||||
"build": "parcel build src/index.html --no-source-maps"
|
"build": "parcel build src/index.html --no-source-maps"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.10.2",
|
"@babel/core": "^7.11.1",
|
||||||
"@babel/plugin-transform-runtime": "^7.10.1",
|
"@babel/plugin-transform-runtime": "^7.11.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-plugin-graphql-tag": "^2.5.0",
|
"babel-plugin-graphql-tag": "^3.0.0",
|
||||||
"babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
|
"babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
|
||||||
"eslint": "^7.2.0",
|
"eslint": "^7.7.0",
|
||||||
"eslint-plugin-react": "^7.20.0",
|
"eslint-plugin-react": "^7.20.6",
|
||||||
"eslint-plugin-react-hooks": "^4.0.4",
|
"eslint-plugin-react-hooks": "^4.1.0",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.2.5",
|
||||||
"lint-staged": "^10.2.10",
|
"lint-staged": "^10.2.11",
|
||||||
"parcel-plugin-sw-cache": "^0.3.1",
|
"parcel-plugin-sw-cache": "^0.3.1",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
"react-router-prop-types": "^1.0.4"
|
"react-router-prop-types": "^1.0.4"
|
||||||
|
|
|
@ -21,9 +21,8 @@ const adminQuery = gql`
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
/* margin-right: 500px; */
|
overflow: hidden;
|
||||||
/* display: grid;
|
position: relative;
|
||||||
grid-template-columns: 80px 1fr 500px; */
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const SideMenu = styled.div`
|
const SideMenu = styled.div`
|
||||||
|
@ -31,6 +30,18 @@ const SideMenu = styled.div`
|
||||||
width: 80px;
|
width: 80px;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding-top: 70px;
|
padding-top: 70px;
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
position: fixed;
|
||||||
|
background: white;
|
||||||
|
z-index: 10;
|
||||||
|
padding-top: 0;
|
||||||
|
display: flex;
|
||||||
|
bottom: 0;
|
||||||
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const Content = styled.div`
|
const Content = styled.div`
|
||||||
|
|
|
@ -31,6 +31,8 @@ const photoQuery = gql`
|
||||||
}
|
}
|
||||||
highRes {
|
highRes {
|
||||||
url
|
url
|
||||||
|
width
|
||||||
|
height
|
||||||
}
|
}
|
||||||
videoWeb {
|
videoWeb {
|
||||||
url
|
url
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
import React, { useState } from 'react'
|
import gql from 'graphql-tag'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import styled from 'styled-components'
|
import React, { useState } from 'react'
|
||||||
import RouterProps from 'react-router-prop-types'
|
import { useQuery } from 'react-apollo'
|
||||||
import { Route, Switch } from 'react-router-dom'
|
import { Route, Switch } from 'react-router-dom'
|
||||||
|
import RouterProps from 'react-router-prop-types'
|
||||||
|
import { Form, Header, Icon, Input, Message } from 'semantic-ui-react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { getSharePassword, saveSharePassword } from '../../authentication'
|
||||||
import AlbumSharePage from './AlbumSharePage'
|
import AlbumSharePage from './AlbumSharePage'
|
||||||
import MediaSharePage from './MediaSharePage'
|
import MediaSharePage from './MediaSharePage'
|
||||||
import { useQuery } from 'react-apollo'
|
|
||||||
import gql from 'graphql-tag'
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Header,
|
|
||||||
Form,
|
|
||||||
Button,
|
|
||||||
Input,
|
|
||||||
Icon,
|
|
||||||
Message,
|
|
||||||
} from 'semantic-ui-react'
|
|
||||||
import { saveSharePassword, getSharePassword } from '../../authentication'
|
|
||||||
|
|
||||||
const shareTokenQuery = gql`
|
const shareTokenQuery = gql`
|
||||||
query SharePageToken($token: String!, $password: String) {
|
query SharePageToken($token: String!, $password: String) {
|
||||||
|
@ -61,9 +53,12 @@ const shareTokenQuery = gql`
|
||||||
}
|
}
|
||||||
downloads {
|
downloads {
|
||||||
title
|
title
|
||||||
url
|
mediaUrl {
|
||||||
width
|
url
|
||||||
height
|
width
|
||||||
|
height
|
||||||
|
fileSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
highRes {
|
highRes {
|
||||||
url
|
url
|
||||||
|
|
|
@ -9,7 +9,7 @@ const Container = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
`
|
`
|
||||||
|
|
||||||
const AlbumBoxes = ({ loading, error, albums, getCustomLink }) => {
|
const AlbumBoxes = ({ error, albums, getCustomLink }) => {
|
||||||
if (error) return <div>Error {error.message}</div>
|
if (error) return <div>Error {error.message}</div>
|
||||||
|
|
||||||
let albumElements = []
|
let albumElements = []
|
||||||
|
@ -28,12 +28,7 @@ const AlbumBoxes = ({ loading, error, albums, getCustomLink }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <Container>{albumElements}</Container>
|
||||||
<Container>
|
|
||||||
{/* <Loader active={loading}>Loading albums</Loader> */}
|
|
||||||
{albumElements}
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AlbumBoxes.propTypes = {
|
AlbumBoxes.propTypes = {
|
||||||
|
|
|
@ -21,6 +21,15 @@ const Title = styled.h1`
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
padding: 2px 12px;
|
padding: 2px 12px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-width: 245px;
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
min-width: auto;
|
||||||
|
|
||||||
|
& span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const Logo = styled.img`
|
const Logo = styled.img`
|
||||||
|
|
|
@ -12,6 +12,10 @@ const Container = styled.div`
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export let MessageState = {
|
export let MessageState = {
|
||||||
|
|
|
@ -15,6 +15,11 @@ const Gallery = styled.div`
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: -4px;
|
margin: -4px;
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
/* Compensate for tab bar on mobile */
|
||||||
|
margin-bottom: 76px;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const PhotoFiller = styled.div`
|
const PhotoFiller = styled.div`
|
||||||
|
|
|
@ -4,14 +4,10 @@ import PropTypes from 'prop-types'
|
||||||
const getProtectedUrl = url => {
|
const getProtectedUrl = url => {
|
||||||
const imgUrl = new URL(url)
|
const imgUrl = new URL(url)
|
||||||
|
|
||||||
if (localStorage.getItem('token') == null) {
|
const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
|
||||||
// Get share token if not authorized
|
if (tokenRegex) {
|
||||||
|
const token = tokenRegex[1]
|
||||||
const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
|
imgUrl.searchParams.set('token', token)
|
||||||
if (tokenRegex) {
|
|
||||||
const token = tokenRegex[1]
|
|
||||||
imgUrl.searchParams.set('token', token)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return imgUrl.href
|
return imgUrl.href
|
||||||
|
@ -20,76 +16,30 @@ const getProtectedUrl = url => {
|
||||||
/**
|
/**
|
||||||
* An image that needs authorization to load
|
* An image that needs authorization to load
|
||||||
*/
|
*/
|
||||||
export const ProtectedImage = ({ src, ...props }) => {
|
export const ProtectedImage = ({ src, ...props }) => (
|
||||||
// const [imgSrc, setImgSrc] = useState(null)
|
<img
|
||||||
|
key={src}
|
||||||
// useEffect(() => {
|
{...props}
|
||||||
// if (imageCache[src]) return
|
src={getProtectedUrl(src)}
|
||||||
|
crossOrigin="use-credentials"
|
||||||
// const fetchController = new AbortController()
|
/>
|
||||||
// let canceled = false
|
)
|
||||||
|
|
||||||
// setImgSrc('')
|
|
||||||
|
|
||||||
// const imgUrl = new URL(src)
|
|
||||||
// const fetchHeaders = {}
|
|
||||||
|
|
||||||
// if (localStorage.getItem('token') == null) {
|
|
||||||
// // Get share token if not authorized
|
|
||||||
|
|
||||||
// const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
|
|
||||||
// if (tokenRegex) {
|
|
||||||
// const token = tokenRegex[1]
|
|
||||||
// imgUrl.searchParams.set('token', token)
|
|
||||||
|
|
||||||
// const tokenPassword = sessionStorage.getItem(`share-token-pw-${token}`)
|
|
||||||
// if (tokenPassword) {
|
|
||||||
// fetchHeaders['TokenPassword'] = tokenPassword
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fetchProtectedImage(imgUrl.href, {
|
|
||||||
// signal: fetchController.signal,
|
|
||||||
// headers: fetchHeaders,
|
|
||||||
// })
|
|
||||||
// .then(newSrc => {
|
|
||||||
// if (!canceled) {
|
|
||||||
// setImgSrc(newSrc)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .catch(error => {
|
|
||||||
// console.log('Fetch image error', error.message)
|
|
||||||
// })
|
|
||||||
|
|
||||||
// return function cleanup() {
|
|
||||||
// canceled = true
|
|
||||||
// fetchController.abort()
|
|
||||||
// }
|
|
||||||
// }, [src])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<img {...props} src={getProtectedUrl(src)} crossOrigin="use-credentials" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtectedImage.propTypes = {
|
ProtectedImage.propTypes = {
|
||||||
src: PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProtectedVideo = ({ media, ...props }) => {
|
export const ProtectedVideo = ({ media, ...props }) => (
|
||||||
return (
|
<video
|
||||||
<video
|
{...props}
|
||||||
{...props}
|
controls
|
||||||
controls
|
key={media.id}
|
||||||
key={media.id}
|
crossOrigin="use-credentials"
|
||||||
crossOrigin="use-credentials"
|
poster={getProtectedUrl(media.thumbnail.url)}
|
||||||
poster={getProtectedUrl(media.thumbnail.url)}
|
>
|
||||||
>
|
<source src={getProtectedUrl(media.videoWeb.url)} type="video/mp4" />
|
||||||
<source src={getProtectedUrl(media.videoWeb.url)} type="video/mp4" />
|
</video>
|
||||||
</video>
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ProtectedVideo.propTypes = {
|
ProtectedVideo.propTypes = {
|
||||||
media: PropTypes.object.isRequired,
|
media: PropTypes.object.isRequired,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { ProtectedImage, ProtectedVideo } from '../ProtectedMedia'
|
import { ProtectedImage, ProtectedVideo } from '../ProtectedMedia'
|
||||||
|
|
|
@ -18,6 +18,7 @@ const OverlayButton = styled.button`
|
||||||
height: 64px;
|
height: 64px;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { createContext } from 'react'
|
import React, { createContext } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
import { Icon } from 'semantic-ui-react'
|
||||||
|
|
||||||
const SidebarContainer = styled.div`
|
const SidebarContainer = styled.div`
|
||||||
width: 28vw;
|
width: 28vw;
|
||||||
|
@ -17,8 +18,23 @@ const SidebarContainer = styled.div`
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: calc(100vw - 85px);
|
/* full height - header - tabbar */
|
||||||
transform: translateX(100vw);
|
height: calc(100% - 60px - 80px);
|
||||||
|
max-width: min(calc(100vw - 85px), 400px);
|
||||||
|
${({ highlighted }) => `right: ${highlighted ? 0 : -100}%;`}
|
||||||
|
padding-top: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: right 200ms ease-in-out;
|
||||||
|
`
|
||||||
|
|
||||||
|
const SidebarDismissButton = styled(Icon)`
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -46,8 +62,14 @@ class Sidebar extends React.Component {
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
<SidebarContext.Consumer>
|
<SidebarContext.Consumer>
|
||||||
{value => (
|
{value => (
|
||||||
<SidebarContainer>
|
<SidebarContainer highlighted={value.content != null}>
|
||||||
{value.content}
|
{value.content}
|
||||||
|
<SidebarDismissButton
|
||||||
|
name="angle double right"
|
||||||
|
size="big"
|
||||||
|
link
|
||||||
|
onClick={() => this.setState({ content: null })}
|
||||||
|
/>
|
||||||
<div style={{ height: 100 }}></div>
|
<div style={{ height: 100 }}></div>
|
||||||
</SidebarContainer>
|
</SidebarContainer>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -14,9 +14,12 @@ const downloadQuery = gql`
|
||||||
id
|
id
|
||||||
downloads {
|
downloads {
|
||||||
title
|
title
|
||||||
url
|
mediaUrl {
|
||||||
width
|
url
|
||||||
height
|
width
|
||||||
|
height
|
||||||
|
fileSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,10 +174,14 @@ const SidebarDownload = ({ photo }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloadRows = downloads.map(x => (
|
let downloadRows = downloads.map(x => (
|
||||||
<DownloadTableRow key={x.url} onClick={() => downloadPhoto(x.url)}>
|
<DownloadTableRow
|
||||||
|
key={x.mediaUrl.url}
|
||||||
|
onClick={() => downloadPhoto(x.mediaUrl.url)}
|
||||||
|
>
|
||||||
<Table.Cell>{`${x.title}`}</Table.Cell>
|
<Table.Cell>{`${x.title}`}</Table.Cell>
|
||||||
<Table.Cell>{`${x.width} x ${x.height}`}</Table.Cell>
|
<Table.Cell>{`${x.mediaUrl.width} x ${x.mediaUrl.height}`}</Table.Cell>
|
||||||
<Table.Cell>{extractExtension(x.url)}</Table.Cell>
|
<Table.Cell>{`${formatBytes(x.mediaUrl.fileSize)}`}</Table.Cell>
|
||||||
|
<Table.Cell>{extractExtension(x.mediaUrl.url)}</Table.Cell>
|
||||||
</DownloadTableRow>
|
</DownloadTableRow>
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -187,6 +194,7 @@ const SidebarDownload = ({ photo }) => {
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.HeaderCell>Name</Table.HeaderCell>
|
<Table.HeaderCell>Name</Table.HeaderCell>
|
||||||
<Table.HeaderCell>Dimensions</Table.HeaderCell>
|
<Table.HeaderCell>Dimensions</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell>Size</Table.HeaderCell>
|
||||||
<Table.HeaderCell>Type</Table.HeaderCell>
|
<Table.HeaderCell>Type</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
|
|
Loading…
Reference in New Issue