refactor: break up admin
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/602/head
parent
5152348586
commit
7861ac6549
|
@ -170,7 +170,7 @@ class OtherController extends GenericApiController
|
||||||
// Check go-vod binary
|
// Check go-vod binary
|
||||||
$extGoVod = Util::getSystemConfig('memories.vod.external');
|
$extGoVod = Util::getSystemConfig('memories.vod.external');
|
||||||
$status['govod'] = $this->getExecutableStatus(
|
$status['govod'] = $this->getExecutableStatus(
|
||||||
BinExt::getGoVodBin(),
|
Util::getSystemConfig('memories.vod.path'),
|
||||||
fn ($p) => BinExt::testStartGoVod(),
|
fn ($p) => BinExt::testStartGoVod(),
|
||||||
!$extGoVod,
|
!$extGoVod,
|
||||||
!$extGoVod,
|
!$extGoVod,
|
||||||
|
|
963
src/Admin.vue
963
src/Admin.vue
|
@ -1,963 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="outer" v-if="loaded">
|
|
||||||
<NcLoadingIcon class="loading-icon" v-show="loading" />
|
|
||||||
|
|
||||||
<!----------------------------- General Settings ----------------------------->
|
|
||||||
<h2>{{ t("memories", "EXIF Extraction") }}</h2>
|
|
||||||
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="binaryStatusType(status.exiftool)">
|
|
||||||
{{ binaryStatus("exiftool", status.exiftool) }}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:label="t('memories', 'Path to packaged exiftool binary')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="exiftoolPath"
|
|
||||||
@change="update('exiftoolPath', $event.target.value)"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="binaryStatusType(status.perl, false)">
|
|
||||||
{{ binaryStatus("perl", status.perl) }}
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"You need perl only if the packaged exiftool binary does not work for some reason."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="exiftoolPerl"
|
|
||||||
@update:checked="update('exiftoolPerl')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
t("memories", "Use system perl (only if exiftool binary does not work)")
|
|
||||||
}}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<!----------------------------- Index Settings ----------------------------->
|
|
||||||
<h2>{{ t("memories", "Media Indexing") }}</h2>
|
|
||||||
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="status.indexed_count > 0 ? 'success' : 'warning'">
|
|
||||||
{{
|
|
||||||
t("memories", "{n} media files have been indexed", {
|
|
||||||
n: status.indexed_count,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard :type="status.last_index_job_status_type">
|
|
||||||
{{
|
|
||||||
t("memories", "Automatic Indexing status: {status}", {
|
|
||||||
status: status.last_index_job_status,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard
|
|
||||||
v-if="status.last_index_job_start"
|
|
||||||
:type="status.last_index_job_duration ? 'success' : 'warning'"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
t("memories", "Last index job was run {t} seconds ago.", {
|
|
||||||
t: status.last_index_job_start,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
status.last_index_job_duration
|
|
||||||
? t("memories", "It took {t} seconds to complete.", {
|
|
||||||
t: status.last_index_job_duration,
|
|
||||||
})
|
|
||||||
: t("memories", "It is still running or was interrupted.")
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard type="error" v-if="status.bad_encryption">
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Only server-side encryption (OC_DEFAULT_MODULE) is supported, but another encryption module is enabled."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"The EXIF indexes are built and checked in a periodic background task. Be careful when selecting anything other than automatic indexing. For example, setting the indexing to only timeline folders may cause delays before media becomes available to users, since the user configures the timeline only after logging in."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
'Folders with a ".nomedia" file are always excluded from indexing.'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="indexingMode"
|
|
||||||
value="1"
|
|
||||||
name="idxm_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('indexingMode')"
|
|
||||||
>{{ t("memories", "Index all media automatically (recommended)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="indexingMode"
|
|
||||||
value="2"
|
|
||||||
name="idxm_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('indexingMode')"
|
|
||||||
>{{
|
|
||||||
t("memories", "Index per-user timeline folders (not recommended)")
|
|
||||||
}}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="indexingMode"
|
|
||||||
value="3"
|
|
||||||
name="idxm_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('indexingMode')"
|
|
||||||
>{{ t("memories", "Index a fixed relative path") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="indexingMode"
|
|
||||||
value="0"
|
|
||||||
name="idxm_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('indexingMode')"
|
|
||||||
>{{ t("memories", "Disable background indexing") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:label="t('memories', 'Indexing path (relative, all users)')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="indexingPath"
|
|
||||||
@change="update('indexingPath', $event.target.value)"
|
|
||||||
v-if="indexingMode === '3'"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{
|
|
||||||
t("memories", "For advanced usage, perform a run of indexing by running:")
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
<code>occ memories:index</code>
|
|
||||||
<br />
|
|
||||||
{{ t("memories", "Run index in parallel with 4 threads:") }}
|
|
||||||
<br />
|
|
||||||
<code>bash -c 'for i in {1..4}; do (occ memories:index &); done'</code>
|
|
||||||
<br />
|
|
||||||
{{ t("memories", "Force re-indexing of all files:") }}
|
|
||||||
<br />
|
|
||||||
<code>occ memories:index --force</code>
|
|
||||||
<br />
|
|
||||||
{{ t("memories", "You can limit indexing by user and/or folder:") }}
|
|
||||||
<br />
|
|
||||||
<code>occ memories:index --user=admin --folder=/Photos/</code>
|
|
||||||
<br />
|
|
||||||
{{ t("memories", "Clear all existing index tables:") }}
|
|
||||||
<br />
|
|
||||||
<code>occ memories:index --clear</code>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"The following MIME types are configured for preview generation correctly. More documentation:"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
href="https://github.com/pulsejet/memories/wiki/File-Type-Support"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ t("memories", "External Link") }}
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
<code v-if="status"
|
|
||||||
><template v-for="mime in status.mimes"
|
|
||||||
>{{ mime }}<br :key="mime" /></template
|
|
||||||
></code>
|
|
||||||
|
|
||||||
<!----------------------------- Places ----------------------------->
|
|
||||||
<h2>{{ t("memories", "Reverse Geocoding") }}</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="gisStatusType">
|
|
||||||
{{ gisStatus }}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard
|
|
||||||
v-if="typeof status.gis_count === 'number'"
|
|
||||||
:type="status.gis_count > 500000 ? 'success' : 'warning'"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
status.gis_count > 0
|
|
||||||
? t("memories", "Database is populated with {n} geometries.", {
|
|
||||||
n: status.gis_count,
|
|
||||||
})
|
|
||||||
: t("memories", "Geometry table has not been created.")
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
status.gis_count > 0 && status.gis_count <= 500000
|
|
||||||
? t("memories", "Looks like the planet data is incomplete.")
|
|
||||||
: ""
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard
|
|
||||||
v-if="typeof gisType !== 'number' || gisType < 0"
|
|
||||||
type="warning"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Reverse geocoding has not been configured ({status}).",
|
|
||||||
{ status: gisType }
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Memories supports offline reverse geocoding using the OpenStreetMaps data on MySQL and Postgres."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"You need to download the planet data into your database. This is highly recommended and has low overhead."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"If the button below does not work for importing the planet data, use the following command:"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
<code>occ memories:places-setup</code>
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Note: the geometry data is stored in the memories_planet_geometry table, with no prefix."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form
|
|
||||||
:action="placesSetupUrl"
|
|
||||||
method="post"
|
|
||||||
@submit="placesSetup"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<input name="requesttoken" type="hidden" :value="requestToken" />
|
|
||||||
<input name="actiontoken" type="hidden" :value="actionToken" />
|
|
||||||
<NcButton nativeType="submit" type="warning">
|
|
||||||
{{ t("memories", "Download planet database") }}
|
|
||||||
</NcButton>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!----------------------------- Video Streaming ----------------------------->
|
|
||||||
<h2>{{ t("memories", "Video Streaming") }}</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Live transcoding provides for adaptive streaming of videos using HLS."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Note that this may be very CPU intensive without hardware acceleration, and transcoding will not be used for external storage."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:checked.sync="enableTranscoding"
|
|
||||||
@update:checked="update('enableTranscoding')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable Transcoding") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="binaryStatusType(status.ffmpeg)">
|
|
||||||
{{ binaryStatus("ffmpeg", status.ffmpeg) }}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard :type="binaryStatusType(status.ffprobe)">
|
|
||||||
{{ binaryStatus("ffprobe", status.ffprobe) }}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:label="t('memories', 'ffmpeg path')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="ffmpegPath"
|
|
||||||
@change="update('ffmpegPath', $event.target.value)"
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:label="t('memories', 'ffprobe path')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="ffprobePath"
|
|
||||||
@change="update('ffprobePath', $event.target.value)"
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
{{ t("memories", "Global default video quality (user may override)") }}
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="videoDefaultQuality"
|
|
||||||
value="0"
|
|
||||||
name="vdq_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('videoDefaultQuality')"
|
|
||||||
>{{ t("memories", "Auto (adaptive transcode)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="videoDefaultQuality"
|
|
||||||
value="-1"
|
|
||||||
name="vdq_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('videoDefaultQuality')"
|
|
||||||
>{{ t("memories", "Original (transcode with max quality)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="videoDefaultQuality"
|
|
||||||
value="-2"
|
|
||||||
name="vdq_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('videoDefaultQuality')"
|
|
||||||
>{{ t("memories", "Direct (original video file without transcode)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>{{ t("memories", "Transcoder configuration") }}</h3>
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Memories uses the go-vod transcoder. You can run go-vod exernally (e.g. in a separate Docker container for hardware acceleration) or use the built-in transcoder. To use an external transcoder, enable the following option and follow the instructions in the documentation:"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/pulsejet/memories/wiki/HW-Transcoding"
|
|
||||||
>
|
|
||||||
{{ t("memories", "External Link") }}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<template v-if="status">
|
|
||||||
<NcNoteCard :type="binaryStatusType(status.govod)">
|
|
||||||
{{ binaryStatus("go-vod", status.govod) }}
|
|
||||||
</NcNoteCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="enableExternalTranscoder"
|
|
||||||
@update:checked="update('enableExternalTranscoder')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable external transcoder (go-vod)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:label="t('memories', 'Binary path (local only)')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="goVodPath"
|
|
||||||
@change="update('goVodPath', $event.target.value)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:label="t('memories', 'Bind address (local only)')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="goVodBind"
|
|
||||||
@change="update('goVodBind', $event.target.value)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NcTextField
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:label="t('memories', 'Connection address (same as bind if local)')"
|
|
||||||
:label-visible="true"
|
|
||||||
:value="goVodConnect"
|
|
||||||
@change="update('goVodConnect', $event.target.value)"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>{{ t("memories", "Hardware Acceleration") }}</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"You must first make sure the correct drivers are installed before configuring acceleration."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Make sure you test hardware acceleration with various options after enabling."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Do not enable multiple types of hardware acceleration simultaneously."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Intel processors supporting QuickSync Video (QSV) as well as some AMD GPUs can be used for transcoding using VA-API acceleration."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"For more details on driver installation, check the documentation:"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/pulsejet/memories/wiki/HW-Transcoding#va-api"
|
|
||||||
>
|
|
||||||
{{ t("memories", "External Link") }}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<NcNoteCard :type="vaapiStatusType" v-if="status">
|
|
||||||
{{ vaapiStatusText }}
|
|
||||||
</NcNoteCard>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="enableVaapi"
|
|
||||||
@update:checked="update('enableVaapi')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable acceleration with VA-API") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding || !enableVaapi"
|
|
||||||
:checked.sync="enableVaapiLowPower"
|
|
||||||
@update:checked="update('enableVaapiLowPower')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable low-power mode (QSV)") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"NVIDIA GPUs can be used for transcoding using the NVENC encoder with the proper drivers."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<br />
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"Depending on the versions of the installed SDK and ffmpeg, you need to specify the scaler to use"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
|
|
||||||
<NcNoteCard type="warning">
|
|
||||||
{{
|
|
||||||
t(
|
|
||||||
"memories",
|
|
||||||
"No automated tests are available for NVIDIA acceleration."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding"
|
|
||||||
:checked.sync="enableNvenc"
|
|
||||||
@update:checked="update('enableNvenc')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable acceleration with NVENC") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding || !enableNvenc"
|
|
||||||
:checked.sync="enableNvencTemporalAQ"
|
|
||||||
@update:checked="update('enableNvencTemporalAQ')"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Enable NVENC Temporal AQ") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding || !enableNvenc"
|
|
||||||
:checked.sync="nvencScaler"
|
|
||||||
value="npp"
|
|
||||||
name="nvence_scaler_radio"
|
|
||||||
type="radio"
|
|
||||||
@update:checked="update('nvencScaler')"
|
|
||||||
class="m-radio"
|
|
||||||
>{{ t("memories", "NPP scaler") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
<NcCheckboxRadioSwitch
|
|
||||||
:disabled="!enableTranscoding || !enableNvenc"
|
|
||||||
:checked.sync="nvencScaler"
|
|
||||||
value="cuda"
|
|
||||||
name="nvence_scaler_radio"
|
|
||||||
type="radio"
|
|
||||||
class="m-radio"
|
|
||||||
@update:checked="update('nvencScaler')"
|
|
||||||
>{{ t("memories", "CUDA scaler") }}
|
|
||||||
</NcCheckboxRadioSwitch>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>{{ t("memories", "Performance") }}</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<NcNoteCard :type="isHttps ? 'success' : 'warning'">
|
|
||||||
{{
|
|
||||||
isHttps
|
|
||||||
? t("memories", "HTTPS is enabled")
|
|
||||||
: t(
|
|
||||||
"memories",
|
|
||||||
"You are accessing this page over an insecure context. Several browser APIs are not available, which will make Memories very slow. Enable HTTPS on your server to improve performance."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
<NcNoteCard :type="httpVerOk ? 'success' : 'warning'">
|
|
||||||
{{
|
|
||||||
httpVerOk
|
|
||||||
? t("memories", "HTTP/2 or HTTP/3 is enabled")
|
|
||||||
: t(
|
|
||||||
"memories",
|
|
||||||
"HTTP/2 or HTTP/3 is strongly recommended ({httpVer} detected)",
|
|
||||||
{
|
|
||||||
httpVer,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NcNoteCard>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from "vue";
|
|
||||||
|
|
||||||
import axios from "@nextcloud/axios";
|
|
||||||
import { API } from "./services/API";
|
|
||||||
import { showError } from "@nextcloud/dialogs";
|
|
||||||
|
|
||||||
const NcCheckboxRadioSwitch = () =>
|
|
||||||
import("@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch");
|
|
||||||
const NcNoteCard = () => import("@nextcloud/vue/dist/Components/NcNoteCard");
|
|
||||||
const NcTextField = () => import("@nextcloud/vue/dist/Components/NcTextField");
|
|
||||||
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon";
|
|
||||||
import NcButton from "@nextcloud/vue/dist/Components/NcButton";
|
|
||||||
|
|
||||||
/** Map from UI to backend settings */
|
|
||||||
const settings = {
|
|
||||||
exiftoolPath: "memories.exiftool",
|
|
||||||
exiftoolPerl: "memories.exiftool_no_local",
|
|
||||||
indexingMode: "memories.index.mode",
|
|
||||||
indexingPath: "memories.index.path",
|
|
||||||
|
|
||||||
gisType: "memories.gis_type",
|
|
||||||
|
|
||||||
enableTranscoding: "memories.vod.disable",
|
|
||||||
ffmpegPath: "memories.vod.ffmpeg",
|
|
||||||
ffprobePath: "memories.vod.ffprobe",
|
|
||||||
goVodPath: "memories.vod.path",
|
|
||||||
goVodBind: "memories.vod.bind",
|
|
||||||
goVodConnect: "memories.vod.connect",
|
|
||||||
enableExternalTranscoder: "memories.vod.external",
|
|
||||||
videoDefaultQuality: "memories.video_default_quality",
|
|
||||||
|
|
||||||
enableVaapi: "memories.vod.vaapi",
|
|
||||||
enableVaapiLowPower: "memories.vod.vaapi.low_power",
|
|
||||||
|
|
||||||
enableNvenc: "memories.vod.nvenc",
|
|
||||||
enableNvencTemporalAQ: "memories.vod.nvenc.temporal_aq",
|
|
||||||
nvencScaler: "memories.vod.nvenc.scale",
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Invert setting before saving */
|
|
||||||
const invertedBooleans = ["enableTranscoding"];
|
|
||||||
|
|
||||||
type BinaryStatus = "ok" | "not_found" | "not_executable" | "test_ok" | string;
|
|
||||||
|
|
||||||
type IStatus = {
|
|
||||||
last_index_job_start: number;
|
|
||||||
last_index_job_duration: number;
|
|
||||||
last_index_job_status: string;
|
|
||||||
last_index_job_status_type: string;
|
|
||||||
|
|
||||||
bad_encryption: boolean;
|
|
||||||
indexed_count: number;
|
|
||||||
mimes: string[];
|
|
||||||
gis_type: number;
|
|
||||||
gis_count?: number;
|
|
||||||
exiftool: BinaryStatus;
|
|
||||||
perl: BinaryStatus;
|
|
||||||
ffmpeg: BinaryStatus;
|
|
||||||
ffprobe: BinaryStatus;
|
|
||||||
govod: BinaryStatus;
|
|
||||||
vaapi_dev: "ok" | "not_found" | "not_readable";
|
|
||||||
|
|
||||||
action_token: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "Admin",
|
|
||||||
components: {
|
|
||||||
NcCheckboxRadioSwitch,
|
|
||||||
NcNoteCard,
|
|
||||||
NcTextField,
|
|
||||||
NcLoadingIcon,
|
|
||||||
NcButton,
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
loaded: false,
|
|
||||||
|
|
||||||
exiftoolPath: "",
|
|
||||||
exiftoolPerl: false,
|
|
||||||
indexingMode: "0",
|
|
||||||
indexingPath: "",
|
|
||||||
|
|
||||||
gisType: 0,
|
|
||||||
|
|
||||||
enableTranscoding: false,
|
|
||||||
ffmpegPath: "",
|
|
||||||
ffprobePath: "",
|
|
||||||
goVodPath: "",
|
|
||||||
goVodBind: "",
|
|
||||||
goVodConnect: "",
|
|
||||||
enableExternalTranscoder: false,
|
|
||||||
videoDefaultQuality: "",
|
|
||||||
|
|
||||||
enableVaapi: false,
|
|
||||||
enableVaapiLowPower: false,
|
|
||||||
|
|
||||||
enableNvenc: false,
|
|
||||||
enableNvencTemporalAQ: false,
|
|
||||||
nvencScaler: "",
|
|
||||||
|
|
||||||
loading: 0,
|
|
||||||
|
|
||||||
status: null as IStatus | null,
|
|
||||||
}),
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.reload();
|
|
||||||
this.refreshStatus();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async reload() {
|
|
||||||
const res = await axios.get(API.SYSTEM_CONFIG(null));
|
|
||||||
for (const key in settings) {
|
|
||||||
if (!res.data.hasOwnProperty(settings[key])) {
|
|
||||||
console.error(
|
|
||||||
`Setting ${settings[key]} not found in backend response`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this[key] = res.data[settings[key]];
|
|
||||||
|
|
||||||
if (invertedBooleans.includes(key)) {
|
|
||||||
this[key] = !this[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loaded = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
async refreshStatus() {
|
|
||||||
try {
|
|
||||||
this.loading++;
|
|
||||||
const res = await axios.get(API.SYSTEM_STATUS());
|
|
||||||
this.status = res.data;
|
|
||||||
} finally {
|
|
||||||
this.loading--;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async update(key: string, value: any = null) {
|
|
||||||
value = value ?? this[key];
|
|
||||||
const setting = settings[key];
|
|
||||||
|
|
||||||
this[key] = value;
|
|
||||||
|
|
||||||
// Inversion
|
|
||||||
if (invertedBooleans.includes(key)) {
|
|
||||||
value = !value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading++;
|
|
||||||
axios
|
|
||||||
.put(API.SYSTEM_CONFIG(setting), {
|
|
||||||
value: value,
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
showError(this.t("memories", "Failed to update setting"));
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading--;
|
|
||||||
|
|
||||||
if (this["refreshTimer"]) {
|
|
||||||
clearTimeout(this["refreshTimer"]);
|
|
||||||
}
|
|
||||||
this["refreshTimer"] = setTimeout(() => {
|
|
||||||
this.refreshStatus();
|
|
||||||
delete this["refreshTimer"];
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
placesSetup(event: Event) {
|
|
||||||
const warnSetup = this.t(
|
|
||||||
"memories",
|
|
||||||
"Looks like the database is already setup. Are you sure you want to redownload planet data?"
|
|
||||||
);
|
|
||||||
const warnLong = this.t(
|
|
||||||
"memories",
|
|
||||||
"You are about to download the planet database. This may take a while."
|
|
||||||
);
|
|
||||||
const warnReindex = this.t(
|
|
||||||
"memories",
|
|
||||||
"This may also cause all photos to be re-indexed!"
|
|
||||||
);
|
|
||||||
const msg =
|
|
||||||
(this.status?.gis_count ? warnSetup : warnLong) + " " + warnReindex;
|
|
||||||
if (!confirm(msg)) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
binaryStatus(name: string, status: BinaryStatus): string {
|
|
||||||
const noescape = {
|
|
||||||
escape: false,
|
|
||||||
sanitize: false,
|
|
||||||
};
|
|
||||||
if (status === "ok") {
|
|
||||||
return this.t("memories", "{name} binary exists and is executable.", {
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
} else if (status === "not_found") {
|
|
||||||
return this.t("memories", "{name} binary not found.", { name });
|
|
||||||
} else if (status === "not_executable") {
|
|
||||||
return this.t("memories", "{name} binary is not executable.", {
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
} else if (status.startsWith("test_fail")) {
|
|
||||||
return this.t(
|
|
||||||
"memories",
|
|
||||||
"{name} failed test: {info}.",
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
info: status.substring(10),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
noescape
|
|
||||||
);
|
|
||||||
} else if (status.startsWith("test_ok")) {
|
|
||||||
return this.t(
|
|
||||||
"memories",
|
|
||||||
"{name} binary exists and is usable ({info}).",
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
info: status.substring(8),
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
noescape
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return this.t("memories", "{name} binary status: {status}.", {
|
|
||||||
name,
|
|
||||||
status,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
binaryStatusType(status: BinaryStatus, critical = true): string {
|
|
||||||
if (status === "ok" || status.startsWith("test_ok")) {
|
|
||||||
return "success";
|
|
||||||
} else if (
|
|
||||||
status === "not_found" ||
|
|
||||||
status === "not_executable" ||
|
|
||||||
status.startsWith("test_fail")
|
|
||||||
) {
|
|
||||||
return critical ? "error" : "warning";
|
|
||||||
} else {
|
|
||||||
return "warning";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
requestToken() {
|
|
||||||
return (<any>axios.defaults.headers).requesttoken;
|
|
||||||
},
|
|
||||||
|
|
||||||
actionToken() {
|
|
||||||
return this.status?.action_token || "";
|
|
||||||
},
|
|
||||||
|
|
||||||
gisStatus() {
|
|
||||||
if (!this.status) return "";
|
|
||||||
|
|
||||||
if (typeof this.status.gis_type !== "number") {
|
|
||||||
return this.status.gis_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.status.gis_type <= 0) {
|
|
||||||
return this.t(
|
|
||||||
"memories",
|
|
||||||
"Geometry support was not detected in your database"
|
|
||||||
);
|
|
||||||
} else if (this.status.gis_type === 1) {
|
|
||||||
return this.t("memories", "MySQL-like geometry support was detected ");
|
|
||||||
} else if (this.status.gis_type === 2) {
|
|
||||||
return this.t(
|
|
||||||
"memories",
|
|
||||||
"Postgres native geometry support was detected"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
gisStatusType() {
|
|
||||||
return typeof this.status?.gis_type !== "number" ||
|
|
||||||
this.status.gis_type <= 0
|
|
||||||
? "error"
|
|
||||||
: "success";
|
|
||||||
},
|
|
||||||
|
|
||||||
placesSetupUrl() {
|
|
||||||
return API.OCC_PLACES_SETUP();
|
|
||||||
},
|
|
||||||
|
|
||||||
vaapiStatusText(): string {
|
|
||||||
if (!this.status) return "";
|
|
||||||
|
|
||||||
const dev = "/dev/dri/renderD128";
|
|
||||||
if (this.status.vaapi_dev === "ok") {
|
|
||||||
return this.t("memories", "VA-API device ({dev}) is readable", { dev });
|
|
||||||
} else if (this.status.vaapi_dev === "not_found") {
|
|
||||||
return this.t("memories", "VA-API device ({dev}) not found", { dev });
|
|
||||||
} else if (this.status.vaapi_dev === "not_readable") {
|
|
||||||
return this.t(
|
|
||||||
"memories",
|
|
||||||
"VA-API device ({dev}) has incorrect permissions",
|
|
||||||
{ dev }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return this.t("memories", "VA-API device status: {status}", {
|
|
||||||
status: this.status.vaapi_dev,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
vaapiStatusType(): string {
|
|
||||||
return this.status?.vaapi_dev === "ok" ? "success" : "error";
|
|
||||||
},
|
|
||||||
|
|
||||||
isHttps(): boolean {
|
|
||||||
return window.location.protocol === "https:";
|
|
||||||
},
|
|
||||||
|
|
||||||
httpVer(): string {
|
|
||||||
const entry = window.performance?.getEntriesByType?.(
|
|
||||||
"navigation"
|
|
||||||
)?.[0] as any;
|
|
||||||
return entry?.nextHopProtocol || this.t("memories", "Unknown");
|
|
||||||
},
|
|
||||||
|
|
||||||
httpVerOk(): boolean {
|
|
||||||
return this.httpVer === "h2" || this.httpVer === "h3";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.outer {
|
|
||||||
padding: 20px;
|
|
||||||
padding-top: 0px;
|
|
||||||
|
|
||||||
.loading-icon {
|
|
||||||
top: 10px;
|
|
||||||
right: 20px;
|
|
||||||
position: absolute;
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
|
|
||||||
:deep svg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-radio-switch {
|
|
||||||
margin: 2px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-radio {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--color-primary-element);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding-left: 10px;
|
|
||||||
-webkit-box-decoration-break: clone;
|
|
||||||
box-decoration-break: clone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
<template>
|
||||||
|
<div class="outer" v-if="loaded">
|
||||||
|
<NcLoadingIcon class="loading-icon" v-show="loading" />
|
||||||
|
|
||||||
|
<component
|
||||||
|
v-for="c in components"
|
||||||
|
:key="c.__name"
|
||||||
|
:is="c"
|
||||||
|
:status="status"
|
||||||
|
:config="config"
|
||||||
|
@update="update"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import axios from "@nextcloud/axios";
|
||||||
|
import { showError } from "@nextcloud/dialogs";
|
||||||
|
|
||||||
|
import { API } from "../../services/API";
|
||||||
|
import * as utils from "../../services/Utils";
|
||||||
|
|
||||||
|
import Exif from "./sections/Exif.vue";
|
||||||
|
import Indexing from "./sections/Indexing.vue";
|
||||||
|
import Performance from "./sections/Performance.vue";
|
||||||
|
import Places from "./sections/Places.vue";
|
||||||
|
import Video from "./sections/Video.vue";
|
||||||
|
import VideoTranscoder from "./sections/VideoTranscoder.vue";
|
||||||
|
import VideoAccel from "./sections/VideoAccel.vue";
|
||||||
|
|
||||||
|
import { ISystemConfig, ISystemStatus } from "./AdminTypes";
|
||||||
|
|
||||||
|
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Admin",
|
||||||
|
components: {
|
||||||
|
NcLoadingIcon,
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
loaded: false,
|
||||||
|
loading: 0,
|
||||||
|
|
||||||
|
status: null as ISystemStatus | null,
|
||||||
|
config: null as ISystemConfig | null,
|
||||||
|
|
||||||
|
components: [
|
||||||
|
Exif,
|
||||||
|
Indexing,
|
||||||
|
Performance,
|
||||||
|
Places,
|
||||||
|
Video,
|
||||||
|
VideoTranscoder,
|
||||||
|
VideoAccel,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.refreshSystemConfig();
|
||||||
|
this.refreshStatus();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async refreshSystemConfig() {
|
||||||
|
try {
|
||||||
|
this.loading++;
|
||||||
|
const res = await axios.get<ISystemConfig>(API.SYSTEM_CONFIG(null));
|
||||||
|
this.config = res.data;
|
||||||
|
this.loaded = true;
|
||||||
|
} catch (e) {
|
||||||
|
showError(JSON.stringify(e));
|
||||||
|
} finally {
|
||||||
|
this.loading--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async refreshStatus() {
|
||||||
|
try {
|
||||||
|
this.loading++;
|
||||||
|
const res = await axios.get<ISystemStatus>(API.SYSTEM_STATUS());
|
||||||
|
this.status = res.data;
|
||||||
|
} catch (e) {
|
||||||
|
showError(JSON.stringify(e));
|
||||||
|
} finally {
|
||||||
|
this.loading--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update(key: keyof ISystemConfig, value: any = null) {
|
||||||
|
if (!this.config?.hasOwnProperty(key)) {
|
||||||
|
console.error("Unknown setting", key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get final value
|
||||||
|
value ??= this.config[key];
|
||||||
|
this.config[key as string] = value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading++;
|
||||||
|
await axios.put(API.SYSTEM_CONFIG(key), {
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.setRenewingTimeout(
|
||||||
|
this,
|
||||||
|
"_refreshTimer",
|
||||||
|
this.refreshStatus.bind(this),
|
||||||
|
500
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
showError(this.t("memories", "Failed to update setting"));
|
||||||
|
} finally {
|
||||||
|
this.loading--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.outer {
|
||||||
|
padding: 20px;
|
||||||
|
padding-top: 0px;
|
||||||
|
|
||||||
|
.admin-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
top: 10px;
|
||||||
|
right: 20px;
|
||||||
|
position: absolute;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
:deep svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-radio-switch {
|
||||||
|
margin: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-radio {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep a {
|
||||||
|
color: var(--color-primary-element);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep code {
|
||||||
|
padding-left: 10px;
|
||||||
|
-webkit-box-decoration-break: clone;
|
||||||
|
box-decoration-break: clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { defineComponent, PropType } from "vue";
|
||||||
|
import { ISystemStatus, ISystemConfig, IBinaryStatus } from "./AdminTypes";
|
||||||
|
import axios from "@nextcloud/axios";
|
||||||
|
|
||||||
|
const NcCheckboxRadioSwitch = () =>
|
||||||
|
import("@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch");
|
||||||
|
const NcNoteCard = () => import("@nextcloud/vue/dist/Components/NcNoteCard");
|
||||||
|
const NcTextField = () => import("@nextcloud/vue/dist/Components/NcTextField");
|
||||||
|
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon";
|
||||||
|
import NcButton from "@nextcloud/vue/dist/Components/NcButton";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "AdminMixin",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
NcCheckboxRadioSwitch,
|
||||||
|
NcNoteCard,
|
||||||
|
NcTextField,
|
||||||
|
NcLoadingIcon,
|
||||||
|
NcButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
status: {
|
||||||
|
type: Object as PropType<ISystemStatus>,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object as PropType<ISystemConfig>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
update(key: keyof ISystemConfig, value: any = null) {
|
||||||
|
this.$emit("update", key, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
binaryStatus(name: string, status: IBinaryStatus): string {
|
||||||
|
const noescape = {
|
||||||
|
escape: false,
|
||||||
|
sanitize: false,
|
||||||
|
};
|
||||||
|
if (status === "ok") {
|
||||||
|
return this.t("memories", "{name} binary exists and is executable.", {
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
} else if (status === "not_found") {
|
||||||
|
return this.t("memories", "{name} binary not found.", { name });
|
||||||
|
} else if (status === "not_executable") {
|
||||||
|
return this.t("memories", "{name} binary is not executable.", {
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
} else if (status.startsWith("test_fail")) {
|
||||||
|
return this.t(
|
||||||
|
"memories",
|
||||||
|
"{name} failed test: {info}.",
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
info: status.substring(10),
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
noescape
|
||||||
|
);
|
||||||
|
} else if (status.startsWith("test_ok")) {
|
||||||
|
return this.t(
|
||||||
|
"memories",
|
||||||
|
"{name} binary exists and is usable ({info}).",
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
info: status.substring(8),
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
noescape
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.t("memories", "{name} binary status: {status}.", {
|
||||||
|
name,
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
binaryStatusType(status: IBinaryStatus, critical = true): string {
|
||||||
|
if (status === "ok" || status.startsWith("test_ok")) {
|
||||||
|
return "success";
|
||||||
|
} else if (
|
||||||
|
status === "not_found" ||
|
||||||
|
status === "not_executable" ||
|
||||||
|
status.startsWith("test_fail")
|
||||||
|
) {
|
||||||
|
return critical ? "error" : "warning";
|
||||||
|
} else {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
requestToken() {
|
||||||
|
return (<any>axios.defaults.headers).requesttoken;
|
||||||
|
},
|
||||||
|
|
||||||
|
actionToken() {
|
||||||
|
return this.status?.action_token || "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
enableTranscoding: {
|
||||||
|
get() {
|
||||||
|
return !this.config["memories.vod.disable"];
|
||||||
|
},
|
||||||
|
set(value: boolean) {
|
||||||
|
this.config["memories.vod.disable"] = !value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,53 @@
|
||||||
|
/** System configuration */
|
||||||
|
export type ISystemConfig = {
|
||||||
|
"memories.exiftool": string;
|
||||||
|
"memories.exiftool_no_local": boolean;
|
||||||
|
"memories.index.mode": string;
|
||||||
|
"memories.index.path": string;
|
||||||
|
|
||||||
|
"memories.gis_type": number;
|
||||||
|
|
||||||
|
"memories.vod.disable": boolean;
|
||||||
|
"memories.vod.ffmpeg": string;
|
||||||
|
"memories.vod.ffprobe": string;
|
||||||
|
"memories.vod.path": string;
|
||||||
|
"memories.vod.bind": string;
|
||||||
|
"memories.vod.connect": string;
|
||||||
|
"memories.vod.external": boolean;
|
||||||
|
"memories.video_default_quality": string;
|
||||||
|
|
||||||
|
"memories.vod.vaapi": boolean;
|
||||||
|
"memories.vod.vaapi.low_power": boolean;
|
||||||
|
|
||||||
|
"memories.vod.nvenc": boolean;
|
||||||
|
"memories.vod.nvenc.temporal_aq": boolean;
|
||||||
|
"memories.vod.nvenc.scale": string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IBinaryStatus =
|
||||||
|
| "ok"
|
||||||
|
| "not_found"
|
||||||
|
| "not_executable"
|
||||||
|
| "test_ok"
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export type ISystemStatus = {
|
||||||
|
last_index_job_start: number;
|
||||||
|
last_index_job_duration: number;
|
||||||
|
last_index_job_status: string;
|
||||||
|
last_index_job_status_type: string;
|
||||||
|
|
||||||
|
bad_encryption: boolean;
|
||||||
|
indexed_count: number;
|
||||||
|
mimes: string[];
|
||||||
|
gis_type: number;
|
||||||
|
gis_count?: number;
|
||||||
|
exiftool: IBinaryStatus;
|
||||||
|
perl: IBinaryStatus;
|
||||||
|
ffmpeg: IBinaryStatus;
|
||||||
|
ffprobe: IBinaryStatus;
|
||||||
|
govod: IBinaryStatus;
|
||||||
|
vaapi_dev: "ok" | "not_found" | "not_readable";
|
||||||
|
|
||||||
|
action_token: string;
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h2>{{ t("memories", "EXIF Extraction") }}</h2>
|
||||||
|
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="binaryStatusType(status.exiftool)">
|
||||||
|
{{ binaryStatus("exiftool", status.exiftool) }}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:label="t('memories', 'Path to packaged exiftool binary')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.exiftool']"
|
||||||
|
@change="update('memories.exiftool', $event.target.value)"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="binaryStatusType(status.perl, false)">
|
||||||
|
{{ binaryStatus("perl", status.perl) }}
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"You need perl only if the packaged exiftool binary does not work for some reason."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="config['memories.exiftool_no_local']"
|
||||||
|
@update:checked="update('memories.exiftool_no_local')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
t("memories", "Use system perl (only if exiftool binary does not work)")
|
||||||
|
}}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Exif",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,157 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h2>{{ t("memories", "Media Indexing") }}</h2>
|
||||||
|
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="status.indexed_count > 0 ? 'success' : 'warning'">
|
||||||
|
{{
|
||||||
|
t("memories", "{n} media files have been indexed", {
|
||||||
|
n: status.indexed_count,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard :type="status.last_index_job_status_type">
|
||||||
|
{{
|
||||||
|
t("memories", "Automatic Indexing status: {status}", {
|
||||||
|
status: status.last_index_job_status,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard
|
||||||
|
v-if="status.last_index_job_start"
|
||||||
|
:type="status.last_index_job_duration ? 'success' : 'warning'"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
t("memories", "Last index job was run {t} seconds ago.", {
|
||||||
|
t: status.last_index_job_start,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
status.last_index_job_duration
|
||||||
|
? t("memories", "It took {t} seconds to complete.", {
|
||||||
|
t: status.last_index_job_duration,
|
||||||
|
})
|
||||||
|
: t("memories", "It is still running or was interrupted.")
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard type="error" v-if="status.bad_encryption">
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Only server-side encryption (OC_DEFAULT_MODULE) is supported, but another encryption module is enabled."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"The EXIF indexes are built and checked in a periodic background task. Be careful when selecting anything other than automatic indexing. For example, setting the indexing to only timeline folders may cause delays before media becomes available to users, since the user configures the timeline only after logging in."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
'Folders with a ".nomedia" file are always excluded from indexing.'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="config['memories.index.mode']"
|
||||||
|
value="1"
|
||||||
|
name="idxm_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.index.mode')"
|
||||||
|
>{{ t("memories", "Index all media automatically (recommended)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="config['memories.index.mode']"
|
||||||
|
value="2"
|
||||||
|
name="idxm_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.index.mode')"
|
||||||
|
>{{
|
||||||
|
t("memories", "Index per-user timeline folders (not recommended)")
|
||||||
|
}}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="config['memories.index.mode']"
|
||||||
|
value="3"
|
||||||
|
name="idxm_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.index.mode')"
|
||||||
|
>{{ t("memories", "Index a fixed relative path") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="config['memories.index.mode']"
|
||||||
|
value="0"
|
||||||
|
name="idxm_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.index.mode')"
|
||||||
|
>{{ t("memories", "Disable background indexing") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:label="t('memories', 'Indexing path (relative, all users)')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.index.path']"
|
||||||
|
@change="update('memories.index.path', $event.target.value)"
|
||||||
|
v-if="config['memories.index.mode'] === '3'"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{
|
||||||
|
t("memories", "For advanced usage, perform a run of indexing by running:")
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
<code>occ memories:index</code>
|
||||||
|
<br />
|
||||||
|
{{ t("memories", "Run index in parallel with 4 threads:") }}
|
||||||
|
<br />
|
||||||
|
<code>bash -c 'for i in {1..4}; do (occ memories:index &); done'</code>
|
||||||
|
<br />
|
||||||
|
{{ t("memories", "Force re-indexing of all files:") }}
|
||||||
|
<br />
|
||||||
|
<code>occ memories:index --force</code>
|
||||||
|
<br />
|
||||||
|
{{ t("memories", "You can limit indexing by user and/or folder:") }}
|
||||||
|
<br />
|
||||||
|
<code>occ memories:index --user=admin --folder=/Photos/</code>
|
||||||
|
<br />
|
||||||
|
{{ t("memories", "Clear all existing index tables:") }}
|
||||||
|
<br />
|
||||||
|
<code>occ memories:index --clear</code>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"The following MIME types are configured for preview generation correctly. More documentation:"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
href="https://github.com/pulsejet/memories/wiki/File-Type-Support"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ t("memories", "External Link") }}
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
<code v-if="status"
|
||||||
|
><template v-for="mime in status.mimes"
|
||||||
|
>{{ mime }}<br :key="mime" /></template
|
||||||
|
></code>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Indexing",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h2>{{ t("memories", "Performance") }}</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<NcNoteCard :type="isHttps ? 'success' : 'warning'">
|
||||||
|
{{
|
||||||
|
isHttps
|
||||||
|
? t("memories", "HTTPS is enabled")
|
||||||
|
: t(
|
||||||
|
"memories",
|
||||||
|
"You are accessing this page over an insecure context. Several browser APIs are not available, which will make Memories very slow. Enable HTTPS on your server to improve performance."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard :type="httpVerOk ? 'success' : 'warning'">
|
||||||
|
{{
|
||||||
|
httpVerOk
|
||||||
|
? t("memories", "HTTP/2 or HTTP/3 is enabled")
|
||||||
|
: t(
|
||||||
|
"memories",
|
||||||
|
"HTTP/2 or HTTP/3 is strongly recommended ({httpVer} detected)",
|
||||||
|
{
|
||||||
|
httpVer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Performance",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isHttps(): boolean {
|
||||||
|
return window.location.protocol === "https:";
|
||||||
|
},
|
||||||
|
|
||||||
|
httpVer(): string {
|
||||||
|
const entry = window.performance?.getEntriesByType?.(
|
||||||
|
"navigation"
|
||||||
|
)?.[0] as any;
|
||||||
|
return entry?.nextHopProtocol || this.t("memories", "Unknown");
|
||||||
|
},
|
||||||
|
|
||||||
|
httpVerOk(): boolean {
|
||||||
|
return this.httpVer === "h2" || this.httpVer === "h3";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,159 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h2>{{ t("memories", "Reverse Geocoding") }}</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="gisStatusType">
|
||||||
|
{{ gisStatus }}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard
|
||||||
|
v-if="typeof status.gis_count === 'number'"
|
||||||
|
:type="status.gis_count > 500000 ? 'success' : 'warning'"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
status.gis_count > 0
|
||||||
|
? t("memories", "Database is populated with {n} geometries.", {
|
||||||
|
n: status.gis_count,
|
||||||
|
})
|
||||||
|
: t("memories", "Geometry table has not been created.")
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
status.gis_count > 0 && status.gis_count <= 500000
|
||||||
|
? t("memories", "Looks like the planet data is incomplete.")
|
||||||
|
: ""
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard
|
||||||
|
v-if="
|
||||||
|
typeof config['memories.gis_type'] !== 'number' ||
|
||||||
|
config['memories.gis_type'] < 0
|
||||||
|
"
|
||||||
|
type="warning"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Reverse geocoding has not been configured ({status}).",
|
||||||
|
{ status: config["memories.gis_type"] }
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Memories supports offline reverse geocoding using the OpenStreetMaps data on MySQL and Postgres."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"You need to download the planet data into your database. This is highly recommended and has low overhead."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"If the button below does not work for importing the planet data, use the following command:"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
<code>occ memories:places-setup</code>
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Note: the geometry data is stored in the memories_planet_geometry table, with no prefix."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form
|
||||||
|
:action="placesSetupUrl"
|
||||||
|
method="post"
|
||||||
|
@submit="placesSetup"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<input name="requesttoken" type="hidden" :value="requestToken" />
|
||||||
|
<input name="actiontoken" type="hidden" :value="actionToken" />
|
||||||
|
<NcButton nativeType="submit" type="warning">
|
||||||
|
{{ t("memories", "Download planet database") }}
|
||||||
|
</NcButton>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { API } from "../../../services/API";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Places",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
gisStatus() {
|
||||||
|
if (!this.status) return "";
|
||||||
|
|
||||||
|
if (typeof this.status.gis_type !== "number") {
|
||||||
|
return this.status.gis_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.status.gis_type <= 0) {
|
||||||
|
return this.t(
|
||||||
|
"memories",
|
||||||
|
"Geometry support was not detected in your database"
|
||||||
|
);
|
||||||
|
} else if (this.status.gis_type === 1) {
|
||||||
|
return this.t("memories", "MySQL-like geometry support was detected ");
|
||||||
|
} else if (this.status.gis_type === 2) {
|
||||||
|
return this.t(
|
||||||
|
"memories",
|
||||||
|
"Postgres native geometry support was detected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
gisStatusType() {
|
||||||
|
return typeof this.status?.gis_type !== "number" ||
|
||||||
|
this.status.gis_type <= 0
|
||||||
|
? "error"
|
||||||
|
: "success";
|
||||||
|
},
|
||||||
|
|
||||||
|
placesSetupUrl() {
|
||||||
|
return API.OCC_PLACES_SETUP();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
placesSetup(event: Event) {
|
||||||
|
const warnSetup = this.t(
|
||||||
|
"memories",
|
||||||
|
"Looks like the database is already setup. Are you sure you want to redownload planet data?"
|
||||||
|
);
|
||||||
|
const warnLong = this.t(
|
||||||
|
"memories",
|
||||||
|
"You are about to download the planet database. This may take a while."
|
||||||
|
);
|
||||||
|
const warnReindex = this.t(
|
||||||
|
"memories",
|
||||||
|
"This may also cause all photos to be re-indexed!"
|
||||||
|
);
|
||||||
|
const msg =
|
||||||
|
(this.status?.gis_count ? warnSetup : warnLong) + " " + warnReindex;
|
||||||
|
if (!confirm(msg)) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h2>{{ t("memories", "Video Streaming") }}</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Live transcoding provides for adaptive streaming of videos using HLS."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Note that this may be very CPU intensive without hardware acceleration, and transcoding will not be used for external storage."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:checked.sync="enableTranscoding"
|
||||||
|
@update:checked="update('memories.vod.disable', !enableTranscoding)"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable Transcoding") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="binaryStatusType(status.ffmpeg)">
|
||||||
|
{{ binaryStatus("ffmpeg", status.ffmpeg) }}
|
||||||
|
</NcNoteCard>
|
||||||
|
<NcNoteCard :type="binaryStatusType(status.ffprobe)">
|
||||||
|
{{ binaryStatus("ffprobe", status.ffprobe) }}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:label="t('memories', 'ffmpeg path')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.vod.ffmpeg']"
|
||||||
|
@change="update('memories.vod.ffmpeg', $event.target.value)"
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:label="t('memories', 'ffprobe path')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.vod.ffprobe']"
|
||||||
|
@change="update('memories.vod.ffprobe', $event.target.value)"
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{{ t("memories", "Global default video quality (user may override)") }}
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.video_default_quality']"
|
||||||
|
value="0"
|
||||||
|
name="vdq_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.video_default_quality')"
|
||||||
|
>{{ t("memories", "Auto (adaptive transcode)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.video_default_quality']"
|
||||||
|
value="-1"
|
||||||
|
name="vdq_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.video_default_quality')"
|
||||||
|
>{{ t("memories", "Original (transcode with max quality)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.video_default_quality']"
|
||||||
|
value="-2"
|
||||||
|
name="vdq_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.video_default_quality')"
|
||||||
|
>{{ t("memories", "Direct (original video file without transcode)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Video",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,171 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h3>{{ t("memories", "Hardware Acceleration") }}</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"You must first make sure the correct drivers are installed before configuring acceleration."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Make sure you test hardware acceleration with various options after enabling."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Do not enable multiple types of hardware acceleration simultaneously."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Intel processors supporting QuickSync Video (QSV) as well as some AMD GPUs can be used for transcoding using VA-API acceleration."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"For more details on driver installation, check the documentation:"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/pulsejet/memories/wiki/HW-Transcoding#va-api"
|
||||||
|
>
|
||||||
|
{{ t("memories", "External Link") }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<NcNoteCard :type="vaapiStatusType" v-if="status">
|
||||||
|
{{ vaapiStatusText }}
|
||||||
|
</NcNoteCard>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.vod.vaapi']"
|
||||||
|
@update:checked="update('memories.vod.vaapi')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable acceleration with VA-API") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding || !config['memories.vod.vaapi']"
|
||||||
|
:checked.sync="config['memories.vod.vaapi.low_power']"
|
||||||
|
@update:checked="update('memories.vod.vaapi.low_power')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable low-power mode (QSV)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"NVIDIA GPUs can be used for transcoding using the NVENC encoder with the proper drivers."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Depending on the versions of the installed SDK and ffmpeg, you need to specify the scaler to use"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
|
||||||
|
<NcNoteCard type="warning">
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"No automated tests are available for NVIDIA acceleration."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NcNoteCard>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.vod.nvenc']"
|
||||||
|
@update:checked="update('memories.vod.nvenc')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable acceleration with NVENC") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding || !config['memories.vod.nvenc']"
|
||||||
|
:checked.sync="config['memories.vod.nvenc.temporal_aq']"
|
||||||
|
@update:checked="update('memories.vod.nvenc.temporal_aq')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable NVENC Temporal AQ") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding || !config['memories.vod.nvenc']"
|
||||||
|
:checked.sync="config['memories.vod.nvenc.scale']"
|
||||||
|
value="npp"
|
||||||
|
name="nvence_scaler_radio"
|
||||||
|
type="radio"
|
||||||
|
@update:checked="update('memories.vod.nvenc.scale')"
|
||||||
|
class="m-radio"
|
||||||
|
>{{ t("memories", "NPP scaler") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding || !config['memories.vod.nvenc']"
|
||||||
|
:checked.sync="config['memories.vod.nvenc.scale']"
|
||||||
|
value="cuda"
|
||||||
|
name="nvence_scaler_radio"
|
||||||
|
type="radio"
|
||||||
|
class="m-radio"
|
||||||
|
@update:checked="update('memories.vod.nvenc.scale')"
|
||||||
|
>{{ t("memories", "CUDA scaler") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "VideoAccel",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
vaapiStatusText(): string {
|
||||||
|
if (!this.status) return "";
|
||||||
|
|
||||||
|
const dev = "/dev/dri/renderD128";
|
||||||
|
if (this.status.vaapi_dev === "ok") {
|
||||||
|
return this.t("memories", "VA-API device ({dev}) is readable", { dev });
|
||||||
|
} else if (this.status.vaapi_dev === "not_found") {
|
||||||
|
return this.t("memories", "VA-API device ({dev}) not found", { dev });
|
||||||
|
} else if (this.status.vaapi_dev === "not_readable") {
|
||||||
|
return this.t(
|
||||||
|
"memories",
|
||||||
|
"VA-API device ({dev}) has incorrect permissions",
|
||||||
|
{ dev }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.t("memories", "VA-API device status: {status}", {
|
||||||
|
status: this.status.vaapi_dev,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
vaapiStatusType(): string {
|
||||||
|
return this.status?.vaapi_dev === "ok" ? "success" : "error";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section">
|
||||||
|
<h3>{{ t("memories", "Transcoder configuration") }}</h3>
|
||||||
|
<p>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"memories",
|
||||||
|
"Memories uses the go-vod transcoder. You can run go-vod exernally (e.g. in a separate Docker container for hardware acceleration) or use the built-in transcoder. To use an external transcoder, enable the following option and follow the instructions in the documentation:"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/pulsejet/memories/wiki/HW-Transcoding"
|
||||||
|
>
|
||||||
|
{{ t("memories", "External Link") }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<template v-if="status">
|
||||||
|
<NcNoteCard :type="binaryStatusType(status.govod)">
|
||||||
|
{{ binaryStatus("go-vod", status.govod) }}
|
||||||
|
</NcNoteCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<NcCheckboxRadioSwitch
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:checked.sync="config['memories.vod.external']"
|
||||||
|
@update:checked="update('memories.vod.external')"
|
||||||
|
type="switch"
|
||||||
|
>
|
||||||
|
{{ t("memories", "Enable external transcoder (go-vod)") }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:label="t('memories', 'Binary path (local only)')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.vod.path']"
|
||||||
|
@change="update('memories.vod.path', $event.target.value)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:label="t('memories', 'Bind address (local only)')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.vod.bind']"
|
||||||
|
@change="update('memories.vod.bind', $event.target.value)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NcTextField
|
||||||
|
:disabled="!enableTranscoding"
|
||||||
|
:label="t('memories', 'Connection address (same as bind if local)')"
|
||||||
|
:label-visible="true"
|
||||||
|
:value="config['memories.vod.connect']"
|
||||||
|
@change="update('memories.vod.connect', $event.target.value)"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "VideoTranscoder",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<template>
|
||||||
|
<div class="admin-section"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
import AdminMixin from "../AdminMixin";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "Template",
|
||||||
|
mixins: [AdminMixin],
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -6,7 +6,7 @@ import XImg from "./components/frame/XImg.vue";
|
||||||
import GlobalMixin from "./mixins/GlobalMixin";
|
import GlobalMixin from "./mixins/GlobalMixin";
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import Admin from "./Admin.vue";
|
import Admin from "./components/admin/AdminMain.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import { generateFilePath } from "@nextcloud/router";
|
import { generateFilePath } from "@nextcloud/router";
|
||||||
import { getRequestToken } from "@nextcloud/auth";
|
import { getRequestToken } from "@nextcloud/auth";
|
||||||
|
|
Loading…
Reference in New Issue