Download: Add Disabled, Originals, MediaRaw & MediaSidecar Flags #2234
Extends DownloadSettings with 4 additional options: - Name: File name pattern for downloaded files (existed) - Disabled: Disables downloads - Originals: Only download files stored in "originals" folder - MediaRaw: Include RAW image files - MediaSidecar: Include metadata sidecar files (JSON, XMP, YAML)
This commit is contained in:
parent
0a9f6a72bc
commit
92e6c4fe1e
335 changed files with 3606 additions and 2708 deletions
|
@ -31,30 +31,13 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
useradd -m -g 1000 -u 1000 -d /photoprism -G video,render photoprism && \
|
||||
chmod 777 /photoprism && \
|
||||
apt-get update && apt-get -qq dist-upgrade && apt-get -qq install --no-install-recommends \
|
||||
ca-certificates \
|
||||
jq \
|
||||
zip \
|
||||
gpg \
|
||||
lshw \
|
||||
wget \
|
||||
curl \
|
||||
make \
|
||||
sudo \
|
||||
bash \
|
||||
sqlite3 \
|
||||
tzdata \
|
||||
libc6 \
|
||||
libatomic1 \
|
||||
libheif-examples \
|
||||
librsvg2-bin \
|
||||
exiftool \
|
||||
darktable \
|
||||
rawtherapee \
|
||||
ffmpeg \
|
||||
ffmpegthumbnailer \
|
||||
libavcodec-extra \
|
||||
mariadb-client \
|
||||
&& \
|
||||
libc6 ca-certificates sudo bash tzdata \
|
||||
gpg zip unzip wget curl rsync make nano \
|
||||
jq lsof lshw sqlite3 mariadb-client \
|
||||
exiftool darktable rawtherapee libheif-examples librsvg2-bin \
|
||||
ffmpeg ffmpegthumbnailer libavcodec-extra libwebm1 \
|
||||
libmatroska7 libdvdread8 libebml5 libgav1-0 libatomic1 \
|
||||
libx264-163 libx265-199 && \
|
||||
install -d -m 0777 -o 1000 -g 1000 \
|
||||
/var/lib/photoprism \
|
||||
/tmp/photoprism \
|
||||
|
|
|
@ -38,60 +38,20 @@ RUN echo 'APT::Acquire::Retries "3";' > /etc/apt/apt.conf.d/80retries && \
|
|||
useradd -m -g 1000 -u 1000 -d /photoprism -G video,render photoprism && \
|
||||
chmod 777 /photoprism && \
|
||||
apt-get update && apt-get -qq dist-upgrade && apt-get -qq install --no-install-recommends \
|
||||
apt-utils \
|
||||
gpg \
|
||||
pkg-config \
|
||||
software-properties-common \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
gcc \
|
||||
g++ \
|
||||
sudo \
|
||||
bash \
|
||||
make \
|
||||
nano \
|
||||
lsof \
|
||||
lshw \
|
||||
wget \
|
||||
curl \
|
||||
rsync \
|
||||
jq \
|
||||
git \
|
||||
zip \
|
||||
unzip \
|
||||
gettext \
|
||||
chromium \
|
||||
chromium-driver \
|
||||
chromium-sandbox \
|
||||
firefox-esr \
|
||||
sqlite3 \
|
||||
libc6-dev \
|
||||
libssl-dev \
|
||||
libxft-dev \
|
||||
libhdf5-serial-dev \
|
||||
libpng-dev \
|
||||
libheif-examples \
|
||||
librsvg2-bin \
|
||||
libzmq3-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
libnss3 \
|
||||
libfreetype6 \
|
||||
libfreetype6-dev \
|
||||
libfontconfig1 \
|
||||
libfontconfig1-dev \
|
||||
fonts-roboto \
|
||||
tzdata \
|
||||
exiftool \
|
||||
rawtherapee \
|
||||
ffmpeg \
|
||||
darktable \
|
||||
ffmpegthumbnailer \
|
||||
libavcodec-extra \
|
||||
davfs2 \
|
||||
chrpath \
|
||||
apache2-utils \
|
||||
mariadb-client \
|
||||
libc6 ca-certificates sudo bash tzdata \
|
||||
gpg zip unzip wget curl rsync make nano \
|
||||
jq lsof lshw sqlite3 mariadb-client \
|
||||
exiftool darktable rawtherapee libheif-examples librsvg2-bin \
|
||||
ffmpeg ffmpegthumbnailer libavcodec-extra libwebm1 \
|
||||
libmatroska7 libdvdread8 libebml5 libgav1-0 libatomic1 \
|
||||
libx264-163 libx265-199 && \
|
||||
apt-get -qq install --no-install-recommends \
|
||||
apt-utils pkg-config software-properties-common \
|
||||
build-essential gcc g++ git gettext davfs2 chrpath apache2-utils \
|
||||
chromium chromium-driver chromium-sandbox firefox-esr \
|
||||
libx264-dev libx265-dev libpng-dev libxft-dev \
|
||||
libc6-dev libhdf5-serial-dev libzmq3-dev libssl-dev libnss3 \
|
||||
libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev fonts-roboto \
|
||||
&& \
|
||||
/scripts/install-nodejs.sh && \
|
||||
/scripts/install-tensorflow.sh && \
|
||||
|
|
|
@ -145,25 +145,100 @@ export default class Util {
|
|||
start = now;
|
||||
}
|
||||
|
||||
static capitalize(s) {
|
||||
if (!s || s === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return s.replace(/\w\S*/g, (w) => w.replace(/^\w/, (c) => c.toUpperCase()));
|
||||
}
|
||||
|
||||
static fileType(value) {
|
||||
if (!value || typeof value !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case "raw":
|
||||
return "Unprocessed Sensor Data (RAW)";
|
||||
case "mov":
|
||||
case "qt":
|
||||
return "Apple QuickTime";
|
||||
case "bmp":
|
||||
return "Bitmap";
|
||||
case "png":
|
||||
return "Portable Network Graphics";
|
||||
case "tiff":
|
||||
return "TIFF";
|
||||
case "gif":
|
||||
return "GIF";
|
||||
case "avc":
|
||||
case "avc1":
|
||||
return "Advanced Video Coding (AVC) / H.264";
|
||||
case "hevc":
|
||||
case "hvc":
|
||||
case "hvc1":
|
||||
return "High Efficiency Video Coding (HEVC) / H.265";
|
||||
case "mkv":
|
||||
return "Matroska Multimedia Container";
|
||||
case "webp":
|
||||
return "Google WebP";
|
||||
case "webm":
|
||||
return "Google WebM";
|
||||
case "flv":
|
||||
return "Flash";
|
||||
case "mpg":
|
||||
return "MPEG";
|
||||
case "mjpg":
|
||||
return "Motion JPEG";
|
||||
case "ogg":
|
||||
case "ogv":
|
||||
return "Ogg Media";
|
||||
case "wmv":
|
||||
return "Windows Media";
|
||||
default:
|
||||
return value.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
static codecName(value) {
|
||||
if (!value || typeof value !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case "raw":
|
||||
return "Unprocessed Sensor Data (RAW)";
|
||||
case "mov":
|
||||
case "qt":
|
||||
return "Apple QuickTime (MOV)";
|
||||
case "avc":
|
||||
case "avc1":
|
||||
return "Advanced Video Coding (AVC) / H.264";
|
||||
case "hevc":
|
||||
case "hvc":
|
||||
case "hvc1":
|
||||
return "High Efficiency Video Coding (HEVC) / H.265";
|
||||
case "vvc":
|
||||
return "Versatile Video Coding (VVC) / H.266";
|
||||
case "av01":
|
||||
return "AOMedia Video 1 (AV1)";
|
||||
case "gif":
|
||||
return "Graphics Interchange Format (GIF)";
|
||||
case "mkv":
|
||||
return "Matroska Multimedia Container (MKV)";
|
||||
case "webp":
|
||||
return "Google WebP";
|
||||
case "webm":
|
||||
return "Google WebM";
|
||||
case "mpeg":
|
||||
return "Moving Picture Experts Group (MPEG)";
|
||||
case "mjpg":
|
||||
return "Motion JPEG (M-JPEG)";
|
||||
case "heif":
|
||||
case "heic":
|
||||
return "High Efficiency Image File Format (HEIF)";
|
||||
case "heic":
|
||||
return "High Efficiency Image Container (HEIC)";
|
||||
case "1":
|
||||
return "Uncompressed";
|
||||
case "2":
|
||||
|
|
|
@ -82,18 +82,18 @@
|
|||
</td>
|
||||
<td>{{ file.Hash }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Root.length > 1">
|
||||
<td>
|
||||
<translate>Storage Folder</translate>
|
||||
</td>
|
||||
<td>{{ file.Root | capitalize }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Name">
|
||||
<td>
|
||||
<translate>Name</translate>
|
||||
<translate>Filename</translate>
|
||||
</td>
|
||||
<td>{{ file.Name }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Root">
|
||||
<td>
|
||||
<translate>Storage</translate>
|
||||
</td>
|
||||
<td>{{ file.storageInfo() }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.OriginalName">
|
||||
<td>
|
||||
<translate>Original Name</translate>
|
||||
|
@ -112,12 +112,18 @@
|
|||
</td>
|
||||
<td>{{ file.typeInfo() }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Codec">
|
||||
<tr v-if="file.Codec && file.Codec !== file.FileType">
|
||||
<td>
|
||||
<translate>Codec</translate>
|
||||
</td>
|
||||
<td>{{ codecName(file) }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Duration && file.Duration > 0">
|
||||
<td>
|
||||
<translate>Duration</translate>
|
||||
</td>
|
||||
<td>{{ formatDuration(file) }}</td>
|
||||
</tr>
|
||||
<tr v-if="file.Frames">
|
||||
<td>
|
||||
<translate>Frames</translate>
|
||||
|
@ -278,6 +284,20 @@ export default {
|
|||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
formatDuration(file) {
|
||||
if (!file || !file.Duration) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return Util.duration(file.Duration);
|
||||
},
|
||||
fileType(file) {
|
||||
if (!file || !file.FileType) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return Util.fileType(file.FileType);
|
||||
},
|
||||
codecName(file) {
|
||||
if (!file || !file.Codec) {
|
||||
return "";
|
||||
|
|
|
@ -30,7 +30,7 @@ import Util from "common/util";
|
|||
import { config } from "app/session";
|
||||
import { $gettext } from "common/vm";
|
||||
import download from "common/download";
|
||||
import { MediaAnimated } from "./photo";
|
||||
import { MediaImage } from "./photo";
|
||||
|
||||
export class File extends RestModel {
|
||||
getDefaults() {
|
||||
|
@ -38,6 +38,9 @@ export class File extends RestModel {
|
|||
UID: "",
|
||||
PhotoUID: "",
|
||||
InstanceID: "",
|
||||
MediaID: "",
|
||||
MediaUTC: 0,
|
||||
TakenAt: "",
|
||||
Root: "/",
|
||||
Name: "",
|
||||
OriginalName: "",
|
||||
|
@ -184,18 +187,54 @@ export class File extends RestModel {
|
|||
return info.join(", ");
|
||||
}
|
||||
|
||||
storageInfo() {
|
||||
if (!this.Root || this.Root === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (this.Root.length === 1) {
|
||||
return $gettext("Originals");
|
||||
} else {
|
||||
return Util.capitalize(this.Root);
|
||||
}
|
||||
}
|
||||
|
||||
typeInfo() {
|
||||
if (this.Type === MediaAnimated) {
|
||||
return $gettext("Animation");
|
||||
}
|
||||
let info = [];
|
||||
|
||||
if (this.Video) {
|
||||
return $gettext("Video");
|
||||
if (
|
||||
this.MediaType &&
|
||||
this.Frames &&
|
||||
this.MediaType === MediaImage &&
|
||||
this.Frames &&
|
||||
this.Frames > 0
|
||||
) {
|
||||
info.push($gettext("Animated"));
|
||||
} else if (this.Sidecar) {
|
||||
return $gettext("Sidecar");
|
||||
info.push($gettext("Sidecar"));
|
||||
}
|
||||
|
||||
return this.FileType.toUpperCase();
|
||||
if (this.Primary && !this.MediaType) {
|
||||
info.push($gettext("Image"));
|
||||
return info.join(" ");
|
||||
} else if (this.Video && !this.MediaType) {
|
||||
info.push($gettext("Video"));
|
||||
return info.join(" ");
|
||||
} else {
|
||||
const format = Util.fileType(this.FileType);
|
||||
if (format) {
|
||||
info.push(format);
|
||||
}
|
||||
|
||||
if (this.MediaType && this.MediaType !== this.FileType) {
|
||||
const media = Util.capitalize(this.MediaType);
|
||||
if (media) {
|
||||
info.push(media);
|
||||
}
|
||||
}
|
||||
|
||||
return info.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
sizeInfo() {
|
||||
|
|
|
@ -43,6 +43,7 @@ export const FormatGif = "gif";
|
|||
export const FormatJpeg = "jpg";
|
||||
export const MediaImage = "image";
|
||||
export const MediaAnimated = "animated";
|
||||
export const MediaSidecar = "sidecar";
|
||||
export const MediaVideo = "video";
|
||||
export const MediaLive = "live";
|
||||
export const MediaRaw = "raw";
|
||||
|
@ -535,9 +536,10 @@ export class Photo extends RestModel {
|
|||
}
|
||||
|
||||
downloadAll() {
|
||||
const settings = config.settings();
|
||||
const s = config.settings();
|
||||
|
||||
if (!settings || !settings.features || !settings.download || !settings.features.download) {
|
||||
if (!s || !s.features || !s.download || !s.features.download || s.download.disabled) {
|
||||
console.log("download: disabled in settings", s.features, s.download);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -560,23 +562,30 @@ export class Photo extends RestModel {
|
|||
return;
|
||||
}
|
||||
|
||||
// Skip sidecar files.
|
||||
if (file.Sidecar) {
|
||||
// Originals only?
|
||||
if (s.download.originals && file.Root.length > 1) {
|
||||
// Don't download broken files and sidecars.
|
||||
if (config.debug) console.log("download: skipped sidecar", file);
|
||||
if (config.debug) console.log(`download: skipped ${file.Root} file ${file.Name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip RAW images.
|
||||
if (!settings.download.raw && file.FileType === MediaRaw) {
|
||||
if (config.debug) console.log("download: skipped raw", file);
|
||||
// Skip metadata sidecar files?
|
||||
if (!s.download.mediaSidecar && (file.MediaType === MediaSidecar || file.Sidecar)) {
|
||||
// Don't download broken files and sidecars.
|
||||
if (config.debug) console.log(`download: skipped sidecar file ${file.Name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip related images if video.
|
||||
// Skip RAW images?
|
||||
if (!s.download.mediaRaw && (file.MediaType === MediaRaw || file.FileType === MediaRaw)) {
|
||||
if (config.debug) console.log(`download: skipped raw file ${file.Name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a video, always skip stacked images...
|
||||
// see https://github.com/photoprism/photoprism/issues/1436
|
||||
if (this.Type === MediaVideo && !file.Video) {
|
||||
if (config.debug) console.log("download: skipped image", file);
|
||||
if (this.Type === MediaVideo && !(file.MediaType === MediaVideo || file.Video)) {
|
||||
if (config.debug) console.log(`download: skipped video sidecar ${file.Name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,7 +236,21 @@ describe("model/file", () => {
|
|||
UpdatedAt: "2012-07-08T14:45:39Z",
|
||||
};
|
||||
const file3 = new File(values3);
|
||||
assert.equal(file3.typeInfo(), "Sidecar");
|
||||
assert.equal(file3.typeInfo(), "Sidecar JPG");
|
||||
const values4 = {
|
||||
InstanceID: 5,
|
||||
UID: "ABC123",
|
||||
Hash: "54ghtfd",
|
||||
FileType: "gif",
|
||||
MediaType: "image",
|
||||
Duration: 8009,
|
||||
Name: "1/2/IMG123.jpg",
|
||||
Sidecar: true,
|
||||
CreatedAt: "2012-07-08T14:45:39Z",
|
||||
UpdatedAt: "2012-07-08T14:45:39Z",
|
||||
};
|
||||
const file4 = new File(values4);
|
||||
assert.equal(file4.typeInfo(), "Sidecar GIF Image");
|
||||
});
|
||||
|
||||
it("should get size info", () => {
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/workers"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// Namespaces for caching and logs.
|
||||
|
@ -49,7 +49,7 @@ func GetAccount(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdUint(c.Param("id"))
|
||||
id := clean.IdUint(c.Param("id"))
|
||||
|
||||
if m, err := query.AccountByID(id); err == nil {
|
||||
c.JSON(http.StatusOK, m)
|
||||
|
@ -82,7 +82,7 @@ func GetAccountFolders(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
start := time.Now()
|
||||
id := sanitize.IdUint(c.Param("id"))
|
||||
id := clean.IdUint(c.Param("id"))
|
||||
cache := service.FolderCache()
|
||||
cacheKey := fmt.Sprintf("%s:%d", accountFolder, id)
|
||||
|
||||
|
@ -132,7 +132,7 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdUint(c.Param("id"))
|
||||
id := clean.IdUint(c.Param("id"))
|
||||
|
||||
m, err := query.AccountByID(id)
|
||||
|
||||
|
@ -150,13 +150,9 @@ func ShareWithAccount(router *gin.RouterGroup) {
|
|||
|
||||
folder := f.Folder
|
||||
|
||||
// Select files to be shared.
|
||||
o := query.FileSelection{
|
||||
Video: true,
|
||||
OriginalsOnly: m.ShareOriginals(),
|
||||
PrimaryOnly: !m.ShareOriginals(),
|
||||
}
|
||||
files, err := query.SelectedFiles(f.Selection, o)
|
||||
// Find files to share.
|
||||
selection := query.ShareSelection(m.ShareOriginals())
|
||||
files, err := query.SelectedFiles(f.Selection, selection)
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -252,7 +248,7 @@ func UpdateAccount(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdUint(c.Param("id"))
|
||||
id := clean.IdUint(c.Param("id"))
|
||||
|
||||
m, err := query.AccountByID(id)
|
||||
|
||||
|
@ -323,7 +319,7 @@ func DeleteAccount(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdUint(c.Param("id"))
|
||||
id := clean.IdUint(c.Param("id"))
|
||||
|
||||
m, err := query.AccountByID(id)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/search"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
var albumMutex = sync.Mutex{}
|
||||
|
@ -35,7 +35,7 @@ func SaveAlbumAsYaml(a entity.Album) {
|
|||
if err := a.SaveAsYaml(fileName); err != nil {
|
||||
log.Errorf("album: %s (update yaml)", err)
|
||||
} else {
|
||||
log.Debugf("album: updated yaml file %s", sanitize.Log(filepath.Base(fileName)))
|
||||
log.Debugf("album: updated yaml file %s", clean.Log(filepath.Base(fileName)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func GetAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
a, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -96,7 +96,7 @@ func CreateAlbum(router *gin.RouterGroup) {
|
|||
|
||||
// Create new album.
|
||||
if err := a.Create(); err != nil {
|
||||
AbortAlreadyExists(c, sanitize.Log(a.AlbumTitle))
|
||||
AbortAlreadyExists(c, clean.Log(a.AlbumTitle))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
a, err := query.AlbumByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -179,7 +179,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
|
||||
a, err := query.AlbumByUID(id)
|
||||
|
||||
|
@ -212,7 +212,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
|
||||
SaveAlbumAsYaml(a)
|
||||
|
||||
event.SuccessMsg(i18n.MsgAlbumDeleted, sanitize.Log(a.AlbumTitle))
|
||||
event.SuccessMsg(i18n.MsgAlbumDeleted, clean.Log(a.AlbumTitle))
|
||||
|
||||
c.JSON(http.StatusOK, a)
|
||||
})
|
||||
|
@ -233,7 +233,7 @@ func LikeAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
a, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -271,7 +271,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
a, err := query.AlbumByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -306,7 +306,7 @@ func CloneAlbums(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
a, err := query.AlbumByUID(sanitize.IdString(c.Param("uid")))
|
||||
a, err := query.AlbumByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
@ -341,7 +341,7 @@ func CloneAlbums(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if len(added) > 0 {
|
||||
event.SuccessMsg(i18n.MsgSelectionAddedTo, sanitize.Log(a.Title()))
|
||||
event.SuccessMsg(i18n.MsgSelectionAddedTo, clean.Log(a.Title()))
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
|
||||
|
@ -371,7 +371,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
a, err := query.AlbumByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -392,9 +392,9 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if len(added) > 0 {
|
||||
if len(added) == 1 {
|
||||
event.SuccessMsg(i18n.MsgEntryAddedTo, sanitize.Log(a.Title()))
|
||||
event.SuccessMsg(i18n.MsgEntryAddedTo, clean.Log(a.Title()))
|
||||
} else {
|
||||
event.SuccessMsg(i18n.MsgEntriesAddedTo, len(added), sanitize.Log(a.Title()))
|
||||
event.SuccessMsg(i18n.MsgEntriesAddedTo, len(added), clean.Log(a.Title()))
|
||||
}
|
||||
|
||||
RemoveFromAlbumCoverCache(a.AlbumUID)
|
||||
|
@ -432,7 +432,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
a, err := query.AlbumByUID(sanitize.IdString(c.Param("uid")))
|
||||
a, err := query.AlbumByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
@ -443,9 +443,9 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if len(removed) > 0 {
|
||||
if len(removed) == 1 {
|
||||
event.SuccessMsg(i18n.MsgEntryRemovedFrom, sanitize.Log(a.Title()))
|
||||
event.SuccessMsg(i18n.MsgEntryRemovedFrom, clean.Log(a.Title()))
|
||||
} else {
|
||||
event.SuccessMsg(i18n.MsgEntriesRemovedFrom, len(removed), sanitize.Log(sanitize.Log(a.Title())))
|
||||
event.SuccessMsg(i18n.MsgEntriesRemovedFrom, len(removed), clean.Log(clean.Log(a.Title())))
|
||||
}
|
||||
|
||||
RemoveFromAlbumCoverCache(a.AlbumUID)
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
@ -60,7 +60,7 @@ func UpdateClientConfig() {
|
|||
func Abort(c *gin.Context, code int, id i18n.Message, params ...interface{}) {
|
||||
resp := i18n.NewResponse(code, id, params...)
|
||||
|
||||
log.Debugf("api: abort %s with code %d (%s)", sanitize.Log(c.FullPath()), code, resp.String())
|
||||
log.Debugf("api: abort %s with code %d (%s)", clean.Log(c.FullPath()), code, resp.String())
|
||||
|
||||
c.AbortWithStatusJSON(code, resp)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func Error(c *gin.Context, code int, err error, id i18n.Message, params ...inter
|
|||
|
||||
if err != nil {
|
||||
resp.Details = err.Error()
|
||||
log.Errorf("api: error %s with code %d in %s (%s)", sanitize.Log(err.Error()), code, sanitize.Log(c.FullPath()), resp.String())
|
||||
log.Errorf("api: error %s with code %d in %s (%s)", clean.Log(err.Error()), code, clean.Log(c.FullPath()), resp.String())
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(code, resp)
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// BatchPhotosArchive moves multiple photos to the archive.
|
||||
|
@ -41,7 +41,7 @@ func BatchPhotosArchive(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("photos: archiving %s", sanitize.Log(f.String()))
|
||||
log.Infof("photos: archiving %s", clean.Log(f.String()))
|
||||
|
||||
if service.Config().BackupYaml() {
|
||||
// Fetch selection from index.
|
||||
|
@ -105,7 +105,7 @@ func BatchPhotosRestore(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("photos: restoring %s", sanitize.Log(f.String()))
|
||||
log.Infof("photos: restoring %s", clean.Log(f.String()))
|
||||
|
||||
if service.Config().BackupYaml() {
|
||||
// Fetch selection from index.
|
||||
|
@ -168,7 +168,7 @@ func BatchPhotosApprove(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("photos: approving %s", sanitize.Log(f.String()))
|
||||
log.Infof("photos: approving %s", clean.Log(f.String()))
|
||||
|
||||
// Fetch selection from index.
|
||||
photos, err := query.SelectedPhotos(f)
|
||||
|
@ -221,7 +221,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("albums: deleting %s", sanitize.Log(f.String()))
|
||||
log.Infof("albums: deleting %s", clean.Log(f.String()))
|
||||
|
||||
entity.Db().Where("album_uid IN (?)", f.Albums).Delete(&entity.Album{})
|
||||
entity.Db().Where("album_uid IN (?)", f.Albums).Delete(&entity.PhotoAlbum{})
|
||||
|
@ -258,7 +258,7 @@ func BatchPhotosPrivate(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("photos: updating private flag for %s", sanitize.Log(f.String()))
|
||||
log.Infof("photos: updating private flag for %s", clean.Log(f.String()))
|
||||
|
||||
if err := entity.Db().Model(entity.Photo{}).Where("photo_uid IN (?)", f.Photos).UpdateColumn("photo_private",
|
||||
gorm.Expr("CASE WHEN photo_private > 0 THEN 0 ELSE 1 END")).Error; err != nil {
|
||||
|
@ -312,7 +312,7 @@ func BatchLabelsDelete(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("labels: deleting %s", sanitize.Log(f.String()))
|
||||
log.Infof("labels: deleting %s", clean.Log(f.String()))
|
||||
|
||||
var labels entity.Labels
|
||||
|
||||
|
@ -364,7 +364,7 @@ func BatchPhotosDelete(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
log.Infof("photos: deleting %s", sanitize.Log(f.String()))
|
||||
log.Infof("photos: deleting %s", clean.Log(f.String()))
|
||||
|
||||
// Fetch selection from index.
|
||||
photos, err := query.SelectedPhotos(f)
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
@ -87,13 +87,13 @@ func SaveConfigOptions(router *gin.RouterGroup) {
|
|||
yamlData, err := os.ReadFile(fileName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
|
||||
log.Errorf("config: failed loading values from %s (%s)", clean.Log(fileName), err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(yamlData, v); err != nil {
|
||||
log.Warnf("config: failed parsing values in %s (%s)", sanitize.Log(fileName), err)
|
||||
log.Warnf("config: failed parsing values in %s (%s)", clean.Log(fileName), err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
@ -122,14 +122,14 @@ func SaveConfigOptions(router *gin.RouterGroup) {
|
|||
|
||||
// Write YAML data to file.
|
||||
if err := os.WriteFile(fileName, yamlData, os.ModePerm); err != nil {
|
||||
log.Errorf("config: failed writing values to %s (%s)", sanitize.Log(fileName), err)
|
||||
log.Errorf("config: failed writing values to %s (%s)", clean.Log(fileName), err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Reload options.
|
||||
if err := conf.Options().Load(fileName); err != nil {
|
||||
log.Warnf("config: failed loading values from %s (%s)", sanitize.Log(fileName), err)
|
||||
log.Warnf("config: failed loading values from %s (%s)", clean.Log(fileName), err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
|
@ -38,13 +38,13 @@ func AlbumCover(router *gin.RouterGroup) {
|
|||
|
||||
start := time.Now()
|
||||
conf := service.Config()
|
||||
thumbName := thumb.Name(sanitize.Token(c.Param("size")))
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
thumbName := thumb.Name(clean.Token(c.Param("size")))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
|
||||
size, ok := thumb.Sizes[thumbName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("%s: invalid size %s", albumCover, sanitize.Log(thumbName.String()))
|
||||
log.Errorf("%s: invalid size %s", albumCover, clean.Log(thumbName.String()))
|
||||
c.Data(http.StatusOK, "image/svg+xml", albumIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -85,11 +85,11 @@ func AlbumCover(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("%s: found no original for %s", albumCover, sanitize.Log(fileName))
|
||||
log.Errorf("%s: found no original for %s", albumCover, clean.Log(fileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", albumIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
log.Warnf("%s: %s is missing", albumCover, sanitize.Log(f.FileName))
|
||||
log.Warnf("%s: %s is missing", albumCover, clean.Log(f.FileName))
|
||||
logError(albumCover, f.Update("FileMissing", true))
|
||||
return
|
||||
}
|
||||
|
@ -150,13 +150,13 @@ func LabelCover(router *gin.RouterGroup) {
|
|||
|
||||
start := time.Now()
|
||||
conf := service.Config()
|
||||
thumbName := thumb.Name(sanitize.Token(c.Param("size")))
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
thumbName := thumb.Name(clean.Token(c.Param("size")))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
|
||||
size, ok := thumb.Sizes[thumbName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("%s: invalid size %s", labelCover, sanitize.Log(thumbName.String()))
|
||||
log.Errorf("%s: invalid size %s", labelCover, clean.Log(thumbName.String()))
|
||||
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ func LabelCover(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("%s: file %s is missing", labelCover, sanitize.Log(f.FileName))
|
||||
log.Errorf("%s: file %s is missing", labelCover, clean.Log(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/search"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// DownloadAlbum streams the album contents as zip archive.
|
||||
|
@ -36,7 +36,7 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
start := time.Now()
|
||||
a, err := query.AlbumByUID(sanitize.IdString(c.Param("uid")))
|
||||
a, err := query.AlbumByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
@ -57,26 +57,19 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
zipWriter := zip.NewWriter(c.Writer)
|
||||
defer zipWriter.Close()
|
||||
|
||||
skipRaw := !conf.Settings().Download.Raw
|
||||
|
||||
var aliases = make(map[string]int)
|
||||
|
||||
for _, file := range files {
|
||||
if file.FileHash == "" {
|
||||
log.Warnf("download: empty file hash, skipped %s", sanitize.Log(file.FileName))
|
||||
log.Warnf("download: empty file hash, skipped %s", clean.Log(file.FileName))
|
||||
continue
|
||||
} else if file.FileName == "" {
|
||||
log.Warnf("download: empty file name, skipped %s", sanitize.Log(file.FileUID))
|
||||
log.Warnf("download: empty file name, skipped %s", clean.Log(file.FileUID))
|
||||
continue
|
||||
}
|
||||
|
||||
if file.FileSidecar {
|
||||
log.Debugf("download: skipped sidecar %s", sanitize.Log(file.FileName))
|
||||
continue
|
||||
}
|
||||
|
||||
if skipRaw && fs.FormatRaw.Is(file.FileType) {
|
||||
log.Debugf("download: skipped raw %s", sanitize.Log(file.FileName))
|
||||
log.Debugf("download: skipped sidecar %s", clean.Log(file.FileName))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -92,17 +85,17 @@ func DownloadAlbum(router *gin.RouterGroup) {
|
|||
|
||||
if fs.FileExists(fileName) {
|
||||
if err := addFileToZip(zipWriter, fileName, alias); err != nil {
|
||||
log.Errorf("download: failed adding %s to album zip (%s)", sanitize.Log(file.FileName), err)
|
||||
log.Errorf("download: failed adding %s to album zip (%s)", clean.Log(file.FileName), err)
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("download: added %s as %s", sanitize.Log(file.FileName), sanitize.Log(alias))
|
||||
log.Infof("download: added %s as %s", clean.Log(file.FileName), clean.Log(alias))
|
||||
} else {
|
||||
log.Warnf("download: album file %s is missing", sanitize.Log(file.FileName))
|
||||
log.Warnf("download: album file %s is missing", clean.Log(file.FileName))
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("download: created %s [%s]", sanitize.Log(zipFileName), time.Since(start))
|
||||
log.Infof("download: created %s [%s]", clean.Log(zipFileName), time.Since(start))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// TODO: GET /api/v1/dl/file/:hash
|
||||
|
@ -45,7 +45,7 @@ func GetDownload(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
fileHash := sanitize.Token(c.Param("hash"))
|
||||
fileHash := clean.Token(c.Param("hash"))
|
||||
|
||||
f, err := query.FileByHash(fileHash)
|
||||
|
||||
|
@ -57,7 +57,7 @@ func GetDownload(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("download: file %s is missing", sanitize.Log(f.FileName))
|
||||
log.Errorf("download: file %s is missing", clean.Log(f.FileName))
|
||||
c.Data(404, "image/svg+xml", brokenIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
|
|
|
@ -20,9 +20,9 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// CreateZip creates a zip file archive for download.
|
||||
|
@ -57,8 +57,17 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
// Select files to be downloaded.
|
||||
files, err := query.SelectedFiles(f, query.FileSelectionAll())
|
||||
// Configure file selection based on user settings.
|
||||
var selection query.FileSelection
|
||||
if dl := conf.Settings().Download; dl.Disabled {
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
} else {
|
||||
selection = query.DownloadSelection(dl.MediaRaw, dl.MediaSidecar, dl.Originals)
|
||||
}
|
||||
|
||||
// Find files to download.
|
||||
files, err := query.SelectedFiles(f, selection)
|
||||
|
||||
if err != nil {
|
||||
Error(c, http.StatusBadRequest, err, i18n.ErrZipFailed)
|
||||
|
@ -68,53 +77,36 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
// Configure file names.
|
||||
dlName := DownloadName(c)
|
||||
zipPath := path.Join(conf.TempPath(), "zip")
|
||||
zipToken := rnd.Token(8)
|
||||
zipToken := rnd.GenerateToken(8)
|
||||
zipBaseName := fmt.Sprintf("photoprism-download-%s-%s.zip", time.Now().Format("20060102-150405"), zipToken)
|
||||
zipFileName := path.Join(zipPath, zipBaseName)
|
||||
|
||||
// Create temp directory.
|
||||
if err := os.MkdirAll(zipPath, 0700); err != nil {
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
|
||||
newZipFile, err := os.Create(zipFileName)
|
||||
|
||||
if err != nil {
|
||||
// Create new zip file.
|
||||
var newZipFile *os.File
|
||||
if newZipFile, err = os.Create(zipFileName); err != nil {
|
||||
Error(c, http.StatusInternalServerError, err, i18n.ErrZipFailed)
|
||||
return
|
||||
} else {
|
||||
defer newZipFile.Close()
|
||||
}
|
||||
|
||||
defer newZipFile.Close()
|
||||
|
||||
// Create zip writer.
|
||||
zipWriter := zip.NewWriter(newZipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
dlName := DownloadName(c)
|
||||
|
||||
skipRaw := !conf.Settings().Download.Raw
|
||||
|
||||
var aliases = make(map[string]int)
|
||||
|
||||
// Add files to zip.
|
||||
for _, file := range files {
|
||||
if file.FileHash == "" {
|
||||
log.Warnf("download: empty file hash, skipped %s", sanitize.Log(file.FileName))
|
||||
continue
|
||||
} else if file.FileName == "" {
|
||||
log.Warnf("download: empty file name, skipped %s", sanitize.Log(file.FileUID))
|
||||
continue
|
||||
}
|
||||
|
||||
if file.FileSidecar {
|
||||
log.Debugf("download: skipped sidecar %s", sanitize.Log(file.FileName))
|
||||
continue
|
||||
}
|
||||
|
||||
if skipRaw && fs.FormatRaw.Is(file.FileType) {
|
||||
log.Debugf("download: skipped raw %s", sanitize.Log(file.FileName))
|
||||
continue
|
||||
}
|
||||
|
||||
fileName := photoprism.FileName(file.FileRoot, file.FileName)
|
||||
alias := file.DownloadName(dlName, 0)
|
||||
key := strings.ToLower(alias)
|
||||
|
@ -127,21 +119,21 @@ func CreateZip(router *gin.RouterGroup) {
|
|||
|
||||
if fs.FileExists(fileName) {
|
||||
if err := addFileToZip(zipWriter, fileName, alias); err != nil {
|
||||
log.Errorf("download: failed adding %s to zip (%s)", sanitize.Log(file.FileName), err)
|
||||
log.Errorf("download: failed adding %s to zip (%s)", clean.Log(file.FileName), err)
|
||||
Abort(c, http.StatusInternalServerError, i18n.ErrZipFailed)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("download: added %s as %s", sanitize.Log(file.FileName), sanitize.Log(alias))
|
||||
log.Infof("download: added %s as %s", clean.Log(file.FileName), clean.Log(alias))
|
||||
} else {
|
||||
log.Warnf("download: media file %s is missing", sanitize.Log(file.FileName))
|
||||
log.Warnf("download: media file %s is missing", clean.Log(file.FileName))
|
||||
logError("download", file.Update("FileMissing", true))
|
||||
}
|
||||
}
|
||||
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
log.Infof("download: created %s [%s]", sanitize.Log(zipBaseName), time.Since(start))
|
||||
log.Infof("download: created %s [%s]", clean.Log(zipBaseName), time.Since(start))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgZipCreatedIn, elapsed), "filename": zipBaseName})
|
||||
})
|
||||
|
@ -158,12 +150,12 @@ func DownloadZip(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
conf := service.Config()
|
||||
zipBaseName := sanitize.FileName(filepath.Base(c.Param("filename")))
|
||||
zipBaseName := clean.FileName(filepath.Base(c.Param("filename")))
|
||||
zipPath := path.Join(conf.TempPath(), "zip")
|
||||
zipFileName := path.Join(zipPath, zipBaseName)
|
||||
|
||||
if !fs.FileExists(zipFileName) {
|
||||
log.Errorf("could not find zip file: %s", sanitize.Log(zipFileName))
|
||||
log.Errorf("could not find zip file: %s", clean.Log(zipFileName))
|
||||
c.Data(404, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -171,7 +163,7 @@ func DownloadZip(router *gin.RouterGroup) {
|
|||
c.FileAttachment(zipFileName, zipBaseName)
|
||||
|
||||
if err := os.Remove(zipFileName); err != nil {
|
||||
log.Errorf("download: failed removing %s (%s)", sanitize.Log(zipFileName), err.Error())
|
||||
log.Errorf("download: failed removing %s (%s)", clean.Log(zipFileName), err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func GetErrors(router *gin.RouterGroup) {
|
|||
|
||||
// Find and return matching logs.
|
||||
if resp, err := query.Errors(limit, offset, c.Query("q")); err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
} else {
|
||||
AddCountHeader(c, len(resp))
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
@ -58,7 +58,7 @@ func UpdateFace(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
faceId := sanitize.Token(c.Param("id"))
|
||||
faceId := clean.Token(c.Param("id"))
|
||||
m := entity.FindFace(faceId)
|
||||
|
||||
if m == nil {
|
||||
|
@ -70,7 +70,7 @@ func UpdateFace(router *gin.RouterGroup) {
|
|||
if !f.FaceHidden && f.FaceHidden == m.FaceHidden {
|
||||
// Do nothing.
|
||||
} else if err := m.Update("FaceHidden", f.FaceHidden); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ func UpdateFace(router *gin.RouterGroup) {
|
|||
if f.SubjUID == "" {
|
||||
// Do nothing.
|
||||
} else if err := m.SetSubjectUID(f.SubjUID); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -24,7 +24,7 @@ func GetFile(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
p, err := query.FileByHash(sanitize.Token(c.Param("hash")))
|
||||
p, err := query.FileByHash(clean.Token(c.Param("hash")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -36,8 +36,8 @@ func DeleteFile(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
photoUID := sanitize.IdString(c.Param("uid"))
|
||||
fileUID := sanitize.IdString(c.Param("file_uid"))
|
||||
photoUID := clean.IdString(c.Param("uid"))
|
||||
fileUID := clean.IdString(c.Param("file_uid"))
|
||||
|
||||
file, err := query.FileByUID(fileUID)
|
||||
|
||||
|
@ -59,17 +59,17 @@ func DeleteFile(router *gin.RouterGroup) {
|
|||
mediaFile, err := photoprism.NewMediaFile(fileName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s (delete %s)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (delete %s)", err, clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := mediaFile.Remove(); err != nil {
|
||||
log.Errorf("photo: %s (delete %s from folder)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (delete %s from folder)", err, clean.Log(baseName))
|
||||
}
|
||||
|
||||
if err := file.Delete(true); err != nil {
|
||||
log.Errorf("photo: %s (delete %s from index)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (delete %s from index)", err, clean.Log(baseName))
|
||||
AbortDeleteFailed(c)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
|
@ -37,7 +37,7 @@ func FolderCover(router *gin.RouterGroup) {
|
|||
start := time.Now()
|
||||
conf := service.Config()
|
||||
uid := c.Param("uid")
|
||||
thumbName := thumb.Name(sanitize.Token(c.Param("size")))
|
||||
thumbName := thumb.Name(clean.Token(c.Param("size")))
|
||||
download := c.Query("download") != ""
|
||||
|
||||
size, ok := thumb.Sizes[thumbName]
|
||||
|
@ -98,7 +98,7 @@ func FolderCover(router *gin.RouterGroup) {
|
|||
c.Data(http.StatusOK, "image/svg+xml", folderIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
log.Warnf("%s: %s is missing", folderCover, sanitize.Log(f.FileName))
|
||||
log.Warnf("%s: %s is missing", folderCover, clean.Log(f.FileName))
|
||||
logError(folderCover, f.Update("FileMissing", true))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// StartImport imports media files from a directory and converts/indexes them as needed.
|
||||
|
@ -53,7 +53,7 @@ func StartImport(router *gin.RouterGroup) {
|
|||
subPath := ""
|
||||
path := conf.ImportPath()
|
||||
|
||||
if subPath = sanitize.Path(c.Param("path")); subPath != "" && subPath != "/" {
|
||||
if subPath = clean.Path(c.Param("path")); subPath != "" && subPath != "/" {
|
||||
subPath = strings.Replace(subPath, ".", "", -1)
|
||||
path = filepath.Join(path, subPath)
|
||||
} else if f.Path != "" {
|
||||
|
@ -70,15 +70,15 @@ func StartImport(router *gin.RouterGroup) {
|
|||
var opt photoprism.ImportOptions
|
||||
|
||||
if f.Move {
|
||||
event.InfoMsg(i18n.MsgMovingFilesFrom, sanitize.Log(filepath.Base(path)))
|
||||
event.InfoMsg(i18n.MsgMovingFilesFrom, clean.Log(filepath.Base(path)))
|
||||
opt = photoprism.ImportOptionsMove(path)
|
||||
} else {
|
||||
event.InfoMsg(i18n.MsgCopyingFilesFrom, sanitize.Log(filepath.Base(path)))
|
||||
event.InfoMsg(i18n.MsgCopyingFilesFrom, clean.Log(filepath.Base(path)))
|
||||
opt = photoprism.ImportOptionsCopy(path)
|
||||
}
|
||||
|
||||
if len(f.Albums) > 0 {
|
||||
log.Debugf("import: adding files to album %s", sanitize.Log(strings.Join(f.Albums, " and ")))
|
||||
log.Debugf("import: adding files to album %s", clean.Log(strings.Join(f.Albums, " and ")))
|
||||
opt.Albums = f.Albums
|
||||
}
|
||||
|
||||
|
@ -86,9 +86,9 @@ func StartImport(router *gin.RouterGroup) {
|
|||
|
||||
if subPath != "" && path != conf.ImportPath() && fs.IsEmpty(path) {
|
||||
if err := os.Remove(path); err != nil {
|
||||
log.Errorf("import: failed deleting empty folder %s: %s", sanitize.Log(path), err)
|
||||
log.Errorf("import: failed deleting empty folder %s: %s", clean.Log(path), err)
|
||||
} else {
|
||||
log.Infof("import: deleted empty folder %s", sanitize.Log(path))
|
||||
log.Infof("import: deleted empty folder %s", clean.Log(path))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -53,7 +53,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
|||
indOpt := photoprism.NewIndexOptions(filepath.Clean(f.Path), f.Rescan, convert, true, false)
|
||||
|
||||
if len(indOpt.Path) > 1 {
|
||||
event.InfoMsg(i18n.MsgIndexingFiles, sanitize.Log(indOpt.Path))
|
||||
event.InfoMsg(i18n.MsgIndexingFiles, clean.Log(indOpt.Path))
|
||||
} else {
|
||||
event.InfoMsg(i18n.MsgIndexingOriginals)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func StartIndexing(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if files, photos, err := prg.Start(prgOpt); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
} else if len(files) > 0 || len(photos) > 0 {
|
||||
event.InfoMsg(i18n.MsgRemovedFilesAndPhotos, len(files), len(photos))
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
@ -35,7 +35,7 @@ func UpdateLabel(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
m, err := query.LabelByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -69,16 +69,16 @@ func LikeLabel(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
label, err := query.LabelByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
if err := label.Update("LabelFavorite", true); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -109,16 +109,16 @@ func DislikeLabel(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
label, err := query.LabelByUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
if err := label.Update("LabelFavorite", false); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -33,7 +33,7 @@ func UpdateLink(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
link := entity.FindLink(sanitize.Token(c.Param("link")))
|
||||
link := entity.FindLink(clean.Token(c.Param("link")))
|
||||
|
||||
link.SetSlug(f.ShareSlug)
|
||||
link.MaxViews = f.MaxViews
|
||||
|
@ -45,13 +45,13 @@ func UpdateLink(c *gin.Context) {
|
|||
|
||||
if f.Password != "" {
|
||||
if err := link.SetPassword(f.Password); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := link.Save(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -73,10 +73,10 @@ func DeleteLink(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
link := entity.FindLink(sanitize.Token(c.Param("link")))
|
||||
link := entity.FindLink(clean.Token(c.Param("link")))
|
||||
|
||||
if err := link.Delete(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func CreateLink(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
link := entity.NewLink(sanitize.IdString(c.Param("uid")), f.CanComment, f.CanEdit)
|
||||
link := entity.NewLink(clean.IdString(c.Param("uid")), f.CanComment, f.CanEdit)
|
||||
|
||||
link.SetSlug(f.ShareSlug)
|
||||
link.MaxViews = f.MaxViews
|
||||
|
@ -113,13 +113,13 @@ func CreateLink(c *gin.Context) {
|
|||
|
||||
if f.Password != "" {
|
||||
if err := link.SetPassword(f.Password); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := link.Save(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ func CreateLink(c *gin.Context) {
|
|||
// POST /api/v1/albums/:uid/links
|
||||
func CreateAlbumLink(router *gin.RouterGroup) {
|
||||
router.POST("/albums/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.AlbumByUID(sanitize.IdString(c.Param("uid"))); err != nil {
|
||||
if _, err := query.AlbumByUID(clean.IdString(c.Param("uid"))); err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
return
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func DeleteAlbumLink(router *gin.RouterGroup) {
|
|||
// GET /api/v1/albums/:uid/links
|
||||
func GetAlbumLinks(router *gin.RouterGroup) {
|
||||
router.GET("/albums/:uid/links", func(c *gin.Context) {
|
||||
m, err := query.AlbumByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.AlbumByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
@ -175,7 +175,7 @@ func GetAlbumLinks(router *gin.RouterGroup) {
|
|||
// POST /api/v1/photos/:uid/links
|
||||
func CreatePhotoLink(router *gin.RouterGroup) {
|
||||
router.POST("/photos/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.PhotoByUID(sanitize.IdString(c.Param("uid"))); err != nil {
|
||||
if _, err := query.PhotoByUID(clean.IdString(c.Param("uid"))); err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ func DeletePhotoLink(router *gin.RouterGroup) {
|
|||
// GET /api/v1/photos/:uid/links
|
||||
func GetPhotoLinks(router *gin.RouterGroup) {
|
||||
router.GET("/photos/:uid/links", func(c *gin.Context) {
|
||||
m, err := query.PhotoByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.PhotoByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
@ -215,7 +215,7 @@ func GetPhotoLinks(router *gin.RouterGroup) {
|
|||
// POST /api/v1/labels/:uid/links
|
||||
func CreateLabelLink(router *gin.RouterGroup) {
|
||||
router.POST("/labels/:uid/links", func(c *gin.Context) {
|
||||
if _, err := query.LabelByUID(sanitize.IdString(c.Param("uid"))); err != nil {
|
||||
if _, err := query.LabelByUID(clean.IdString(c.Param("uid"))); err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrLabelNotFound)
|
||||
return
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ func DeleteLabelLink(router *gin.RouterGroup) {
|
|||
// GET /api/v1/labels/:uid/links
|
||||
func GetLabelLinks(router *gin.RouterGroup) {
|
||||
router.GET("/labels/:uid/links", func(c *gin.Context) {
|
||||
m, err := query.LabelByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.LabelByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrAlbumNotFound)
|
||||
|
|
|
@ -23,7 +23,7 @@ func GetMomentsTime(router *gin.RouterGroup) {
|
|||
result, err := query.MomentsTime(1)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// SavePhotoAsYaml saves photo data as YAML file.
|
||||
|
@ -33,7 +33,7 @@ func SavePhotoAsYaml(p entity.Photo) {
|
|||
if err := p.SaveAsYaml(fileName); err != nil {
|
||||
log.Errorf("photo: %s (update yaml)", err)
|
||||
} else {
|
||||
log.Debugf("photo: updated yaml file %s", sanitize.Log(filepath.Base(fileName)))
|
||||
log.Debugf("photo: updated yaml file %s", clean.Log(filepath.Base(fileName)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func GetPhoto(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(sanitize.IdString(c.Param("uid")))
|
||||
p, err := query.PhotoPreloadByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -74,7 +74,7 @@ func UpdatePhoto(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
m, err := query.PhotoByUID(uid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -136,7 +136,7 @@ func GetPhotoDownload(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
f, err := query.FileByPhotoUID(sanitize.IdString(c.Param("uid")))
|
||||
f, err := query.FileByPhotoUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
||||
|
@ -146,7 +146,7 @@ func GetPhotoDownload(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("photo: file %s is missing", sanitize.Log(f.FileName))
|
||||
log.Errorf("photo: file %s is missing", clean.Log(f.FileName))
|
||||
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
|
@ -172,7 +172,7 @@ func GetPhotoYaml(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(sanitize.IdString(c.Param("uid")))
|
||||
p, err := query.PhotoPreloadByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
|
@ -187,7 +187,7 @@ func GetPhotoYaml(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if c.Query("download") != "" {
|
||||
AddDownloadHeader(c, sanitize.IdString(c.Param("uid"))+fs.YamlExt)
|
||||
AddDownloadHeader(c, clean.IdString(c.Param("uid"))+fs.ExtYAML)
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "text/x-yaml; charset=utf-8", data)
|
||||
|
@ -207,7 +207,7 @@ func ApprovePhoto(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -242,7 +242,7 @@ func LikePhoto(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -277,7 +277,7 @@ func DislikePhoto(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
id := sanitize.IdString(c.Param("uid"))
|
||||
id := clean.IdString(c.Param("uid"))
|
||||
m, err := query.PhotoByUID(id)
|
||||
|
||||
if err != nil {
|
||||
|
@ -313,8 +313,8 @@ func PhotoPrimary(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
fileUID := sanitize.IdString(c.Param("file_uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
fileUID := clean.IdString(c.Param("file_uid"))
|
||||
err := query.SetPhotoPrimary(uid, fileUID)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -29,7 +29,7 @@ func AddPhotoLabel(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
m, err := query.PhotoByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.PhotoByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -70,7 +70,7 @@ func AddPhotoLabel(router *gin.RouterGroup) {
|
|||
}
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(sanitize.IdString(c.Param("uid")))
|
||||
p, err := query.PhotoPreloadByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -78,7 +78,7 @@ func AddPhotoLabel(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := p.SaveLabels(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -104,24 +104,24 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
m, err := query.PhotoByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.PhotoByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
labelId, err := strconv.Atoi(sanitize.Token(c.Param("id")))
|
||||
labelId, err := strconv.Atoi(clean.Token(c.Param("id")))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
label, err := query.PhotoLabel(m.ID, uint(labelId))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
|||
logError("label", entity.Db().Save(&label).Error)
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(sanitize.IdString(c.Param("uid")))
|
||||
p, err := query.PhotoPreloadByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -142,11 +142,11 @@ func RemovePhotoLabel(router *gin.RouterGroup) {
|
|||
logError("label", p.RemoveKeyword(label.Label.LabelName))
|
||||
|
||||
if err := p.SaveLabels(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, sanitize.IdString(c.Param("uid")), c)
|
||||
PublishPhotoEvent(EntityUpdated, clean.IdString(c.Param("uid")), c)
|
||||
|
||||
event.Success("label removed")
|
||||
|
||||
|
@ -170,24 +170,24 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
|
||||
// TODO: Code clean-up, simplify
|
||||
|
||||
m, err := query.PhotoByUID(sanitize.IdString(c.Param("uid")))
|
||||
m, err := query.PhotoByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
labelId, err := strconv.Atoi(sanitize.Token(c.Param("id")))
|
||||
labelId, err := strconv.Atoi(clean.Token(c.Param("id")))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
label, err := query.PhotoLabel(m.ID, uint(labelId))
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -197,11 +197,11 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := label.Save(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
p, err := query.PhotoPreloadByUID(sanitize.IdString(c.Param("uid")))
|
||||
p, err := query.PhotoPreloadByUID(clean.IdString(c.Param("uid")))
|
||||
|
||||
if err != nil {
|
||||
AbortEntityNotFound(c)
|
||||
|
@ -209,11 +209,11 @@ func UpdatePhotoLabel(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := p.SaveLabels(); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, sanitize.IdString(c.Param("uid")), c)
|
||||
PublishPhotoEvent(EntityUpdated, clean.IdString(c.Param("uid")), c)
|
||||
|
||||
event.Success("label saved")
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -34,7 +34,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
conf := service.Config()
|
||||
fileUID := sanitize.IdString(c.Param("file_uid"))
|
||||
fileUID := clean.IdString(c.Param("file_uid"))
|
||||
file, err := query.FileByUID(fileUID)
|
||||
|
||||
if err != nil {
|
||||
|
@ -63,7 +63,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
unstackFile, err := photoprism.NewMediaFile(fileName)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
} else if file.Photo == nil {
|
||||
|
@ -76,7 +76,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
stackPrimary, err := stackPhoto.PrimaryFile()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: cannot find primary file for %s (unstack)", sanitize.Log(baseName))
|
||||
log.Errorf("photo: cannot find primary file for %s (unstack)", clean.Log(baseName))
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
@ -87,15 +87,15 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
related, err := unstackFile.RelatedFiles(false)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
} else if related.Len() == 0 {
|
||||
log.Errorf("photo: found no files for %s (unstack)", sanitize.Log(baseName))
|
||||
log.Errorf("photo: found no files for %s (unstack)", clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
} else if related.Main == nil {
|
||||
log.Errorf("photo: found no main file for %s (unstack)", sanitize.Log(baseName))
|
||||
log.Errorf("photo: found no main file for %s (unstack)", clean.Log(baseName))
|
||||
AbortEntityNotFound(c)
|
||||
return
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
|
||||
if unstackFile.BasePrefix(false) == stackPhoto.PhotoName {
|
||||
if conf.ReadOnly() {
|
||||
log.Errorf("photo: cannot rename files in read only mode (unstack %s)", sanitize.Log(baseName))
|
||||
log.Errorf("photo: cannot rename files in read only mode (unstack %s)", clean.Log(baseName))
|
||||
AbortFeatureDisabled(c)
|
||||
return
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
destName := fmt.Sprintf("%s.%s%s", unstackFile.AbsPrefix(false), unstackFile.Checksum(), unstackFile.Extension())
|
||||
|
||||
if err := unstackFile.Move(destName); err != nil {
|
||||
log.Errorf("photo: cannot rename %s to %s (unstack)", sanitize.Log(unstackFile.BaseName()), sanitize.Log(filepath.Base(destName)))
|
||||
log.Errorf("photo: cannot rename %s to %s (unstack)", clean.Log(unstackFile.BaseName()), clean.Log(filepath.Base(destName)))
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
newPhoto.PhotoName = unstackFile.BasePrefix(false)
|
||||
|
||||
if err := newPhoto.Create(); err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), clean.Log(baseName))
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
@ -150,17 +150,17 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
newPhoto.ID, newPhoto.PhotoUID, r.RootRelName(),
|
||||
relName, relRoot).Error; err != nil {
|
||||
// Handle error...
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), clean.Log(r.BaseName()))
|
||||
|
||||
// Remove new photo from index.
|
||||
if _, err := newPhoto.Delete(true); err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), clean.Log(r.BaseName()))
|
||||
}
|
||||
|
||||
// Revert file rename.
|
||||
if unstackSingle {
|
||||
if err := r.Move(photoprism.FileName(relRoot, relName)); err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), sanitize.Log(r.BaseName()))
|
||||
log.Errorf("photo: %s (unstack %s)", err.Error(), clean.Log(r.BaseName()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,21 +173,21 @@ func PhotoUnstack(router *gin.RouterGroup) {
|
|||
|
||||
// Index unstacked files.
|
||||
if res := ind.FileName(unstackFile.FileName(), photoprism.IndexOptionsSingle()); res.Failed() {
|
||||
log.Errorf("photo: %s (unstack %s)", res.Err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", res.Err, clean.Log(baseName))
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Reset type for existing photo stack to image.
|
||||
if err := stackPhoto.Update("PhotoType", entity.MediaImage); err != nil {
|
||||
log.Errorf("photo: %s (unstack %s)", err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", err, clean.Log(baseName))
|
||||
AbortUnexpected(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Re-index existing photo stack.
|
||||
if res := ind.FileName(photoprism.FileName(stackPrimary.FileRoot, stackPrimary.FileName), photoprism.IndexOptionsSingle()); res.Failed() {
|
||||
log.Errorf("photo: %s (unstack %s)", res.Err, sanitize.Log(baseName))
|
||||
log.Errorf("photo: %s (unstack %s)", res.Err, clean.Log(baseName))
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func SearchAlbums(router *gin.RouterGroup) {
|
|||
result, err := search.Albums(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func SearchFaces(router *gin.RouterGroup) {
|
|||
result, err := search.Faces(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
|
@ -68,7 +68,7 @@ func SearchFolders(router *gin.RouterGroup, urlPath, rootName, rootPath string)
|
|||
listFiles := f.Files
|
||||
uncached := listFiles || f.Uncached
|
||||
resp := FoldersResponse{Root: rootName, Recursive: recursive, Cached: !uncached}
|
||||
path := sanitize.Path(c.Param("path"))
|
||||
path := clean.Path(c.Param("path"))
|
||||
|
||||
cacheKey := fmt.Sprintf("folder:%s:%t:%t", filepath.Join(rootName, path), recursive, listFiles)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/search"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -66,7 +66,7 @@ func SearchGeo(router *gin.RouterGroup) {
|
|||
var resp []byte
|
||||
|
||||
// Render JSON response.
|
||||
switch sanitize.Token(c.Param("format")) {
|
||||
switch clean.Token(c.Param("format")) {
|
||||
case "view":
|
||||
conf := service.Config()
|
||||
resp, err = photos.ViewerJSON(conf.ContentUri(), conf.ApiUri(), conf.PreviewToken(), conf.DownloadToken())
|
||||
|
@ -75,7 +75,7 @@ func SearchGeo(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ func SearchLabels(router *gin.RouterGroup) {
|
|||
result, err := search.Labels(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func SearchSubjects(router *gin.RouterGroup) {
|
|||
result, err := search.Subjects(f)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(400, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -91,7 +91,7 @@ func CreateSession(router *gin.RouterGroup) {
|
|||
// DELETE /api/v1/session/:id
|
||||
func DeleteSession(router *gin.RouterGroup) {
|
||||
router.DELETE("/session/:id", func(c *gin.Context) {
|
||||
id := sanitize.Token(c.Param("id"))
|
||||
id := clean.Token(c.Param("id"))
|
||||
|
||||
service.Session().Delete(id)
|
||||
|
||||
|
@ -128,10 +128,10 @@ func Auth(id string, resource acl.Resource, action acl.Action) session.Data {
|
|||
|
||||
// InvalidPreviewToken returns true if the token is invalid.
|
||||
func InvalidPreviewToken(c *gin.Context) bool {
|
||||
token := sanitize.Token(c.Param("token"))
|
||||
token := clean.Token(c.Param("token"))
|
||||
|
||||
if token == "" {
|
||||
token = sanitize.Token(c.Query("t"))
|
||||
token = clean.Token(c.Query("t"))
|
||||
}
|
||||
|
||||
return service.Config().InvalidPreviewToken(token)
|
||||
|
@ -139,5 +139,5 @@ func InvalidPreviewToken(c *gin.Context) bool {
|
|||
|
||||
// InvalidDownloadToken returns true if the token is invalid.
|
||||
func InvalidDownloadToken(c *gin.Context) bool {
|
||||
return service.Config().InvalidDownloadToken(sanitize.Token(c.Query("t")))
|
||||
return service.Config().InvalidDownloadToken(clean.Token(c.Query("t")))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// GET /s/:token/...
|
||||
|
@ -17,7 +17,7 @@ func Shares(router *gin.RouterGroup) {
|
|||
router.GET("/:token", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
|
||||
token := sanitize.Token(c.Param("token"))
|
||||
token := clean.Token(c.Param("token"))
|
||||
|
||||
links := entity.FindValidLinks(token, "")
|
||||
|
||||
|
@ -36,8 +36,8 @@ func Shares(router *gin.RouterGroup) {
|
|||
router.GET("/:token/:share", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
|
||||
token := sanitize.Token(c.Param("token"))
|
||||
share := sanitize.Token(c.Param("share"))
|
||||
token := clean.Token(c.Param("token"))
|
||||
share := clean.Token(c.Param("share"))
|
||||
|
||||
links := entity.FindValidLinks(token, share)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -30,8 +30,8 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
router.GET("/:token/:share/preview", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
|
||||
token := sanitize.Token(c.Param("token"))
|
||||
share := sanitize.Token(c.Param("share"))
|
||||
token := clean.Token(c.Param("token"))
|
||||
share := clean.Token(c.Param("share"))
|
||||
links := entity.FindLinks(token, share)
|
||||
|
||||
if len(links) != 1 {
|
||||
|
@ -52,13 +52,13 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
yesterday := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
if info, err := os.Stat(previewFilename); err != nil {
|
||||
log.Debugf("share: creating new preview for %s", sanitize.Log(share))
|
||||
log.Debugf("share: creating new preview for %s", clean.Log(share))
|
||||
} else if info.ModTime().After(yesterday) {
|
||||
log.Debugf("share: using cached preview for %s", sanitize.Log(share))
|
||||
log.Debugf("share: using cached preview for %s", clean.Log(share))
|
||||
c.File(previewFilename)
|
||||
return
|
||||
} else if err := os.Remove(previewFilename); err != nil {
|
||||
log.Errorf("share: could not remove old preview of %s", sanitize.Log(share))
|
||||
log.Errorf("share: could not remove old preview of %s", clean.Log(share))
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
|
||||
return
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("share: file %s is missing (preview)", sanitize.Log(f.FileName))
|
||||
log.Errorf("share: file %s is missing (preview)", clean.Log(f.FileName))
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
|
||||
return
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("share: file %s is missing (preview)", sanitize.Log(f.FileName))
|
||||
log.Errorf("share: file %s is missing (preview)", clean.Log(f.FileName))
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
|
@ -28,7 +28,7 @@ func GetSubject(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
if subj := entity.FindSubject(sanitize.IdString(c.Param("uid"))); subj == nil {
|
||||
if subj := entity.FindSubject(clean.IdString(c.Param("uid"))); subj == nil {
|
||||
Abort(c, http.StatusNotFound, i18n.ErrSubjectNotFound)
|
||||
return
|
||||
} else {
|
||||
|
@ -56,7 +56,7 @@ func UpdateSubject(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
m := entity.FindSubject(uid)
|
||||
|
||||
if m == nil {
|
||||
|
@ -109,7 +109,7 @@ func LikeSubject(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
subj := entity.FindSubject(uid)
|
||||
|
||||
if subj == nil {
|
||||
|
@ -118,7 +118,7 @@ func LikeSubject(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := subj.Update("SubjFavorite", true); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ func DislikeSubject(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
subj := entity.FindSubject(uid)
|
||||
|
||||
if subj == nil {
|
||||
|
@ -152,7 +152,7 @@ func DislikeSubject(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
if err := subj.Update("SubjFavorite", false); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UcFirst(err.Error())})
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": txt.UpperFirst(err.Error())})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// GetThumb returns a thumbnail image matching the file hash, crop area, and type.
|
||||
|
@ -37,16 +37,16 @@ func GetThumb(router *gin.RouterGroup) {
|
|||
start := time.Now()
|
||||
conf := service.Config()
|
||||
download := c.Query("download") != ""
|
||||
fileHash, cropArea := crop.ParseThumb(sanitize.Token(c.Param("thumb")))
|
||||
fileHash, cropArea := crop.ParseThumb(clean.Token(c.Param("thumb")))
|
||||
|
||||
// Is cropped thumbnail?
|
||||
if cropArea != "" {
|
||||
cropName := crop.Name(sanitize.Token(c.Param("size")))
|
||||
cropName := crop.Name(clean.Token(c.Param("size")))
|
||||
|
||||
cropSize, ok := crop.Sizes[cropName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("%s: invalid size %s", logPrefix, sanitize.Log(string(cropName)))
|
||||
log.Errorf("%s: invalid size %s", logPrefix, clean.Log(string(cropName)))
|
||||
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -73,12 +73,12 @@ func GetThumb(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
thumbName := thumb.Name(sanitize.Token(c.Param("size")))
|
||||
thumbName := thumb.Name(clean.Token(c.Param("size")))
|
||||
|
||||
size, ok := thumb.Sizes[thumbName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("%s: invalid size %s", logPrefix, sanitize.Log(thumbName.String()))
|
||||
log.Errorf("%s: invalid size %s", logPrefix, clean.Log(thumbName.String()))
|
||||
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -153,17 +153,17 @@ func GetThumb(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("%s: file %s is missing", logPrefix, sanitize.Log(f.FileName))
|
||||
log.Errorf("%s: file %s is missing", logPrefix, clean.Log(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
logError(logPrefix, f.Update("FileMissing", true))
|
||||
|
||||
if f.AllFilesMissing() {
|
||||
log.Infof("%s: deleting photo, all files missing for %s", logPrefix, sanitize.Log(f.FileName))
|
||||
log.Infof("%s: deleting photo, all files missing for %s", logPrefix, clean.Log(f.FileName))
|
||||
|
||||
if _, err := f.RelatedPhoto().Delete(false); err != nil {
|
||||
log.Errorf("%s: %s while deleting %s", logPrefix, err, sanitize.Log(f.FileName))
|
||||
log.Errorf("%s: %s while deleting %s", logPrefix, err, clean.Log(f.FileName))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// POST /api/v1/upload/:path
|
||||
|
@ -32,7 +32,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
start := time.Now()
|
||||
subPath := sanitize.Path(c.Param("path"))
|
||||
subPath := clean.Path(c.Param("path"))
|
||||
|
||||
f, err := c.MultipartForm()
|
||||
|
||||
|
@ -51,7 +51,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
p := path.Join(conf.ImportPath(), "upload", subPath)
|
||||
|
||||
if err := os.MkdirAll(p, os.ModePerm); err != nil {
|
||||
log.Errorf("upload: failed creating folder %s", sanitize.Log(subPath))
|
||||
log.Errorf("upload: failed creating folder %s", clean.Log(subPath))
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
@ -59,10 +59,10 @@ func Upload(router *gin.RouterGroup) {
|
|||
for _, file := range files {
|
||||
filename := path.Join(p, filepath.Base(file.Filename))
|
||||
|
||||
log.Debugf("upload: saving file %s", sanitize.Log(file.Filename))
|
||||
log.Debugf("upload: saving file %s", clean.Log(file.Filename))
|
||||
|
||||
if err := c.SaveUploadedFile(file, filename); err != nil {
|
||||
log.Errorf("upload: failed saving file %s", sanitize.Log(filepath.Base(file.Filename)))
|
||||
log.Errorf("upload: failed saving file %s", clean.Log(filepath.Base(file.Filename)))
|
||||
AbortBadRequest(c)
|
||||
return
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
continue
|
||||
}
|
||||
|
||||
log.Infof("nsfw: %s might be offensive", sanitize.Log(filename))
|
||||
log.Infof("nsfw: %s might be offensive", clean.Log(filename))
|
||||
|
||||
containsNSFW = true
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func Upload(router *gin.RouterGroup) {
|
|||
if containsNSFW {
|
||||
for _, filename := range uploads {
|
||||
if err := os.Remove(filename); err != nil {
|
||||
log.Errorf("nsfw: could not delete %s", sanitize.Log(filename))
|
||||
log.Errorf("nsfw: could not delete %s", clean.Log(filename))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/acl"
|
||||
|
@ -30,7 +30,7 @@ func ChangePassword(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
uid := sanitize.IdString(c.Param("uid"))
|
||||
uid := clean.IdString(c.Param("uid"))
|
||||
m := entity.FindUserByUID(uid)
|
||||
|
||||
if s.User.UserUID != m.UserUID {
|
||||
|
|
|
@ -3,13 +3,14 @@ package api
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/video"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/video"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// GetVideo streams videos.
|
||||
|
@ -26,13 +27,13 @@ func GetVideo(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
fileHash := sanitize.Token(c.Param("hash"))
|
||||
formatName := sanitize.Token(c.Param("format"))
|
||||
fileHash := clean.Token(c.Param("hash"))
|
||||
formatName := clean.Token(c.Param("format"))
|
||||
|
||||
format, ok := video.Formats[formatName]
|
||||
format, ok := video.Types[formatName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("video: invalid format %s", sanitize.Log(formatName))
|
||||
log.Errorf("video: invalid format %s", clean.Log(formatName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
|
||||
return
|
||||
}
|
||||
|
@ -64,7 +65,7 @@ func GetVideo(router *gin.RouterGroup) {
|
|||
fileName := photoprism.FileName(f.FileRoot, f.FileName)
|
||||
|
||||
if mf, err := photoprism.NewMediaFile(fileName); err != nil {
|
||||
log.Errorf("video: file %s is missing", sanitize.Log(f.FileName))
|
||||
log.Errorf("video: file %s is missing", clean.Log(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
|
@ -75,7 +76,7 @@ func GetVideo(router *gin.RouterGroup) {
|
|||
conv := service.Convert()
|
||||
|
||||
if avcFile, err := conv.ToAvc(mf, service.Config().FFmpegEncoder(), false, false); err != nil {
|
||||
log.Errorf("video: transcoding %s failed", sanitize.Log(f.FileName))
|
||||
log.Errorf("video: transcoding %s failed", clean.Log(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
|
||||
return
|
||||
} else {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
var autoImport = time.Time{}
|
||||
|
@ -66,7 +66,7 @@ func Import() error {
|
|||
|
||||
api.RemoveFromFolderCache(entity.RootImport)
|
||||
|
||||
event.InfoMsg(i18n.MsgCopyingFilesFrom, sanitize.Log(filepath.Base(path)))
|
||||
event.InfoMsg(i18n.MsgCopyingFilesFrom, clean.Log(filepath.Base(path)))
|
||||
|
||||
var opt photoprism.ImportOptions
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"text/template"
|
||||
"unicode"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
|
@ -34,7 +34,7 @@ func main() {
|
|||
fileName := "rules.yml"
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Panicf("classify: found no label rules in %s", sanitize.Log(filepath.Base(fileName)))
|
||||
log.Panicf("classify: found no label rules in %s", clean.Log(filepath.Base(fileName)))
|
||||
}
|
||||
|
||||
yamlConfig, err := os.ReadFile(fileName)
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
tf "github.com/tensorflow/tensorflow/tensorflow/go"
|
||||
)
|
||||
|
||||
|
@ -148,7 +148,7 @@ func (t *TensorFlow) loadModel() error {
|
|||
|
||||
modelPath := path.Join(t.modelsPath, t.modelName)
|
||||
|
||||
log.Infof("classify: loading %s", sanitize.Log(filepath.Base(modelPath)))
|
||||
log.Infof("classify: loading %s", clean.Log(filepath.Base(modelPath)))
|
||||
|
||||
// Load model
|
||||
model, err := tf.LoadSavedModel(modelPath, t.modelTags, nil)
|
||||
|
|
|
@ -16,8 +16,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
const backupDescription = "A user-defined SQL dump FILENAME or - for stdout can be passed as the first argument. " +
|
||||
|
@ -113,7 +113,7 @@ func backupAction(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
log.Infof("writing SQL dump to %s", sanitize.Log(indexFileName))
|
||||
log.Infof("writing SQL dump to %s", clean.Log(indexFileName))
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
|
@ -178,7 +178,7 @@ func backupAction(ctx *cli.Context) error {
|
|||
albumsPath = conf.AlbumsPath()
|
||||
}
|
||||
|
||||
log.Infof("saving albums in %s", sanitize.Log(albumsPath))
|
||||
log.Infof("saving albums in %s", clean.Log(albumsPath))
|
||||
|
||||
if count, err := photoprism.BackupAlbums(albumsPath, true); err != nil {
|
||||
return err
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// ConvertCommand registers the convert cli command.
|
||||
|
@ -54,7 +54,7 @@ func convertAction(ctx *cli.Context) error {
|
|||
convertPath = filepath.Join(convertPath, subPath)
|
||||
}
|
||||
|
||||
log.Infof("converting originals in %s", sanitize.Log(convertPath))
|
||||
log.Infof("converting originals in %s", clean.Log(convertPath))
|
||||
|
||||
w := service.Convert()
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// FacesCommand registers the facial recognition subcommands.
|
||||
|
@ -239,9 +239,9 @@ func facesIndexAction(ctx *cli.Context) error {
|
|||
subPath := strings.TrimSpace(ctx.Args().First())
|
||||
|
||||
if subPath == "" {
|
||||
log.Infof("finding faces in %s", sanitize.Log(conf.OriginalsPath()))
|
||||
log.Infof("finding faces in %s", clean.Log(conf.OriginalsPath()))
|
||||
} else {
|
||||
log.Infof("finding faces in %s", sanitize.Log(filepath.Join(conf.OriginalsPath(), subPath)))
|
||||
log.Infof("finding faces in %s", clean.Log(filepath.Join(conf.OriginalsPath(), subPath)))
|
||||
}
|
||||
|
||||
if conf.ReadOnly() {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// IndexCommand registers the index cli command.
|
||||
|
@ -57,9 +57,9 @@ func indexAction(ctx *cli.Context) error {
|
|||
subPath := strings.TrimSpace(ctx.Args().First())
|
||||
|
||||
if subPath == "" {
|
||||
log.Infof("indexing originals in %s", sanitize.Log(conf.OriginalsPath()))
|
||||
log.Infof("indexing originals in %s", clean.Log(conf.OriginalsPath()))
|
||||
} else {
|
||||
log.Infof("indexing originals in %s", sanitize.Log(filepath.Join(conf.OriginalsPath(), subPath)))
|
||||
log.Infof("indexing originals in %s", clean.Log(filepath.Join(conf.OriginalsPath(), subPath)))
|
||||
}
|
||||
|
||||
if conf.ReadOnly() {
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// PasswdCommand updates a password.
|
||||
|
@ -39,7 +39,7 @@ func passwdAction(ctx *cli.Context) error {
|
|||
|
||||
user := entity.Admin
|
||||
|
||||
log.Infof("please enter a new password for %s (at least 6 characters)\n", sanitize.Log(user.Username()))
|
||||
log.Infof("please enter a new password for %s (at least 6 characters)\n", clean.Log(user.Username()))
|
||||
|
||||
newPassword := getPassword("New Password: ")
|
||||
|
||||
|
@ -57,7 +57,7 @@ func passwdAction(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infof("changed password for %s\n", sanitize.Log(user.Username()))
|
||||
log.Infof("changed password for %s\n", clean.Log(user.Username()))
|
||||
|
||||
conf.Shutdown()
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// PurgeCommand registers the index cli command.
|
||||
|
@ -56,9 +56,9 @@ func purgeAction(ctx *cli.Context) error {
|
|||
subPath := strings.TrimSpace(ctx.Args().First())
|
||||
|
||||
if subPath == "" {
|
||||
log.Infof("purge: removing missing files in %s", sanitize.Log(filepath.Base(conf.OriginalsPath())))
|
||||
log.Infof("purge: removing missing files in %s", clean.Log(filepath.Base(conf.OriginalsPath())))
|
||||
} else {
|
||||
log.Infof("purge: removing missing files in %s", sanitize.Log(fs.RelName(filepath.Join(conf.OriginalsPath(), subPath), filepath.Dir(conf.OriginalsPath()))))
|
||||
log.Infof("purge: removing missing files in %s", clean.Log(fs.RelName(filepath.Join(conf.OriginalsPath(), subPath), filepath.Dir(conf.OriginalsPath()))))
|
||||
}
|
||||
|
||||
if conf.ReadOnly() {
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
const restoreDescription = "A user-defined SQL dump FILENAME can be passed as the first argument. " +
|
||||
|
@ -124,7 +124,7 @@ func restoreAction(ctx *cli.Context) error {
|
|||
log.Warnf("replacing existing index with %d photos", counts.Photos)
|
||||
}
|
||||
|
||||
log.Infof("restoring index from %s", sanitize.Log(indexFileName))
|
||||
log.Infof("restoring index from %s", clean.Log(indexFileName))
|
||||
|
||||
sqlBackup, err := os.ReadFile(indexFileName)
|
||||
|
||||
|
@ -203,9 +203,9 @@ func restoreAction(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
if !fs.PathExists(albumsPath) {
|
||||
log.Warnf("album files path %s not found", sanitize.Log(albumsPath))
|
||||
log.Warnf("album files path %s not found", clean.Log(albumsPath))
|
||||
} else {
|
||||
log.Infof("restoring albums from %s", sanitize.Log(albumsPath))
|
||||
log.Infof("restoring albums from %s", clean.Log(albumsPath))
|
||||
|
||||
if count, err := photoprism.RestoreAlbums(albumsPath, true); err != nil {
|
||||
return err
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/report"
|
||||
)
|
||||
|
||||
|
@ -15,8 +16,8 @@ var ShowFormatsCommand = cli.Command{
|
|||
Usage: "Lists supported media and sidecar file formats",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "compact, c",
|
||||
Usage: "hide format descriptions to make the output more compact",
|
||||
Name: "short, s",
|
||||
Usage: "hides format descriptions",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "md, m",
|
||||
|
@ -28,8 +29,7 @@ var ShowFormatsCommand = cli.Command{
|
|||
|
||||
// showFormatsAction lists supported media and sidecar file formats.
|
||||
func showFormatsAction(ctx *cli.Context) error {
|
||||
rows, cols := fs.Extensions.Formats(true).Report(!ctx.Bool("compact"), true, true)
|
||||
|
||||
rows, cols := media.Report(fs.Extensions.Types(true), !ctx.Bool("short"), true, true)
|
||||
fmt.Println(report.Table(rows, cols, ctx.Bool("md")))
|
||||
|
||||
return nil
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/server"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/workers"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// StartCommand registers the start cli command.
|
||||
|
@ -95,7 +95,7 @@ func startAction(ctx *cli.Context) error {
|
|||
|
||||
if child != nil {
|
||||
if !fs.Overwrite(conf.PIDFilename(), []byte(strconv.Itoa(child.Pid))) {
|
||||
log.Fatalf("failed writing process id to %s", sanitize.Log(conf.PIDFilename()))
|
||||
log.Fatalf("failed writing process id to %s", clean.Log(conf.PIDFilename()))
|
||||
}
|
||||
|
||||
log.Infof("daemon started with process id %v\n", child.Pid)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// StopCommand registers the stop cli command.
|
||||
|
@ -22,7 +22,7 @@ var StopCommand = cli.Command{
|
|||
func stopAction(ctx *cli.Context) error {
|
||||
conf := config.NewConfig(ctx)
|
||||
|
||||
log.Infof("looking for pid in %s", sanitize.Log(conf.PIDFilename()))
|
||||
log.Infof("looking for pid in %s", clean.Log(conf.PIDFilename()))
|
||||
|
||||
dcxt := new(daemon.Context)
|
||||
dcxt.PidFileName = conf.PIDFilename()
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// ThumbsCommand registers the resample cli command.
|
||||
|
@ -34,7 +34,7 @@ func thumbsAction(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infof("creating thumbnails in %s", sanitize.Log(conf.ThumbPath()))
|
||||
log.Infof("creating thumbnails in %s", clean.Log(conf.ThumbPath()))
|
||||
|
||||
rs := service.Resample()
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// UsersCommand registers user management subcommands.
|
||||
|
@ -183,7 +183,7 @@ func usersDeleteAction(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
actionPrompt := promptui.Prompt{
|
||||
Label: fmt.Sprintf("Delete %s?", sanitize.Log(userName)),
|
||||
Label: fmt.Sprintf("Delete %s?", clean.Log(userName)),
|
||||
IsConfirm: true,
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ func usersDeleteAction(ctx *cli.Context) error {
|
|||
} else if err := m.Delete(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Infof("%s deleted", sanitize.Log(userName))
|
||||
log.Infof("%s deleted", clean.Log(userName))
|
||||
}
|
||||
} else {
|
||||
log.Infof("keeping user")
|
||||
|
@ -242,7 +242,7 @@ func usersUpdateAction(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("password successfully changed: %s\n", sanitize.Log(u.Username()))
|
||||
fmt.Printf("password successfully changed: %s\n", clean.Log(u.Username()))
|
||||
}
|
||||
|
||||
if ctx.IsSet("fullname") {
|
||||
|
@ -261,7 +261,7 @@ func usersUpdateAction(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("user successfully updated: %s\n", sanitize.Log(u.Username()))
|
||||
fmt.Printf("user successfully updated: %s\n", clean.Log(u.Username()))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/colors"
|
||||
"github.com/photoprism/photoprism/pkg/env"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -64,7 +66,7 @@ type ClientConfig struct {
|
|||
Colors []map[string]string `json:"colors"`
|
||||
Categories CategoryLabels `json:"categories"`
|
||||
Clip int `json:"clip"`
|
||||
Server RuntimeInfo `json:"server"`
|
||||
Server env.Resources `json:"server"`
|
||||
}
|
||||
|
||||
// Years represents a list of years.
|
||||
|
@ -384,7 +386,7 @@ func (c *Config) UserConfig() ClientConfig {
|
|||
PreviewToken: c.PreviewToken(),
|
||||
ManifestUri: c.ClientManifestUri(),
|
||||
Clip: txt.ClipDefault,
|
||||
Server: NewRuntimeInfo(),
|
||||
Server: env.Info(),
|
||||
}
|
||||
|
||||
c.Db().
|
||||
|
@ -409,12 +411,12 @@ func (c *Config) UserConfig() ClientConfig {
|
|||
|
||||
c.Db().
|
||||
Table("photos").
|
||||
Select("SUM(photo_type = 'video' AND photo_quality >= 0 AND photo_private = 0) AS videos, " +
|
||||
"SUM(photo_type = 'live' AND photo_quality >= 0 AND photo_private = 0) AS live, " +
|
||||
"SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw','animated') AND photo_private = 0 AND photo_quality >= 0) AS photos, " +
|
||||
"SUM(photo_type IN ('image','raw','live','animated') AND photo_quality < 3 AND photo_quality >= 0 AND photo_private = 0) AS review, " +
|
||||
"SUM(photo_favorite = 1 AND photo_private = 0 AND photo_quality >= 0) AS favorites, " +
|
||||
"SUM(photo_private = 1 AND photo_quality >= 0) AS private").
|
||||
Select("SUM(photo_type = 'video' AND photo_quality > -1 AND photo_private = 0) AS videos, " +
|
||||
"SUM(photo_type = 'live' AND photo_quality > -1 AND photo_private = 0) AS live, " +
|
||||
"SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw','animated') AND photo_private = 0 AND photo_quality > -1) AS photos, " +
|
||||
"SUM(photo_type IN ('image','raw','live','animated') AND photo_quality < 3 AND photo_quality > -1 AND photo_private = 0) AS review, " +
|
||||
"SUM(photo_favorite = 1 AND photo_private = 0 AND photo_quality > -1) AS favorites, " +
|
||||
"SUM(photo_private = 1 AND photo_quality > -1) AS private").
|
||||
Where("photos.id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND (file_missing = 1 OR file_error <> ''))").
|
||||
Where("deleted_at IS NULL").
|
||||
Take(&result.Count)
|
||||
|
@ -460,7 +462,7 @@ func (c *Config) UserConfig() ClientConfig {
|
|||
result.People, _ = query.People()
|
||||
|
||||
c.Db().
|
||||
Where("id IN (SELECT photos.camera_id FROM photos WHERE photos.photo_quality >= 0 OR photos.deleted_at IS NULL)").
|
||||
Where("id IN (SELECT photos.camera_id FROM photos WHERE photos.photo_quality > -1 OR photos.deleted_at IS NULL)").
|
||||
Where("deleted_at IS NULL").
|
||||
Limit(10000).Order("camera_slug").
|
||||
Find(&result.Cameras)
|
||||
|
@ -477,7 +479,7 @@ func (c *Config) UserConfig() ClientConfig {
|
|||
|
||||
c.Db().
|
||||
Table("photos").
|
||||
Where("photo_year > 0 AND (photos.photo_quality >= 0 OR photos.deleted_at IS NULL)").
|
||||
Where("photo_year > 0 AND (photos.photo_quality > -1 OR photos.deleted_at IS NULL)").
|
||||
Order("photo_year DESC").
|
||||
Pluck("DISTINCT photo_year", &result.Years)
|
||||
|
|
@ -29,9 +29,9 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/hub/places"
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
@ -39,37 +39,6 @@ var once sync.Once
|
|||
var LowMem = false
|
||||
var TotalMem uint64
|
||||
|
||||
const MsgSponsor = "PhotoPrism® needs your support!"
|
||||
const SignUpURL = "https://docs.photoprism.app/funding/"
|
||||
const MsgSignUp = "Visit " + SignUpURL + " to learn more."
|
||||
const MsgSponsorCommand = "Since running this command puts additional load on our infrastructure," +
|
||||
" we unfortunately can only offer it to sponsors."
|
||||
|
||||
const ApiUri = "/api/v1" // REST API
|
||||
const StaticUri = "/static" // Static Content
|
||||
|
||||
const DefaultAutoIndexDelay = int(5 * 60) // 5 Minutes
|
||||
const DefaultAutoImportDelay = int(3 * 60) // 3 Minutes
|
||||
|
||||
const DefaultWakeupIntervalSeconds = int(15 * 60) // 15 Minutes
|
||||
const DefaultWakeupInterval = time.Second * time.Duration(DefaultWakeupIntervalSeconds)
|
||||
const MaxWakeupInterval = time.Hour * 24 // 1 Day
|
||||
|
||||
// Megabyte in bytes.
|
||||
const Megabyte = 1000 * 1000
|
||||
|
||||
// Gigabyte in bytes.
|
||||
const Gigabyte = Megabyte * 1000
|
||||
|
||||
// MinMem is the minimum amount of system memory required.
|
||||
const MinMem = Gigabyte
|
||||
|
||||
// RecommendedMem is the recommended amount of system memory.
|
||||
const RecommendedMem = 3 * Gigabyte
|
||||
|
||||
// serialName is the name of the unique storage serial.
|
||||
const serialName = "serial"
|
||||
|
||||
// Config holds database, cache and all parameters of photoprism
|
||||
type Config struct {
|
||||
once sync.Once
|
||||
|
@ -125,16 +94,16 @@ func NewConfig(ctx *cli.Context) *Config {
|
|||
// Initialize options from config file and CLI context.
|
||||
c := &Config{
|
||||
options: NewOptions(ctx),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
env: os.Getenv("DOCKER_ENV"),
|
||||
}
|
||||
|
||||
// Overwrite values with options.yml from config path.
|
||||
if optionsYaml := c.OptionsYaml(); fs.FileExists(optionsYaml) {
|
||||
if err := c.options.Load(optionsYaml); err != nil {
|
||||
log.Warnf("config: failed loading values from %s (%s)", sanitize.Log(optionsYaml), err)
|
||||
log.Warnf("config: failed loading values from %s (%s)", clean.Log(optionsYaml), err)
|
||||
} else {
|
||||
log.Debugf("config: overriding config with values from %s", sanitize.Log(optionsYaml))
|
||||
log.Debugf("config: overriding config with values from %s", clean.Log(optionsYaml))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +179,7 @@ func (c *Config) Init() error {
|
|||
}
|
||||
|
||||
if cpuName := cpuid.CPU.BrandName; cpuName != "" {
|
||||
log.Debugf("config: running on %s, %s memory detected", sanitize.Log(cpuid.CPU.BrandName), humanize.Bytes(TotalMem))
|
||||
log.Debugf("config: running on %s, %s memory detected", clean.Log(cpuid.CPU.BrandName), humanize.Bytes(TotalMem))
|
||||
}
|
||||
|
||||
// Exit if less than 128 MB RAM was detected.
|
||||
|
@ -261,7 +230,7 @@ func (c *Config) readSerial() string {
|
|||
if data, err := os.ReadFile(storageName); err == nil && len(data) == 16 {
|
||||
return string(data)
|
||||
} else {
|
||||
log.Tracef("config: could not read %s (%s)", sanitize.Log(storageName), err)
|
||||
log.Tracef("config: could not read %s (%s)", clean.Log(storageName), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +238,7 @@ func (c *Config) readSerial() string {
|
|||
if data, err := os.ReadFile(backupName); err == nil && len(data) == 16 {
|
||||
return string(data)
|
||||
} else {
|
||||
log.Tracef("config: could not read %s (%s)", sanitize.Log(backupName), err)
|
||||
log.Tracef("config: could not read %s (%s)", clean.Log(backupName), err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,7 +251,7 @@ func (c *Config) initSerial() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
c.serial = rnd.PPID('z')
|
||||
c.serial = rnd.GenerateUID('z')
|
||||
|
||||
storageName := filepath.Join(c.StoragePath(), serialName)
|
||||
backupName := filepath.Join(c.BackupPath(), serialName)
|
||||
|
|
|
@ -35,7 +35,7 @@ func (c *Config) InvalidDownloadToken(t string) bool {
|
|||
// DownloadToken returns the DOWNLOAD api token (you can optionally use a static value for permanent caching).
|
||||
func (c *Config) DownloadToken() string {
|
||||
if c.options.DownloadToken == "" {
|
||||
c.options.DownloadToken = rnd.Token(8)
|
||||
c.options.DownloadToken = rnd.GenerateToken(8)
|
||||
}
|
||||
|
||||
return c.options.DownloadToken
|
34
internal/config/config_const.go
Normal file
34
internal/config/config_const.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
const MsgSponsor = "PhotoPrism® needs your support!"
|
||||
const SignUpURL = "https://docs.photoprism.app/funding/"
|
||||
const MsgSignUp = "Visit " + SignUpURL + " to learn more."
|
||||
const MsgSponsorCommand = "Since running this command puts additional load on our infrastructure," +
|
||||
" we unfortunately can only offer it to sponsors."
|
||||
|
||||
const ApiUri = "/api/v1" // REST API
|
||||
const StaticUri = "/static" // Static Content
|
||||
|
||||
const DefaultAutoIndexDelay = int(5 * 60) // 5 Minutes
|
||||
const DefaultAutoImportDelay = int(3 * 60) // 3 Minutes
|
||||
|
||||
const DefaultWakeupIntervalSeconds = int(15 * 60) // 15 Minutes
|
||||
const DefaultWakeupInterval = time.Second * time.Duration(DefaultWakeupIntervalSeconds)
|
||||
const MaxWakeupInterval = time.Hour * 24 // 1 Day
|
||||
|
||||
// Megabyte in bytes.
|
||||
const Megabyte = 1000 * 1000
|
||||
|
||||
// Gigabyte in bytes.
|
||||
const Gigabyte = Megabyte * 1000
|
||||
|
||||
// MinMem is the minimum amount of system memory required.
|
||||
const MinMem = Gigabyte
|
||||
|
||||
// RecommendedMem is the recommended amount of system memory.
|
||||
const RecommendedMem = 3 * Gigabyte
|
||||
|
||||
// serialName is the name of the unique storage serial.
|
||||
const serialName = "serial"
|
|
@ -7,8 +7,8 @@ import (
|
|||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// binPaths stores known executable paths.
|
||||
|
@ -48,9 +48,9 @@ func findExecutable(configBin, defaultBin string) (binPath string) {
|
|||
func (c *Config) CreateDirectories() error {
|
||||
createError := func(path string, err error) (result error) {
|
||||
if fs.FileExists(path) {
|
||||
result = fmt.Errorf("directory path %s is a file, please check your configuration", sanitize.Log(path))
|
||||
result = fmt.Errorf("directory path %s is a file, please check your configuration", clean.Log(path))
|
||||
} else {
|
||||
result = fmt.Errorf("failed to create the directory %s, check configuration and permissions", sanitize.Log(path))
|
||||
result = fmt.Errorf("failed to create the directory %s, check configuration and permissions", clean.Log(path))
|
||||
}
|
||||
|
||||
log.Debug(err)
|
||||
|
@ -59,7 +59,7 @@ func (c *Config) CreateDirectories() error {
|
|||
}
|
||||
|
||||
notFoundError := func(name string) error {
|
||||
return fmt.Errorf("invalid %s path, check configuration and permissions", sanitize.Log(name))
|
||||
return fmt.Errorf("invalid %s path, check configuration and permissions", clean.Log(name))
|
||||
}
|
||||
|
||||
if c.AssetsPath() == "" {
|
||||
|
@ -154,11 +154,11 @@ func (c *Config) CreateDirectories() error {
|
|||
|
||||
if c.DarktableEnabled() {
|
||||
if cachePath, err := c.CreateDarktableCachePath(); err != nil {
|
||||
return fmt.Errorf("could not create darktable cache path %s", sanitize.Log(cachePath))
|
||||
return fmt.Errorf("could not create darktable cache path %s", clean.Log(cachePath))
|
||||
}
|
||||
|
||||
if configPath, err := c.CreateDarktableConfigPath(); err != nil {
|
||||
return fmt.Errorf("could not create darktable cache path %s", sanitize.Log(configPath))
|
||||
return fmt.Errorf("could not create darktable cache path %s", clean.Log(configPath))
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ func TestConfig_CreateDirectories(t *testing.T) {
|
|||
|
||||
c := &Config{
|
||||
options: NewTestOptions("config"),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
if err := c.CreateDirectories(); err != nil {
|
||||
|
@ -106,7 +106,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
c.options.AssetsPath = ""
|
||||
|
||||
|
@ -130,7 +130,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
c.options.StoragePath = "/-*&^%$#@!`~"
|
||||
|
@ -147,7 +147,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
c.options.OriginalsPath = ""
|
||||
|
||||
|
@ -172,7 +172,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
c.options.ImportPath = ""
|
||||
|
||||
|
@ -197,7 +197,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
c.options.SidecarPath = "/-*&^%$#@!`~"
|
||||
|
@ -214,7 +214,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
c.options.CachePath = "/-*&^%$#@!`~"
|
||||
|
@ -231,7 +231,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
c.options.ConfigPath = "/-*&^%$#@!`~"
|
||||
|
@ -248,7 +248,7 @@ func TestConfig_CreateDirectories2(t *testing.T) {
|
|||
defer testConfigMutex.Unlock()
|
||||
c := &Config{
|
||||
options: NewTestOptions(),
|
||||
token: rnd.Token(8),
|
||||
token: rnd.GenerateToken(8),
|
||||
}
|
||||
|
||||
c.options.TempPath = "/-*&^%$#@!`~"
|
|
@ -4,10 +4,30 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// DefaultTheme returns the default user interface theme name.
|
||||
func (c *Config) DefaultTheme() string {
|
||||
if c.options.DefaultTheme == "" || !c.Sponsor() {
|
||||
return "default"
|
||||
}
|
||||
|
||||
return c.options.DefaultTheme
|
||||
}
|
||||
|
||||
// DefaultLocale returns the default user interface language locale name.
|
||||
func (c *Config) DefaultLocale() string {
|
||||
if c.options.DefaultLocale == "" {
|
||||
return i18n.Default.Locale()
|
||||
}
|
||||
|
||||
return c.options.DefaultLocale
|
||||
}
|
||||
|
||||
// AppIcon returns the app icon when installed on a device.
|
||||
func (c *Config) AppIcon() string {
|
||||
defaultIcon := "logo"
|
|
@ -7,6 +7,31 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConfig_DefaultTheme(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = false
|
||||
c.options.DefaultTheme = "grayscale"
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = true
|
||||
assert.Equal(t, "grayscale", c.DefaultTheme())
|
||||
c.options.DefaultTheme = ""
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = false
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
}
|
||||
|
||||
func TestConfig_DefaultLocale(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "en", c.DefaultLocale())
|
||||
c.options.DefaultLocale = "de"
|
||||
assert.Equal(t, "de", c.DefaultLocale())
|
||||
c.options.DefaultLocale = ""
|
||||
assert.Equal(t, "en", c.DefaultLocale())
|
||||
}
|
||||
|
||||
func TestConfig_AppIcon(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
)
|
||||
|
||||
// DefaultTheme returns the default user interface theme name.
|
||||
func (c *Config) DefaultTheme() string {
|
||||
if c.options.DefaultTheme == "" || !c.Sponsor() {
|
||||
return "default"
|
||||
}
|
||||
|
||||
return c.options.DefaultTheme
|
||||
}
|
||||
|
||||
// DefaultLocale returns the default user interface language locale name.
|
||||
func (c *Config) DefaultLocale() string {
|
||||
if c.options.DefaultLocale == "" {
|
||||
return i18n.Default.Locale()
|
||||
}
|
||||
|
||||
return c.options.DefaultLocale
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConfig_DefaultTheme(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = false
|
||||
c.options.DefaultTheme = "grayscale"
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = true
|
||||
assert.Equal(t, "grayscale", c.DefaultTheme())
|
||||
c.options.DefaultTheme = ""
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
c.options.Sponsor = false
|
||||
assert.Equal(t, "default", c.DefaultTheme())
|
||||
}
|
||||
|
||||
func TestConfig_DefaultLocale(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "en", c.DefaultLocale())
|
||||
c.options.DefaultLocale = "de"
|
||||
assert.Equal(t, "de", c.DefaultLocale())
|
||||
c.options.DefaultLocale = ""
|
||||
assert.Equal(t, "en", c.DefaultLocale())
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Define photoprism specific errors
|
||||
var (
|
||||
ErrReadOnly = errors.New("not available in read-only mode")
|
||||
ErrUnauthorized = errors.New("please log in and try again")
|
||||
ErrUploadNSFW = errors.New("upload might be offensive")
|
||||
)
|
||||
|
||||
func LogError(err error) {
|
||||
if err != nil {
|
||||
log.Errorf("config: %s", err.Error())
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/urfave/cli"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -150,9 +150,9 @@ func NewOptions(ctx *cli.Context) *Options {
|
|||
if defaultsYaml := ctx.GlobalString("defaults-yaml"); defaultsYaml == "" {
|
||||
log.Tracef("config: defaults yaml file not specified")
|
||||
} else if c.DefaultsYaml = fs.Abs(defaultsYaml); !fs.FileExists(c.DefaultsYaml) {
|
||||
log.Tracef("config: defaults file %s does not exist", sanitize.Log(c.DefaultsYaml))
|
||||
log.Tracef("config: defaults file %s does not exist", clean.Log(c.DefaultsYaml))
|
||||
} else if err := c.Load(c.DefaultsYaml); err != nil {
|
||||
log.Warnf("config: failed loading defaults from %s (%s)", sanitize.Log(c.DefaultsYaml), err)
|
||||
log.Warnf("config: failed loading defaults from %s (%s)", clean.Log(c.DefaultsYaml), err)
|
||||
}
|
||||
|
||||
if err := c.SetContext(ctx); err != nil {
|
15
internal/config/log_error.go
Normal file
15
internal/config/log_error.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrReadOnly = errors.New("not available in read-only mode")
|
||||
)
|
||||
|
||||
func LogError(err error) {
|
||||
if err != nil {
|
||||
log.Errorf("config: %s", err.Error())
|
||||
}
|
||||
}
|
|
@ -8,89 +8,10 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/i18n"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/sanitize"
|
||||
)
|
||||
|
||||
// UISettings represents user interface settings.
|
||||
type UISettings struct {
|
||||
Scrollbar bool `json:"scrollbar" yaml:"Scrollbar"`
|
||||
Zoom bool `json:"zoom" yaml:"Zoom"`
|
||||
Theme string `json:"theme" yaml:"Theme"`
|
||||
Language string `json:"language" yaml:"Language"`
|
||||
}
|
||||
|
||||
// SearchSettings represents search UI preferences.
|
||||
type SearchSettings struct {
|
||||
BatchSize int `json:"batchSize" yaml:"BatchSize"`
|
||||
}
|
||||
|
||||
// TemplateSettings represents template settings for the UI and messaging.
|
||||
type TemplateSettings struct {
|
||||
Default string `json:"default" yaml:"Default"`
|
||||
}
|
||||
|
||||
// MapsSettings represents maps settings (for places).
|
||||
type MapsSettings struct {
|
||||
Animate int `json:"animate" yaml:"Animate"`
|
||||
Style string `json:"style" yaml:"Style"`
|
||||
}
|
||||
|
||||
// FeatureSettings represents feature flags, mainly for the Web UI.
|
||||
type FeatureSettings struct {
|
||||
Upload bool `json:"upload" yaml:"Upload"`
|
||||
Download bool `json:"download" yaml:"Download"`
|
||||
Private bool `json:"private" yaml:"Private"`
|
||||
Review bool `json:"review" yaml:"Review"`
|
||||
Files bool `json:"files" yaml:"Files"`
|
||||
Videos bool `json:"videos" yaml:"Videos"`
|
||||
Folders bool `json:"folders" yaml:"Folders"`
|
||||
Albums bool `json:"albums" yaml:"Albums"`
|
||||
Moments bool `json:"moments" yaml:"Moments"`
|
||||
Estimates bool `json:"estimates" yaml:"Estimates"`
|
||||
People bool `json:"people" yaml:"People"`
|
||||
Labels bool `json:"labels" yaml:"Labels"`
|
||||
Places bool `json:"places" yaml:"Places"`
|
||||
Edit bool `json:"edit" yaml:"Edit"`
|
||||
Archive bool `json:"archive" yaml:"Archive"`
|
||||
Delete bool `json:"delete" yaml:"Delete"`
|
||||
Share bool `json:"share" yaml:"Share"`
|
||||
Library bool `json:"library" yaml:"Library"`
|
||||
Import bool `json:"import" yaml:"Import"`
|
||||
Logs bool `json:"logs" yaml:"Logs"`
|
||||
}
|
||||
|
||||
// ImportSettings represents import settings.
|
||||
type ImportSettings struct {
|
||||
Path string `json:"path" yaml:"Path"`
|
||||
Move bool `json:"move" yaml:"Move"`
|
||||
}
|
||||
|
||||
// IndexSettings represents indexing settings.
|
||||
type IndexSettings struct {
|
||||
Path string `json:"path" yaml:"Path"`
|
||||
Convert bool `json:"convert" yaml:"Convert"`
|
||||
Rescan bool `json:"rescan" yaml:"Rescan"`
|
||||
}
|
||||
|
||||
// StackSettings represents settings for files that belong to the same photo.
|
||||
type StackSettings struct {
|
||||
UUID bool `json:"uuid" yaml:"UUID"`
|
||||
Meta bool `json:"meta" yaml:"Meta"`
|
||||
Name bool `json:"name" yaml:"Name"`
|
||||
}
|
||||
|
||||
// ShareSettings represents content sharing settings.
|
||||
type ShareSettings struct {
|
||||
Title string `json:"title" yaml:"Title"`
|
||||
}
|
||||
|
||||
// DownloadSettings represents content download settings.
|
||||
type DownloadSettings struct {
|
||||
Name entity.DownloadName `json:"name" yaml:"Name"`
|
||||
Raw bool `json:"raw" yaml:"Raw"`
|
||||
}
|
||||
|
||||
// Settings represents user settings for Web UI, indexing, and import.
|
||||
type Settings struct {
|
||||
UI UISettings `json:"ui" yaml:"UI"`
|
||||
|
@ -159,9 +80,7 @@ func NewSettings(c *Config) *Settings {
|
|||
Share: ShareSettings{
|
||||
Title: "",
|
||||
},
|
||||
Download: DownloadSettings{
|
||||
Name: entity.DownloadNameDefault,
|
||||
},
|
||||
Download: NewDownloadSettings(),
|
||||
Templates: TemplateSettings{
|
||||
Default: "index.tmpl",
|
||||
},
|
||||
|
@ -191,7 +110,7 @@ func (s Settings) StackMeta() bool {
|
|||
// Load user settings from file.
|
||||
func (s *Settings) Load(fileName string) error {
|
||||
if !fs.FileExists(fileName) {
|
||||
return fmt.Errorf("settings file not found: %s", sanitize.Log(fileName))
|
||||
return fmt.Errorf("settings file not found: %s", clean.Log(fileName))
|
||||
}
|
||||
|
||||
yamlConfig, err := os.ReadFile(fileName)
|
||||
|
|
23
internal/config/settings_download.go
Normal file
23
internal/config/settings_download.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package config
|
||||
|
||||
import "github.com/photoprism/photoprism/internal/entity"
|
||||
|
||||
// DownloadSettings represents content download settings.
|
||||
type DownloadSettings struct {
|
||||
Name entity.DownloadName `json:"name" yaml:"Name"`
|
||||
Disabled bool `json:"disabled" yaml:"Disabled"`
|
||||
Originals bool `json:"originals" yaml:"Originals"`
|
||||
MediaRaw bool `json:"mediaRaw" yaml:"MediaRaw"`
|
||||
MediaSidecar bool `json:"mediaSidecar" yaml:"MediaSidecar"`
|
||||
}
|
||||
|
||||
// NewDownloadSettings creates download settings with defaults.
|
||||
func NewDownloadSettings() DownloadSettings {
|
||||
return DownloadSettings{
|
||||
Name: entity.DownloadNameDefault,
|
||||
Disabled: false,
|
||||
Originals: true,
|
||||
MediaRaw: false,
|
||||
MediaSidecar: false,
|
||||
}
|
||||
}
|
25
internal/config/settings_feature.go
Normal file
25
internal/config/settings_feature.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package config
|
||||
|
||||
// FeatureSettings represents feature flags, mainly for the Web UI.
|
||||
type FeatureSettings struct {
|
||||
Upload bool `json:"upload" yaml:"Upload"`
|
||||
Download bool `json:"download" yaml:"Download"`
|
||||
Private bool `json:"private" yaml:"Private"`
|
||||
Review bool `json:"review" yaml:"Review"`
|
||||
Files bool `json:"files" yaml:"Files"`
|
||||
Videos bool `json:"videos" yaml:"Videos"`
|
||||
Folders bool `json:"folders" yaml:"Folders"`
|
||||
Albums bool `json:"albums" yaml:"Albums"`
|
||||
Moments bool `json:"moments" yaml:"Moments"`
|
||||
Estimates bool `json:"estimates" yaml:"Estimates"`
|
||||
People bool `json:"people" yaml:"People"`
|
||||
Labels bool `json:"labels" yaml:"Labels"`
|
||||
Places bool `json:"places" yaml:"Places"`
|
||||
Edit bool `json:"edit" yaml:"Edit"`
|
||||
Archive bool `json:"archive" yaml:"Archive"`
|
||||
Delete bool `json:"delete" yaml:"Delete"`
|
||||
Share bool `json:"share" yaml:"Share"`
|
||||
Library bool `json:"library" yaml:"Library"`
|
||||
Import bool `json:"import" yaml:"Import"`
|
||||
Logs bool `json:"logs" yaml:"Logs"`
|
||||
}
|
7
internal/config/settings_import.go
Normal file
7
internal/config/settings_import.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package config
|
||||
|
||||
// ImportSettings represents import settings.
|
||||
type ImportSettings struct {
|
||||
Path string `json:"path" yaml:"Path"`
|
||||
Move bool `json:"move" yaml:"Move"`
|
||||
}
|
8
internal/config/settings_index.go
Normal file
8
internal/config/settings_index.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package config
|
||||
|
||||
// IndexSettings represents indexing settings.
|
||||
type IndexSettings struct {
|
||||
Path string `json:"path" yaml:"Path"`
|
||||
Convert bool `json:"convert" yaml:"Convert"`
|
||||
Rescan bool `json:"rescan" yaml:"Rescan"`
|
||||
}
|
7
internal/config/settings_maps.go
Normal file
7
internal/config/settings_maps.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package config
|
||||
|
||||
// MapsSettings represents maps settings (for places).
|
||||
type MapsSettings struct {
|
||||
Animate int `json:"animate" yaml:"Animate"`
|
||||
Style string `json:"style" yaml:"Style"`
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue