2023-04-10 06:24:59 +00:00
< template >
< div class = "outer" v-if ="loaded" >
2023-04-10 23:58:45 +00:00
< NcLoadingIcon class = "loading-icon" v -show = " loading " / >
2023-04-11 02:17:16 +00:00
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- - General Settings -- -- -- -- -- -- -- -- -- -- -- -- -- -- - >
2023-04-10 06:42:23 +00:00
< h2 > { { t ( "memories" , "EXIF Extraction" ) } } < / h2 >
2023-04-10 07:20:17 +00:00
< template v-if ="status" >
< NcNoteCard :type ="binaryStatusType(status.exiftool)" >
{ { binaryStatus ( "exiftool" , status . exiftool ) } }
< / NcNoteCard >
< / template >
2023-04-10 06:42:23 +00:00
< NcTextField
: label = "t('memories', 'Path to packaged exiftool binary')"
: label - visible = "true"
: value = "exiftoolPath"
@ change = "update('exiftoolPath', $event.target.value)"
disabled
/ >
2023-04-10 07:20:17 +00:00
< template v-if ="status" >
< NcNoteCard : type = "binaryStatusType(status.perl, false)" >
{ { binaryStatus ( "perl" , status . perl ) } }
< / NcNoteCard >
< / template >
2023-04-10 06:42:23 +00:00
< NcCheckboxRadioSwitch
: checked . sync = "exiftoolPerl"
@ update : checked = "update('exiftoolPerl')"
type = "switch"
>
{ {
t ( "memories" , "Use system perl (only if packaged binary does not work)" )
} }
< / NcCheckboxRadioSwitch >
2023-04-11 02:17:16 +00:00
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- - Places -- -- -- -- -- -- -- -- -- -- -- -- -- -- - >
< h2 > { { t ( "memories" , "Reverse Geocoding" ) } } < / h2 >
< p >
< template v-if ="status" >
< NcNoteCard :type ="gisStatusType" >
{ { gisStatus } }
< / NcNoteCard >
< NcNoteCard
v - if = "status.gis_count !== undefined"
: type = "status.gis_count > 0 ? '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" )
} }
< / 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 'occ memories:places-setup'."
)
} }
< 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" / >
< NcButton nativeType = "submit" type = "warning" >
{ { t ( "memories" , "Download planet database" ) } }
< / NcButton >
< / form >
<!-- -- -- -- -- -- -- -- -- -- -- -- -- -- - Video Streaming -- -- -- -- -- -- -- -- -- -- -- -- -- -- - >
2023-04-10 06:42:23 +00:00
< h2 > { { t ( "memories" , "Video Streaming" ) } } < / h2 >
< p >
{ {
t (
"memories" ,
"Live transcoding provides for adaptive streaming of videos using HLS."
)
} }
< br / >
{ {
t (
"memories" ,
2023-04-10 21:33:24 +00:00
"Note that this may be very CPU intensive without hardware acceleration, and transcoding will not be used for external storage."
2023-04-10 06:42:23 +00:00
)
} }
< NcCheckboxRadioSwitch
: checked . sync = "enableTranscoding"
@ update : checked = "update('enableTranscoding')"
type = "switch"
>
{ { t ( "memories" , "Enable Transcoding" ) } }
< / NcCheckboxRadioSwitch >
2023-04-10 07:20:17 +00:00
< 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 >
2023-04-10 06:42:23 +00:00
< NcTextField
: label = "t('memories', 'ffmpeg path')"
: label - visible = "true"
: value = "ffmpegPath"
@ change = "update('ffmpegPath', $event.target.value)"
/ >
< NcTextField
: label = "t('memories', 'ffprobe path')"
: label - visible = "true"
: value = "ffprobePath"
@ change = "update('ffprobePath', $event.target.value)"
/ >
< br / >
{ { t ( "memories" , "Global default video quality (user may override)" ) } }
< NcCheckboxRadioSwitch
: checked . sync = "videoDefaultQuality"
value = "0"
name = "vdq_radio"
type = "radio"
@ update : checked = "update('videoDefaultQuality')"
> { { t ( "memories" , "Auto (adaptive transcode)" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: checked . sync = "videoDefaultQuality"
value = "-1"
name = "vdq_radio"
type = "radio"
@ update : checked = "update('videoDefaultQuality')"
> { { t ( "memories" , "Original (transcode with max quality)" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: 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 at this link:"
)
} }
< a
target = "_blank"
href = "https://github.com/pulsejet/memories/wiki/HW-Transcoding"
>
{ { t ( "memories" , "external transcoder configuration" ) } }
< / a >
2023-04-10 07:20:17 +00:00
< template v-if ="status" >
< NcNoteCard :type ="binaryStatusType(status.govod)" >
{ { binaryStatus ( "go-vod" , status . govod ) } }
< / NcNoteCard >
< / template >
2023-04-10 06:42:23 +00:00
< NcCheckboxRadioSwitch
: checked . sync = "enableExternalTranscoder"
@ update : checked = "update('enableExternalTranscoder')"
type = "switch"
>
{ { t ( "memories" , "Enable external transcoder (go-vod)" ) } }
< / NcCheckboxRadioSwitch >
< NcTextField
: label = "t('memories', 'Binary path (local only)')"
: label - visible = "true"
: value = "goVodPath"
@ change = "update('goVodPath', $event.target.value)"
/ >
< NcTextField
: label = "t('memories', 'Bind address (local only)')"
: label - visible = "true"
: value = "goVodBind"
@ change = "update('goVodBind', $event.target.value)"
/ >
< NcTextField
: 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 following link:"
)
} }
< a
target = "_blank"
href = "https://github.com/pulsejet/memories/wiki/HW-Transcoding#va-api"
>
VA - API configuration
< / a >
2023-04-11 02:17:16 +00:00
< NcNoteCard :type ="vaapiStatusType" v-if ="status" >
{ { vaapiStatusText } }
2023-04-10 06:42:23 +00:00
< / NcNoteCard >
< NcCheckboxRadioSwitch
: checked . sync = "enableVaapi"
@ update : checked = "update('enableVaapi')"
type = "switch"
>
{ { t ( "memories" , "Enable acceleration with VA-API" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: 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" >
2023-04-10 06:24:59 +00:00
{ {
t (
"memories" ,
2023-04-10 06:42:23 +00:00
"No automated tests are available for NVIDIA acceleration."
2023-04-10 06:24:59 +00:00
)
} }
2023-04-10 06:42:23 +00:00
< / NcNoteCard >
< NcCheckboxRadioSwitch
: checked . sync = "enableNvenc"
@ update : checked = "update('enableNvenc')"
type = "switch"
>
{ { t ( "memories" , "Enable acceleration with NVENC" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: checked . sync = "enableNvencTemporalAQ"
@ update : checked = "update('enableNvencTemporalAQ')"
type = "switch"
>
{ { t ( "memories" , "Enable NVENC Temporal AQ" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: checked . sync = "nvencScaler"
value = "npp"
name = "nvence_scaler_radio"
type = "radio"
@ update : checked = "update('nvencScaler')"
class = "m-radio"
> { { t ( "memories" , "NPP scaler" ) } }
< / NcCheckboxRadioSwitch >
< NcCheckboxRadioSwitch
: checked . sync = "nvencScaler"
value = "cuda"
name = "nvence_scaler_radio"
type = "radio"
class = "m-radio"
@ update : checked = "update('nvencScaler')"
> { { t ( "memories" , "CUDA scaler" ) } }
< / NcCheckboxRadioSwitch >
< / p >
2023-04-10 06:24:59 +00:00
< / 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" ) ;
2023-04-10 23:58:45 +00:00
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon" ;
2023-04-11 02:17:16 +00:00
import NcButton from "@nextcloud/vue/dist/Components/NcButton" ;
2023-04-10 06:24:59 +00:00
/** Map from UI to backend settings */
const settings = {
2023-04-10 06:42:23 +00:00
exiftoolPath : "memories.exiftool" ,
exiftoolPerl : "memories.exiftool_no_local" ,
2023-04-10 06:24:59 +00:00
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" ] ;
2023-04-10 07:28:23 +00:00
type BinaryStatus = "ok" | "not_found" | "not_executable" | "test_ok" | string ;
2023-04-10 07:20:17 +00:00
type IStatus = {
2023-04-11 02:17:16 +00:00
gis _type : number ;
gis _count ? : number ;
2023-04-10 07:20:17 +00:00
exiftool : BinaryStatus ;
perl : BinaryStatus ;
ffmpeg : BinaryStatus ;
ffprobe : BinaryStatus ;
govod : BinaryStatus ;
vaapi _dev : "ok" | "not_found" | "not_readable" ;
} ;
2023-04-10 06:24:59 +00:00
export default defineComponent ( {
name : "Admin" ,
components : {
NcCheckboxRadioSwitch ,
NcNoteCard ,
NcTextField ,
2023-04-10 23:58:45 +00:00
NcLoadingIcon ,
2023-04-11 02:17:16 +00:00
NcButton ,
2023-04-10 06:24:59 +00:00
} ,
data : ( ) => ( {
loaded : false ,
2023-04-10 06:42:23 +00:00
exiftoolPath : "" ,
exiftoolPerl : false ,
2023-04-10 06:24:59 +00:00
enableTranscoding : false ,
ffmpegPath : "" ,
ffprobePath : "" ,
goVodPath : "" ,
goVodBind : "" ,
goVodConnect : "" ,
enableExternalTranscoder : false ,
videoDefaultQuality : "" ,
enableVaapi : false ,
enableVaapiLowPower : false ,
enableNvenc : false ,
enableNvencTemporalAQ : false ,
nvencScaler : "" ,
2023-04-10 07:20:17 +00:00
2023-04-10 23:58:45 +00:00
loading : 0 ,
2023-04-10 07:20:17 +00:00
status : null as IStatus ,
2023-04-10 06:24:59 +00:00
} ) ,
mounted ( ) {
this . reload ( ) ;
2023-04-10 07:20:17 +00:00
this . refreshStatus ( ) ;
2023-04-10 06:24:59 +00:00
} ,
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 ;
} ,
2023-04-10 07:20:17 +00:00
async refreshStatus ( ) {
2023-04-10 23:58:45 +00:00
try {
this . loading ++ ;
const res = await axios . get ( API . SYSTEM _STATUS ( ) ) ;
this . status = res . data ;
} finally {
this . loading -- ;
}
2023-04-10 07:20:17 +00:00
} ,
2023-04-10 06:24:59 +00:00
async update ( key : string , value = null ) {
2023-04-10 21:32:35 +00:00
value = value ? ? this [ key ] ;
2023-04-10 06:24:59 +00:00
const setting = settings [ key ] ;
2023-04-10 23:58:45 +00:00
this [ key ] = value ;
2023-04-10 06:24:59 +00:00
// Inversion
if ( invertedBooleans . includes ( key ) ) {
value = ! value ;
}
2023-04-10 23:58:45 +00:00
this . loading ++ ;
2023-04-10 06:24:59 +00:00
axios
. put ( API . SYSTEM _CONFIG ( setting ) , {
value : value ,
} )
. catch ( ( err ) => {
console . error ( err ) ;
showError ( this . t ( "memories" , "Failed to update setting" ) ) ;
2023-04-10 23:58:45 +00:00
} )
. finally ( ( ) => {
this . loading -- ;
if ( this [ "refreshTimer" ] ) {
clearTimeout ( this [ "refreshTimer" ] ) ;
}
this [ "refreshTimer" ] = setTimeout ( ( ) => {
this . refreshStatus ( ) ;
delete this [ "refreshTimer" ] ;
} , 500 ) ;
2023-04-10 06:24:59 +00:00
} ) ;
} ,
2023-04-10 07:20:17 +00:00
2023-04-11 02:17:16 +00:00
placesSetup ( event : Event ) {
const msg =
"Looks like the database is already setup. Are you sure you want to drop the table and redownload OSM data?" ;
if ( this . status . gis _count && ! confirm ( msg ) ) {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
return ;
} else {
alert (
"Please wait for the download and insertion to complete. This may take a while."
) ;
}
} ,
2023-04-10 07:20:17 +00:00
binaryStatus ( name : string , status : BinaryStatus ) : string {
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 ,
} ) ;
2023-04-10 07:28:23 +00:00
} else if ( status . startsWith ( "test_fail" ) ) {
return this . t (
"memories" ,
2023-04-10 21:01:29 +00:00
"{name} failed test: {info}" ,
2023-04-10 07:28:23 +00:00
{
name ,
info : status . substring ( 10 ) ,
} ,
0 ,
{
escape : false ,
sanitize : false ,
}
) ;
2023-04-10 07:20:17 +00:00
} else if ( status === "test_ok" ) {
return this . t ( "memories" , "{name} binary exists and is usable" , {
name ,
} ) ;
} else {
return this . t ( "memories" , "{name} binary status: {status}" , {
name ,
status ,
} ) ;
}
} ,
binaryStatusType ( status : BinaryStatus , critical = true ) : string {
if ( status === "ok" || status === "test_ok" ) {
return "success" ;
} else if (
status === "not_found" ||
status === "not_executable" ||
2023-04-10 07:28:23 +00:00
status . startsWith ( "test_fail" )
2023-04-10 07:20:17 +00:00
) {
return critical ? "error" : "warning" ;
} else {
return "warning" ;
}
} ,
2023-04-11 02:17:16 +00:00
} ,
computed : {
requestToken ( ) {
return ( < any > axios . defaults . headers ) . requesttoken ;
} ,
gisStatus ( ) {
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 ( ) ;
} ,
2023-04-10 07:20:17 +00:00
vaapiStatusText ( ) : string {
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" ;
} ,
2023-04-10 06:24:59 +00:00
} ,
} ) ;
< / script >
< style lang = "scss" scoped >
. outer {
padding : 20 px ;
2023-04-10 06:42:23 +00:00
padding - top : 0 px ;
2023-04-10 06:24:59 +00:00
2023-04-10 23:58:45 +00:00
. loading - icon {
top : 10 px ;
right : 20 px ;
position : absolute ;
width : 28 px ;
height : 28 px ;
: deep svg {
width : 100 % ;
height : 100 % ;
}
}
2023-04-11 02:17:16 +00:00
form {
margin - top : 1 em ;
}
2023-04-10 06:24:59 +00:00
. checkbox - radio - switch {
margin : 2 px 8 px ;
}
. m - radio {
display : inline - block ;
}
h2 {
font - size : 1.5 em ;
font - weight : bold ;
2023-04-10 06:42:23 +00:00
margin - top : 25 px ;
2023-04-10 06:24:59 +00:00
}
h3 {
font - size : 1.2 em ;
font - weight : 500 ;
margin - top : 20 px ;
}
a {
color : var ( -- color - primary - element ) ;
}
}
< / style >