Rename "Import" to "Library" and add tabs to page

This commit is contained in:
Michael Mayer
2019-06-14 12:16:59 -07:00
parent 2ae35b0d8b
commit 2005da9a25
10 changed files with 215 additions and 93 deletions

View File

@@ -165,13 +165,13 @@
</v-list-tile>
</v-list-group>
<v-list-tile to="/import" @click="">
<v-list-tile to="/library" @click="">
<v-list-tile-action>
<v-icon>camera_roll</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>Import</v-list-tile-title>
<v-list-tile-title>Library</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>

View File

@@ -47,6 +47,14 @@ main {
color: #00B8D4;
}
#photoprism main a.v-tabs__item {
color: #546E7A;
}
#photoprism main a.v-tabs__item--active {
color: #455A64;
}
#photoprism .v-badge__badge {
font-size: 12px;
height: 19px;

View File

@@ -0,0 +1,54 @@
<template>
<div class="p-page p-page-library">
<v-tabs
v-model="active"
flat
grow
color="blue-grey lighten-4"
slider-color="blue-grey darken-1"
height="64"
>
<v-tab id="tab-upload" ripple>
Upload
</v-tab>
<v-tab-item>
<p-tab-upload></p-tab-upload>
</v-tab-item>
<v-tab id="tab-import" ripple>
Import
</v-tab>
<v-tab-item>
<p-tab-import></p-tab-import>
</v-tab-item>
<v-tab id="tab-index" ripple>
Index
</v-tab>
<v-tab-item>
<p-tab-index></p-tab-index>
</v-tab-item>
</v-tabs>
</div>
</template>
<script>
import uploadTab from "pages/library/upload.vue";
import importTab from "pages/library/import.vue";
import indexTab from "pages/library/index.vue";
export default {
name: 'p-page-library',
components: {
'p-tab-upload': uploadTab,
'p-tab-import': importTab,
'p-tab-index': indexTab,
},
data() {
return {
active: 0,
}
},
methods: {}
};
</script>

View File

@@ -0,0 +1,35 @@
<template>
<div class="p-tab p-tab-import">
<v-form ref="form" class="p-photo-import" lazy-validation @submit.prevent="submit" dense>
<input type="file" ref="upload" multiple @change.stop="upload()" class="d-none">
<v-container fluid>
<h3 class="subheading">Only available using the command-line interface at the moment</h3>
<p class="body-1 mt-2">
Issues labeled <a href="https://github.com/photoprism/photoprism/labels/help%20wanted">help
wanted</a> /
<a href="https://github.com/photoprism/photoprism/labels/easy">easy</a> can be good (first)
contributions.
Our <a href="https://github.com/photoprism/photoprism/wiki">Developer Guide</a> contains all
information
necessary to get you started.
</p>
</v-container>
</v-form>
</div>
</template>
<script>
import axios from "axios";
import Event from "pubsub-js";
export default {
name: 'p-tab-import',
data() {
return {
}
},
methods: {
}
};
</script>

View File

@@ -0,0 +1,35 @@
<template>
<div class="p-tab p-tab-index">
<v-form ref="form" class="p-photo-index" lazy-validation @submit.prevent="submit" dense>
<input type="file" ref="upload" multiple @change.stop="upload()" class="d-none">
<v-container fluid>
<h3 class="subheading">Only available using the command-line interface at the moment</h3>
<p class="body-1 mt-2">
Issues labeled <a href="https://github.com/photoprism/photoprism/labels/help%20wanted">help
wanted</a> /
<a href="https://github.com/photoprism/photoprism/labels/easy">easy</a> can be good (first)
contributions.
Our <a href="https://github.com/photoprism/photoprism/wiki">Developer Guide</a> contains all
information
necessary to get you started.
</p>
</v-container>
</v-form>
</div>
</template>
<script>
import axios from "axios";
import Event from "pubsub-js";
export default {
name: 'p-tab-index',
data() {
return {
}
},
methods: {
}
};
</script>

View File

@@ -1,56 +1,20 @@
<template>
<div class="p-page p-page-import">
<v-form ref="form" class="p-photo-import" lazy-validation @submit.prevent="submit" dense>
<div class="p-tab p-tab-upload">
<v-form ref="form" class="p-photo-upload" lazy-validation @submit.prevent="submit" dense>
<input type="file" ref="upload" multiple @change.stop="upload()" class="d-none">
<v-toolbar flat color="blue-grey lighten-4">
<v-toolbar-title>Import</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-container fluid>
<p class="subheading">
<span v-if="total === 0">Select photos to start import...</span>
<span v-else-if="total > 0 && completed < 100">Adding {{current}} of {{total}}...</span>
<span v-if="total === 0">Select photos to start upload...</span>
<span v-else-if="total > 0 && completed < 100">Uploaded {{current}} of {{total}}...</span>
<span v-else-if="completed === 100">Done.</span>
</p>
<v-progress-linear color="blue-grey" v-model="completed"></v-progress-linear>
<v-container grid-list-xs fluid class="pa-0 p-photos p-photo-mosaic">
<v-layout row wrap>
<v-flex
v-for="(file, index) in uploads"
:key="index"
class="p-photo"
xs4 sm3 md2 lg1 d-flex
>
<v-card tile class="elevation-2 ma-2">
<v-img :src="file.data"
aspect-ratio="1"
:title="file.name"
class="grey lighten-2"
>
<v-layout
slot="placeholder"
fill-height
align-center
justify-center
ma-0
>
<v-progress-circular indeterminate
color="grey lighten-5"></v-progress-circular>
</v-layout>
</v-img>
</v-card>
</v-flex>
</v-layout>
</v-container>
<v-btn
:disabled="busy"
color="blue-grey lighten-2"
color="blue-grey"
class="white--text ml-0"
depressed
@click.stop="uploadDialog()"
@@ -68,7 +32,7 @@
import Event from "pubsub-js";
export default {
name: 'p-page-import',
name: 'p-tab-upload',
data() {
return {
selected: [],
@@ -77,6 +41,7 @@
current: 0,
total: 0,
completed: 0,
started: 0,
}
},
methods: {
@@ -87,6 +52,7 @@
this.$refs.upload.click();
},
upload() {
this.started = Date.now();
this.selected = this.$refs.upload.files;
this.busy = true;
this.total = this.selected.length;
@@ -94,7 +60,7 @@
this.completed = 0;
this.uploads = [];
if(!this.total) {
if (!this.total) {
return
}
@@ -104,24 +70,12 @@
async function performUpload(ctx) {
for (let i = 0; i < ctx.selected.length; i++) {
ctx.current = i + 1;
ctx.completed = Math.round((ctx.current / ctx.total) * 100);
let file = ctx.selected[i];
let formData = new FormData();
formData.append('files', file);
if (file.type.match('image.*')) {
const reader = new FileReader;
reader.onload = e => {
ctx.uploads.push({name: file.name, data: e.target.result});
};
reader.readAsDataURL(file)
}
await axios.post('/api/v1/upload',
await axios.post('/api/v1/upload/' + ctx.started,
formData,
{
headers: {
@@ -129,6 +83,8 @@
}
}
).then(function () {
ctx.current = i + 1;
ctx.completed = Math.round((ctx.current / ctx.total) * 100);
}).catch(function () {
Event.publish("alert.error", "Upload failed");
});
@@ -136,8 +92,12 @@
}
performUpload(this).then(() => {
axios.post('/api/v1/import/upload/' + this.started).then(function () {
Event.publish("alert.success", "Finished indexing upload");
});
Event.publish("ajax.end");
Event.publish("alert.success", "Photos uploaded and imported");
Event.publish("alert.success", "Upload completed");
this.busy = false;
});
},

View File

@@ -3,7 +3,7 @@ import Places from "pages/places.vue";
import Labels from "pages/labels.vue";
import Events from "pages/events.vue";
import People from "pages/people.vue";
import Import from "pages/import.vue";
import Library from "pages/library.vue";
import Share from "pages/share.vue";
import Settings from "pages/settings.vue";
import Todo from "pages/todo.vue";
@@ -64,10 +64,10 @@ export default [
meta: {area: "Albums"},
},
{
name: "Import",
path: "/import",
component: Import,
meta: {area: "Import"},
name: "Library",
path: "/library",
component: Library,
meta: {area: "Library"},
},
{
name: "Share",

52
internal/api/import.go Normal file
View File

@@ -0,0 +1,52 @@
package api
import (
"fmt"
"net/http"
"time"
"github.com/photoprism/photoprism/internal/config"
log "github.com/sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/photoprism"
)
var importer *photoprism.Importer
func initImporter(conf *config.Config) {
if importer != nil {
return
}
tensorFlow := photoprism.NewTensorFlow(conf)
indexer := photoprism.NewIndexer(conf, tensorFlow)
converter := photoprism.NewConverter(conf)
importer = photoprism.NewImporter(conf, indexer, converter)
}
// POST /api/v1/import
func Import(router *gin.RouterGroup, conf *config.Config) {
router.POST("/import/*path", func(c *gin.Context) {
start := time.Now()
path := conf.ImportPath()
if subPath := c.Param("path"); subPath != "" {
log.Debugf("import sub path: %s", subPath)
path = path + subPath
}
log.Infof("importing photos from %s", path)
initImporter(conf)
importer.ImportPhotosFromDirectory(path)
elapsed := time.Since(start)
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("completed import in %s", elapsed)})
})
}

View File

@@ -9,33 +9,16 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/util"
uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/photoprism"
)
var importer *photoprism.Importer
func initImporter(conf *config.Config) {
if importer != nil {
return
}
tensorFlow := photoprism.NewTensorFlow(conf)
indexer := photoprism.NewIndexer(conf, tensorFlow)
converter := photoprism.NewConverter(conf)
importer = photoprism.NewImporter(conf, indexer, converter)
}
// POST /api/v1/upload
// POST /api/v1/upload/:path
func Upload(router *gin.RouterGroup, conf *config.Config) {
router.POST("/upload", func(c *gin.Context) {
router.POST("/upload/:path", func(c *gin.Context) {
start := time.Now()
subPath := c.Param("path")
form, err := c.MultipartForm()
@@ -49,7 +32,7 @@ func Upload(router *gin.RouterGroup, conf *config.Config) {
files := form.File["files"]
path := fmt.Sprintf("%s/uploads/%s", conf.ImportPath(), uuid.NewV4())
path := fmt.Sprintf("%s/upload/%s", conf.ImportPath(), subPath)
if err := os.MkdirAll(path, os.ModePerm); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": util.UcFirst(err.Error())})
@@ -65,16 +48,10 @@ func Upload(router *gin.RouterGroup, conf *config.Config) {
}
}
log.Infof("importing photos from %s", conf.ImportPath())
initImporter(conf)
importer.ImportPhotosFromDirectory(conf.ImportPath())
elapsed := time.Since(start)
log.Infof("%d files imported in %s", len(files), elapsed)
log.Infof("%d files uploaded in %s", len(files), elapsed)
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%d files imported in %s", len(files), elapsed)})
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%d files uploaded in %s", len(files), elapsed)})
})
}

View File

@@ -31,6 +31,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
api.LabelThumbnail(v1, conf)
api.Upload(v1, conf)
api.Import(v1, conf)
}
// Default HTML page (client-side routing implemented via Vue.js)