app/js/renderer.js

/**
* @file Contains all renderer code
* @author yafp
* @namespace renderer
*/

'use strict'

// ----------------------------------------------------------------------------
// IMPORT MODULES
// ----------------------------------------------------------------------------
require('v8-compile-cache')

// ----------------------------------------------------------------------------
// IMPORT MEDIA-DUPES MODULES
// ----------------------------------------------------------------------------
const utils = require('./js/modules/utils.js')
const ffmpeg = require('./js/modules/ffmpeg.js')
const ui = require('./js/modules/ui.js')
const settings = require('./js/modules/settings.js')
const youtubeDl = require('./js/modules/youtubeDl.js')
const sentry = require('./js/modules/sentry.js')
const crash = require('./js/modules/crashReporter.js') // crashReporter
const unhandled = require('./js/modules/unhandled.js') // electron-unhandled

// ----------------------------------------------------------------------------
// ERROR HANDLING
// ----------------------------------------------------------------------------
// crash.initCrashReporter() // since electron9: crashReporter.start is deprecated in the renderer process. Call it from the main process instead.
unhandled.initUnhandled()

// ----------------------------------------------------------------------------
// VARIABLES
// ----------------------------------------------------------------------------

// Settings variables
//
var ytdlBinaryVersion = '0.0.0'
var youtubeDlBinaryDetailsVersion
var youtubeDlBinaryDetailsPath
var youtubeDLBinaryDetailsExec

// ----------------------------------------------------------------------------
// FUNCTIONS - MAIN WINDOW CLICKS
// ----------------------------------------------------------------------------

/**
* @function windowMainClickDistract
* @summary Handles the click on the app icon
* @description Triggered from the mainWindow. Starts the easteregg / distract function
* @memberof renderer
*/
function windowMainClickDistract () {
    ui.windowMainDistract()
}

/**
* @function windowMainClickButtonAddUrl
* @summary Handles the click on the AddUrl button
* @description Triggered from the mainWindow. Starts the add url function from the module ui
* @memberof renderer
*/
function windowMainClickButtonAddUrl () {
    ui.windowMainAddUrl()
}

/**
* @function windowMainClickButtonVideo
* @summary Handles the click on the video button
* @description Triggered from the mainWindow. Starts the video download function from the module ui
* @memberof renderer
*/
function windowMainClickButtonVideo () {
    ui.windowMainDownloadContent('video')
    sentry.countEvent('usageButtonVideoExec')
}

/**
* @function windowMainClickButtonVideoV2
* @summary Handles the click on the video button
* @description Triggered from the mainWindow. Starts the video download function from the module ui
* @memberof renderer
*/
function windowMainClickButtonVideoV2 () {
    ui.windowMainDownloadVideo()
}

/**
* @function windowMainClickButtonAudio
* @summary Handles the click on the audio button
* @description Triggered from the mainWindow. Starts the audio download function from the module ui
* @memberof renderer
*/
function windowMainClickButtonAudio () {
    ui.windowMainDownloadContent('audio')
    sentry.countEvent('usageButtonAudioExec')
}

/**
* @function windowMainClickButtonSettings
* @summary Handles the click on the settings button
* @description Triggered from the mainWindow. Starts the settings UI from the module ui
* @memberof renderer
*/
function windowMainClickButtonSettings () {
    ui.windowMainSettingsUiLoad()
}

/**
* @function windowMainClickButtonIntro
* @summary Handles the click on the intro button
* @description Triggered from the mainWindow. Starts the application intro from the module ui
* @memberof renderer
*/
function windowMainClickButtonIntro () {
    ui.windowMainIntroShow()
}

/**
* @function windowMainClickButtonDownloads
* @summary Handles the click on the Downloads button
* @description Triggered from the mainWindow. Starts the open-download-folder function from the module ui
* @memberof renderer
*/
function windowMainClickButtonDownloads () {
    ui.windowMainOpenDownloadFolder()
}

/**
* @function windowMainClickButtonLogReset
* @summary Handles the click on the log-reset button
* @description Triggered from the mainWindow. Starts the reset-log function from the module ui
* @memberof renderer
*/
function windowMainClickButtonLogReset () {
    ui.windowMainLogReset()
}

/**
* @function windowMainClickButtonUIReset
* @summary Handles the click on the reset-UI button
* @description Triggered from the mainWindow. Starts the reset-UI function from the module ui
* @memberof renderer
*/
function windowMainClickButtonUIReset () {
    ui.windowMainResetAskUser()
}

// ----------------------------------------------------------------------------
// FUNCTIONS - SETTINGS WINDOW CLICKS
// ----------------------------------------------------------------------------
/**
* @function windowSettingsClickIconUserSettingsDir
* @summary Handles the click on the settings icon
* @description Triggered from the settingsWindow. Starts the open-settings-folder function from the module settings
* @memberof renderer
*/
function windowSettingsClickIconUserSettingsDir () {
    settings.settingsFolderOpen()
}

/**
* @function windowSettingsClickButtonChooseDownloadDir
* @summary Handles the click on the choose download dir button. Starts the select-download-dir function from the module settings
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickButtonChooseDownloadDir () {
    settings.settingsSelectDownloadDir()
}

/**
* @function windowSettingsClickCheckboxVerboseMode
* @summary Handles the click on the checkbox verbose mode
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickCheckboxVerboseMode () {
    settings.settingsToggleVerboseMode()
}

/**
* @function windowSettingsClickCheckboxAdditionalParameter
* @summary Handles the click on the checkbox vadditional parameter
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickCheckboxAdditionalParameter () {
    settings.settingsToggleAdditionalParameter()
}

/**
* @function windowSettingsClickButtonAdditionalParameterSave
* @summary Handles the click on the button additional parameter save
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickButtonAdditionalParameterSave () {
    settings.settingsSaveAdditionalParameter()
}

/**
* @function windowSettingsClickCheckboxUpdatePolicy
* @summary Handles the click on the checkbox verbose mode
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickCheckboxUpdatePolicy () {
    settings.settingsTogglePrereleases()
}

/**
* @function windowSettingsClickIconBug
* @summary Handles the click on the bug icon
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickIconBug () {
    settings.settingsOpenDevTools()
}

/**
* @function windowSettingsClickCheckboxErrorReporting
* @summary Handles the click on the checkbox error reporting
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickCheckboxErrorReporting () {
    settings.settingsToggleErrorReporting()
}

/**
* @function windowSettingsClickCheckboxErrorReportingMoreInfo
* @summary Handles the click on the question icon in the error reporting section
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickCheckboxErrorReportingMoreInfo () {
    const { urlGithubSentryUsage } = require('./js/modules/urls.js') // get url
    utils.openURL(urlGithubSentryUsage)
}

/**
* @function windowSettingsClickDropdownAudioFormats
* @summary Handles the click on the dropdown audio formats
* @description Triggered from the settingsWindow.
* @memberof renderer
*/
function windowSettingsClickDropdownAudioFormats () {
    settings.settingsAudioFormatSave()
}

/**
* @function windowSettingsClickOpenUrl
* @summary Handles the click on the dropdown audio formats
* @description Triggered from the settingsWindow.
* @param {string} url - the url
* @memberof renderer
*/
function windowSettingsClickOpenUrl (url) {
    settings.settingsOpenExternal(url)
}

/**
* @function windowSettingsClickYoutubeDlUpdate
* @summary Starts the check for youtube-dl updates routine with feedback to the user
* @description Starts the check for youtube-dl updates routine with feedback to the user
* @memberof renderer
*/
function windowSettingsClickYoutubeDlUpdate () {
    youtubeDl.binaryUpdateCheck(false, false) // If silent = false -> Forces result feedback, even if no update is available
}

// ----------------------------------------------------------------------------
// FUNCTIONS - OTHERS
// ----------------------------------------------------------------------------

/**
* @function titlebarInit
* @summary Init the titlebar for the frameless mainWindow
* @description Creates a custom titlebar for the mainWindow using custom-electron-titlebar (https://github.com/AlexTorresSk/custom-electron-titlebar).
* @memberof renderer
*/
function titlebarInit () {
    const customTitlebar = require('custom-electron-titlebar')

    const myTitlebar = new customTitlebar.Titlebar({
    // new customTitlebar.Titlebar({
        titleHorizontalAlignment: 'center', // position of window title
        icon: 'img/icon/icon.png',
        drag: true, // whether or not you can drag the window by holding the click on the title bar.
        backgroundColor: customTitlebar.Color.fromHex('#343a40'),
        minimizable: true,
        maximizable: true,
        closeable: true,
        unfocusEffect: false, // added in 0.9.0
        itemBackgroundColor: customTitlebar.Color.fromHex('#ffe5e5') // menu item -> hover color
    })

    // Be aware: the font-size of .window-title (aka application name) is set by app/css/core.css
    utils.writeConsoleMsg('info', 'titlebarInit ::: Initialized custom titlebar')
}

/**
* @function checkApplicationDependencies
* @function checkApplicationDependencies
* @summary Checks for missing dependencies
* @description Checks on startup for missing dependencies (youtube-dl and ffmpeg). Both are bundles and should be find
* @memberof renderer
*/
function checkApplicationDependencies () {
    var countErrors = 0

    // youtube-dl
    //
    var youtubeDlBinaryPath = youtubeDl.binaryPathGet()
    if (utils.pathExists(youtubeDlBinaryPath) === true) {
        utils.writeConsoleMsg('info', 'checkApplicationDependencies ::: Found youtube-dl in: _' + youtubeDlBinaryPath + '_.')
    } else {
        countErrors = countErrors + 1
        utils.writeConsoleMsg('error', 'checkApplicationDependencies ::: Unable to find youtube-dl in: _' + youtubeDlBinaryPath + '_.')
        utils.showNoty('error', 'Unable to find dependency <b>youtube-dl</b>. Please report this.', 0)
    }

    // ffmpeg
    //
    var ffmpegBinaryPath = ffmpeg.ffmpegGetBinaryPath()
    if (utils.pathExists(ffmpegBinaryPath) === true) {
        utils.writeConsoleMsg('info', 'checkApplicationDependencies ::: Found ffmpeg in: _' + ffmpegBinaryPath + '_.')
    } else {
        countErrors = countErrors + 1
        utils.writeConsoleMsg('error', 'checkApplicationDependencies ::: Unable to find ffmpeg in: _' + ffmpegBinaryPath + '_.')
        utils.showNoty('error', 'Unable to find dependency <b>ffmpeg</b>. Please report this', 0)
    }

    // if errors occured - disable / hide the action buttons
    //
    if (countErrors !== 0) {
        $('#buttonStartVideoExec').hide() // hide video button
        $('#buttonStartVideo').hide() // hide video button
        $('#buttonStartAudioExec').hide() // hide audio button
        utils.showNoty('error', 'Download buttons are now hidden. Please contact the developers via github.', 0)
    }

    utils.writeConsoleMsg('info', 'checkApplicationDependencies ::: Finished checking dependencies. Found overall _' + countErrors + '_ problems.')
}

/**
* @function settingsLoadAllOnAppStart
* @summary Reads all user-setting-files and fills some global variables
* @description Reads all user-setting-files and fills some global variables
* @memberof renderer
*/
function settingsLoadAllOnAppStart () {
    utils.writeConsoleMsg('info', 'settingsLoadAllOnAppStart ::: Gonna read several user config files now ...')
    utils.userSettingRead('enableVerboseMode') // verbose mode
    utils.userSettingRead('enableUrlInformations') // url informations
    utils.userSettingRead('enableAdditionalParameter') // additional parameter
    utils.userSettingRead('additionalYoutubeDlParameter') // additional parameter
    utils.userSettingRead('enablePrereleases') // pre-releases
    utils.userSettingRead('enableErrorReporting') // get setting for error-reporting
    utils.userSettingRead('downloadDir') // download dir
    utils.userSettingRead('audioFormat') // get setting for configured audio format
}

/**
* @function settingsLoadAllOnSettingsUiLoad
* @summary Reads all user-setting-files and fills some global variables and adjusts the settings UI
* @description Reads all user-setting-files and fills some global variables and adjusts the settings UI
* @memberof renderer
*/
function settingsLoadAllOnSettingsUiLoad () {
    utils.writeConsoleMsg('info', 'settingsLoadAllOnAppStart ::: Gonna read several user config files now and adjust the settings UI')
    utils.userSettingRead('enableVerboseMode', true) // verbose mode
    utils.userSettingRead('enableUrlInformations', true) // url informations
    utils.userSettingRead('enableAdditionalParameter', true) // enable or not: additional parameter
    utils.userSettingRead('additionalYoutubeDlParameter', true) // the actual additional youtube-dl parameter
    utils.userSettingRead('enablePrereleases', true) // pre-releases
    utils.userSettingRead('enableErrorReporting', true) // get setting for error-reporting
    utils.userSettingRead('downloadDir', true) // download dir
    utils.userSettingRead('audioFormat', true) // load configured audio format and update the settings UI

    settings.settingsEnableOrDisableYoutubeDLUpdateButton()
}

/**
* @function urlInputFieldOnKeyUp
* @summary On Key up event, checks if the field is empty or not
* @description On Key up event, checks if the field is empty or not
* @memberof renderer
*/
function urlInputFieldOnKeyUp () {
    var currentContentOfUrlInputField = $('#inputNewUrl').val() // get current content of field
    if (currentContentOfUrlInputField === '') {
        utils.writeConsoleMsg('info', 'urlInputFieldOnKeyUp ::: Is now empty, gonna reset the background color')
        ui.inputUrlFieldSetState() // empty = white
    } else {
        ui.inputUrlFieldSetState('unchecked') // unchecked = light red

        var isUrlValid = utils.validURL(currentContentOfUrlInputField)
        if (isUrlValid) {
            utils.writeConsoleMsg('info', 'urlInputFieldOnKeyUp ::: User input is a valid URL (' + currentContentOfUrlInputField + ').')
            ui.inputUrlFieldSetState('valid') // valid = green
        } else {
            // utils.writeConsoleMsg('info', 'urlInputFieldOnKeyUp ::: User input is not a valid URL (' + currentContentOfUrlInputField + ').')
        }
    }
}

/**
* @function urlInputFieldOnKeyPress
* @summary Executed on keypress inside url-input-field
* @description Checks if the key-press was the ENTER-key - if so simulates a press of the button ADD URL
* @memberof renderer
* @event keyCode - The key press event
*/
function urlInputFieldOnKeyPress (event) {
    var code = 0
    code = event.keyCode
    if (code === 13) {
        windowMainClickButtonAddUrl() // simulare click on ADD URL buttom
    }
}

/**
* @function urlInputFieldOnFocus
* @summary Handles auto-pasting urls to url input field
* @description Executed on focus - checks if the clipboard contains a valid URL - if so - its auto-pasted into the field
* @memberof renderer
*/
function urlInputFieldOnFocus () {
    utils.writeConsoleMsg('info', 'urlInputFieldOnFocus ::: url input field got focus')

    var currentContentOfUrlInputField = $('#inputNewUrl').val() // get current content of field

    // if the field is empty - continue
    if (currentContentOfUrlInputField === '') {
        const { clipboard } = require('electron')
        var currentClipboardContent = clipboard.readText() // get content of clipboard
        currentClipboardContent = currentClipboardContent.trim() // remove leading and trailing blanks

        var isUrlValid = utils.validURL(currentClipboardContent)
        if (isUrlValid) {
            utils.writeConsoleMsg('info', 'urlInputFieldOnFocus ::: Clipboard contains a valid URL: _' + currentClipboardContent + '_.')
            $('#inputNewUrl').val(currentClipboardContent) // paste it
            $('#inputNewUrl').select() // select it entirely
            ui.inputUrlFieldSetState('valid') // valid = green
        } else {
            utils.writeConsoleMsg('warn', 'urlInputFieldOnFocus ::: Clipboard contains a non valid URL: _' + currentClipboardContent + '_.')
        }
    } else {
        // input field is not empty
        // doing nothing at this point
    }
}

/**
* @function searchUpdate
* @summary Checks if there is a new media-dupes release available
* @description Compares the local app version number with the tag of the latest github release. Displays a notification in the settings window if an update is available. Is executed on app launch NOT on reload.
* @memberof renderer
* @param {booean} [silent] - Boolean with default value. Shows a feedback in case of no available updates If 'silent' = false. Special handling for manually triggered update search
*/
function searchUpdate (silent = true) {
    var semver = require('semver')

    ui.windowMainApplicationStateSet('Searching media-dupes updates')

    // check if pre-releases should be included or not
    var curEnablePrereleasesSetting = utils.globalObjectGet('enablePrereleases')

    // get url for github releases / api
    const { urlGithubApiReleases } = require('./js/modules/urls.js') // get API url

    var remoteAppVersionLatest = '0.0.0'
    var remoteAppVersionLatestPrerelease = false
    var localAppVersion = '0.0.0'
    var versions

    // get local version
    //
    localAppVersion = require('electron').remote.app.getVersion()

    // var updateStatus = $.get(urlGithubApiReleases, function (data, status) {
    $.get(urlGithubApiReleases, function (data, status) {
        // 3000 // in milliseconds

        utils.writeConsoleMsg('info', 'searchUpdate ::: Accessing _' + urlGithubApiReleases + '_ ended with: _' + status + '_')

        // success
        versions = data.sort(function (v1, v2) {
            // return semver.compare(v2.tag_name, v1.tag_name);
            // console.error(v1.tag_name)
            // console.error(v2.tag_name)
        })

        if (curEnablePrereleasesSetting === true) {
            // user wants the latest release - ignoring if it is a prerelease or an official one
            utils.writeConsoleMsg('info', 'searchUpdate ::: Including pre-releases in update search')
            remoteAppVersionLatest = versions[0].tag_name // Example: 0.4.2
            remoteAppVersionLatestPrerelease = versions[0].prerelease // boolean
        } else {
            // user wants official releases only
            utils.writeConsoleMsg('info', 'searchUpdate ::: Ignoring pre-releases in update search')
            // find the latest non pre-release build
            // loop over the versions array to find the latest non-pre-release
            // var latestOfficialRelease
            for (var i = 0; i < versions.length; i++) {
                if (versions[i].prerelease === false) {
                    // latestOfficialRelease = i
                    break
                }
            }

            remoteAppVersionLatest = versions[i].tag_name // Example: 0.4.2
            remoteAppVersionLatestPrerelease = versions[i].prerelease // boolean
        }

        // simulate different  update scenarios:
        //
        // localAppVersion = '0.0.1'; //  overwrite variable to simulate
        // remoteAppVersionLatest = 'v0.6.0' //  overwrite variable to simulate

        // strip the v away
        // - up to 0.5.0 the tag used on github did not start with v.
        // - comapring versions without leading chars is much easier.
        localAppVersion = localAppVersion.replace('v', '')
        remoteAppVersionLatest = remoteAppVersionLatest.replace('v', '')

        utils.writeConsoleMsg('info', 'searchUpdate ::: Local media-dupes version: ' + localAppVersion)
        utils.writeConsoleMsg('info', 'searchUpdate ::: Latest media-dupes version: ' + remoteAppVersionLatest)

        // If a stable (not a prelease) update is available - see #73

        // if (localAppVersion < remoteAppVersionLatest) {
        if (semver.lt(localAppVersion, remoteAppVersionLatest)) {
            utils.writeConsoleMsg('info', 'searchUpdate ::: Found update, notify user')

            // prepare the message for the user - depending on the fact if it is a pre-release or not
            var updateText
            if (remoteAppVersionLatestPrerelease === false) {
                updateText = 'A media-dupes update from <b>' + localAppVersion + '</b> to version <b>' + remoteAppVersionLatest + '</b> is available. Do you want to visit the release page?'
            } else {
                updateText = 'A media-dupes <b>pre-release</b> update from <b>' + localAppVersion + '</b> to version <b>' + remoteAppVersionLatest + '</b> is available. Do you want to visit the release page?'
            }

            // ask user using a noty confirm dialog
            const Noty = require('noty')
            var n = new Noty(
                {
                    theme: 'bootstrap-v4',
                    layout: 'bottom',
                    type: 'info',
                    closeWith: [''], // to prevent closing the confirm-dialog by clicking something other then a confirm-dialog-button
                    text: updateText,
                    buttons: [
                        Noty.button('Yes', 'btn btn-success mediaDupes_btnDefaultWidth', function () {
                            n.close()
                            openReleasesOverview()
                        },
                        {
                            id: 'button1', 'data-status': 'ok'
                        }),

                        Noty.button('No', 'btn btn-secondary mediaDupes_btnDefaultWidth float-right', function () {
                            n.close()

                            if (remoteAppVersionLatestPrerelease === false) {
                                utils.showNoty('warning', 'Please be aware that not updating <b>media-dupes</b> will result in an <b>outdated youtube-dl version</b> which again will result in download errors.', 0)
                            }
                        })
                    ]
                })

            // show the noty dialog
            n.show()
        } else {
            utils.writeConsoleMsg('info', 'searchUpdate ::: No newer version of media-dupes found.')

            // when executed manually via menu -> user should see result of this search
            if (silent === false) {
                utils.showNoty('info', 'No updates for <b>media-dupes (' + localAppVersion + ')</b> available.')
            }
        }

        utils.writeConsoleMsg('info', 'searchUpdate ::: Successfully checked ' + urlGithubApiReleases + ' for available releases')
    })
        .done(function () {
        // utils.writeConsoleMsg('info', 'searchUpdate ::: Successfully checked ' + urlGithubApiReleases + ' for available releases');
        })

        .fail(function () {
            utils.writeConsoleMsg('info', 'searchUpdate ::: Checking ' + urlGithubApiReleases + ' for available releases failed.')
            utils.showNoty('error', 'Checking <b>' + urlGithubApiReleases + '</b> for available media-dupes releases failed. Please troubleshoot your network connection.', 0)
        })

        .always(function () {
            utils.writeConsoleMsg('info', 'searchUpdate ::: Finished checking ' + urlGithubApiReleases + ' for available releases')
            ui.windowMainButtonsOthersEnable()
            ui.windowMainApplicationStateSet()
        })
}

/**
* @function openReleasesOverview
* @summary Opens the media-dupes release page
* @description Opens the url https://github.com/yafp/media-dupes/releases in the default browser. Used in searchUpdate().
* @memberof renderer
*/
function openReleasesOverview () {
    const { urlGitHubReleases } = require('./js/modules/urls.js')
    utils.writeConsoleMsg('info', 'openReleasesOverview ::: Opening _' + urlGitHubReleases + '_ to show available releases.')
    utils.openURL(urlGitHubReleases)
}

/**
* @function settingsShowYoutubeDLInfo
* @summary Searches the youtube-binary and shows it in the settings UI
* @description Searches the youtube-binary and shows it in the settings UI
* @memberof renderer
*/
function settingsShowYoutubeDLInfo () {
    const youtubedl = require('youtube-dl')

    settingsGetYoutubeDLBinaryVersion(function () {
        utils.writeConsoleMsg('info', 'settingsShowYoutubeDLInfo ::: Searching youtube-dl ...')
        var youtubeDl = youtubedl.getYtdlBinary()
        if (youtubeDl === '') {
            utils.writeConsoleMsg('error', 'settingsShowYoutubeDLInfo ::: Unable to find youtube-dl')
            utils.showNoty('error', 'Unable to find dependency <b>youtube-dl</b>.', 0)
        } else {
            utils.writeConsoleMsg('info', 'settingsShowYoutubeDLInfo ::: Found youtube-dl in: _' + youtubeDl + '_.')
            $('#userSettingsYouTubeDLPathInfo').val(youtubeDl) // show in UI
            $('#headerYoutubeDL').html('Installation <small>Version: ' + ytdlBinaryVersion + '</small>')
        }
    })
}

/**
* @function settingsShowFfmpegInfo
* @summary Searches the ffmpeg-binary and shows it in the settings UI
* @description Searches the ffmpeg-binary and shows it in the settings UI
* @memberof renderer
*/
function settingsShowFfmpegInfo () {
    var ffmpeg = require('ffmpeg-static-electron')
    // utils.writeConsoleMsg('info', 'settingsShowFfmpegInfo ::: Searching ffmpeg ...')
    if (ffmpeg === '') {
        utils.writeConsoleMsg('error', 'settingsShowFfmpegInfo ::: Unable to find ffmpeg')
        utils.showNoty('error', 'Unable to find dependency <b>ffmpeg</b>.', 0)
    } else {
        utils.writeConsoleMsg('info', 'settingsShowFfmpegInfo ::: Found ffmpeg in: _' + ffmpeg.path + '_.')
        $('#userSettingsFfmpegPathInfo').val(ffmpeg.path) // show in UI
    }
}

/**
* @function settingsGetYoutubeDLBinaryVersion
* @summary Gets the youtube-dl binary version and displays it in settings ui
* @description Reads the youtube-dl binary version from 'node_modules/youtube-dl/bin/details'
* @memberof renderer
* @return ytdlBinaryVersion - The youtube-dl binary version string
*/
function settingsGetYoutubeDLBinaryVersion (_callback) {
    const fs = require('fs')

    var youtubeDlBinaryDetailsPath = youtubeDl.binaryDetailsPathGet() // get path to youtube-dl binary details
    fs.readFile(youtubeDlBinaryDetailsPath, 'utf8', function (error, contents) {
        if (error) {
            utils.writeConsoleMsg('error', 'settingsGetYoutubeDLBinaryVersion ::: Unable to detect youtube-dl binary version. Error: ' + error + '.')
            utils.showNoty('error', 'Unable to detect local youtube-dl binary version number. Error: ' + error, 0) // see sentry issue: MEDIA-DUPES-5A
            throw error
        } else {
            const data = JSON.parse(contents)
            ytdlBinaryVersion = data.version // extract and store the version number
            utils.writeConsoleMsg('info', 'settingsGetYoutubeDLBinaryVersion ::: youtube-dl binary is version: _' + ytdlBinaryVersion + '_.')
            _callback()
        }
    })
}

/**
* @function validateUrlBeforeAdd
* @summary Gets the content of the url field, checks if it is a valid url, if so checks if it is reachable or not
* @description Gets the content of the url field, checks if it is a valid url, if so checks if it is reachable or not
* @memberof renderer
*/
function validateUrlBeforeAdd () {
    var currentContentOfUrlInputField = $('#inputNewUrl').val() // get current content of field

    // if the field is empty - continue
    if (currentContentOfUrlInputField === '') {
        utils.writeConsoleMsg('info', 'validateUrlBeforeAdd ::: Empty field')
    } else {
        var isUrlValid = utils.validURL(currentContentOfUrlInputField)
        if (isUrlValid) {
            utils.writeConsoleMsg('info', 'validateUrlBeforeAdd ::: URL seems valid URL (' + currentContentOfUrlInputField + ').')
        } else {
            utils.writeConsoleMsg('info', 'urlInputFieldOnFocus ::: Clipboard contains a non valid URL (' + currentContentOfUrlInputField + ').')
        }
    }
}

// ----------------------------------------------------------------------------
// IPC
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// IPC - on ready-to-show
// ----------------------------------------------------------------------------

/**
* @name startSearchUpdatesSilent
* @summary Triggers the check for media-dupes updates in silent mode
* @description Called via ipc from main.js on-ready to start the search for media-dupes updates
* @memberof renderer
*/
/*
require('electron').ipcRenderer.on('startSearchUpdatesSilent', function () {
    searchUpdate(true) // If silent = false -> Forces result feedback, even if no update is available
})
*/

/**
* @name youtubeDlSearchUpdatesSilent
* @summary Triggers the check for youtube-dl updates in silent mode
* @description Called via ipc from main.js on-ready to check the search for youtube-dl updates
* @memberof renderer
*/
/*
require('electron').ipcRenderer.on('youtubeDlSearchUpdatesSilent', function () {
    youtubeDl.binaryUpdateCheck(true, false) // If silent = false -> Forces result feedback, even if no update is available
})
*/

/**
* @name initSettings
* @summary Triggers the check for the application dependencies
* @description Called via ipc from main.js on-ready to check the application dependencies
* @memberof renderer
*/
/*
require('electron').ipcRenderer.on('initSettings', function () {
    settingsLoadAllOnAppStart()
})
*/

/**
* @name startCheckingDependencies
* @summary Triggers the check for the application dependencies
* @description Called via ipc from main.js on-ready to check the application dependencies
* @memberof renderer
*/
require('electron').ipcRenderer.on('startCheckingDependencies', function () {
    checkApplicationDependencies()
})

/**
* @name scheduleUpdateCheckMediaDupes
* @summary Starts the silent search for media-dupes updates
* @description Starts the silent search for media-dupes updates after several seconds (to speed up the application startup)
* @memberof renderer
*/
require('electron').ipcRenderer.on('scheduleUpdateCheckMediaDupes', function () {
    setTimeout(
        function () {
            utils.writeConsoleMsg('info', 'scheduleUpdateCheckMediaDupes ::: Starting scheduled search for new media-dupes updates.')
            searchUpdate(true) // silent
        }, 3000) // after 3 seconds
})

/**
* @name scheduleUpdateCheckYoutubeDl
* @summary Starts the silent search for youtube-dl updates
* @description Starts the silent search for youtube-dl updates after several seconds (to speed up the application startup)
* @memberof renderer
*/
require('electron').ipcRenderer.on('scheduleUpdateCheckYoutubeDl', function () {
    setTimeout(
        function () {
            utils.writeConsoleMsg('info', 'scheduleUpdateCheckYoutubeDl ::: Starting scheduled search for new youtube-dl updates.')
            youtubeDl.binaryUpdateCheck(true, false) // If silent = false -> Forces result feedback, even if no update is available
        }, 3000) // after 3 seconds
})

/**
* @name startDisclaimerCheck
* @summary Triggers the check for disclaimer need
* @description Called via ipc from main.js on-ready to check for the disclaimer need
* @memberof renderer
*/
require('electron').ipcRenderer.on('startDisclaimerCheck', function () {
    utils.disclaimerCheck()
})

/**
* @name todoListCheck
* @summary Triggers the check restoring previosly stored urls
* @description Called via ipc from main.js on-ready-to-show and starts the restore function
* @memberof renderer
*/
require('electron').ipcRenderer.on('todoListCheck', function () {
    // utils.writeConsoleMsg('info', 'todoListCheck ::: main.js forces renderer to check for urls to restore')
    ui.windowMainToDoListRestore()
})

// ----------------------------------------------------------------------------
// IPC - by menu
// ----------------------------------------------------------------------------

/**
* @name startSearchUpdatesVerbose
* @summary Start searching for updates in non-silent mode
* @description Called via ipc from main.js / menu to search for applicatipn updates
* @memberof renderer
*/
require('electron').ipcRenderer.on('startSearchUpdatesVerbose', function () {
    searchUpdate(false) // silent = false. Forces result feedback, even if no update is available
})

/**
* @name openSettings
* @summary Triggers loading the settings UI
* @description Called via ipc from main.js / menu to open the Settings UI
* @memberof renderer
*/
require('electron').ipcRenderer.on('openSettings', function () {
    ui.windowMainSettingsUiLoad()
})

/**
* @name openYoutubeSuggestDialog
* @summary Triggers a input dialog to search for youtube suggest based on an input string
* @description Called via ipc from main.js / menu to ....
* @memberof renderer
*/
require('electron').ipcRenderer.on('openYoutubeSuggestDialog', function () {
    ui.youtubeSuggest()
})

/**
* @name youtubeDlBinaryUpdate
* @summary Triggers updating the youtube-dl binary
* @description Called via ipc from main.js / menu to update the youtube-dl binary
* @memberof renderer
*/
require('electron').ipcRenderer.on('youtubeDlBinaryUpdate', function () {
    youtubeDl.binaryUpdateCheck(false, true) // silent = false && force = true
})

/**
* @name youtubeDlBinaryPathReset
* @summary Triggers resetting the path to the youtube-dl binary back to default
* @description Called via ipc from main.js / menu to reset the path to the youtube-dl binary
* @memberof renderer
*/
require('electron').ipcRenderer.on('youtubeDlBinaryPathReset', function () {
    var youtubeDlBinaryDetailsPath = youtubeDl.binaryDetailsPathGet() // get path to youtube-dl binary details file

    utils.canWriteFileOrFolder(youtubeDlBinaryDetailsPath, function (error, isWritable) {
        if (error) {
            utils.writeConsoleMsg('error', 'youtubeDlBinaryPathReset ::: Error while trying to check if the youtube-dl details file is writeable or not. Error: ' + error)
            throw error
        }

        if (isWritable === true) {
            utils.writeConsoleMsg('info', 'youtubeDlBinaryPathReset ::: :  Found the youtube-dl details file and it is writeable. Gonna ask the user now if he wants to reset the path now')

            // ask the user if he wants to update using a confirm dialog
            const Noty = require('noty')
            var n = new Noty(
                {
                    theme: 'bootstrap-v4',
                    layout: 'bottom',
                    type: 'info',
                    closeWith: [''], // to prevent closing the confirm-dialog by clicking something other then a confirm-dialog-button
                    text: 'Do you really want to reset the youtube-dl binary path back to its default value?',
                    buttons: [
                        Noty.button('Yes', 'btn btn-success mediaDupes_btnDownloadActionWidth', function () {
                            n.close()
                            youtubeDl.binaryPathReset(youtubeDlBinaryDetailsPath)
                        },
                        {
                            id: 'button1', 'data-status': 'ok'
                        }),

                        Noty.button('No', 'btn btn-secondary mediaDupes_btnDownloadActionWidth float-right', function () {
                            n.close()
                        })
                    ]
                })

            n.show() // show the noty dialog
        } else {
            // details file cant be resetted due to permission issues
            utils.writeConsoleMsg('warn', 'youtubeDlBinaryPathReset ::: Found youtube-dl binary update, but unable to execute update due to permissions')
            utils.showNoty('error', 'Unable to reset the <b>youtube-dl</b> setup due to permissions issues. The file: ' + youtubeDlBinaryDetailsPath + ' is not writeable.')
        }
    })

    utils.writeConsoleMsg('info', 'youtubeDlBinaryPathReset ::: Finished re-setting the binary path for youtube-dl')
})

// ----------------------------------------------------------------------------
// IPC - by power-state
// ----------------------------------------------------------------------------

/**
* @name powerMonitorNotification
* @summary Triggers power specific notifications to the UI
* @description Called via ipc from main.js to trigger a notification about the powerState
* @memberof renderer
*/
require('electron').ipcRenderer.on('powerMonitorNotification', function (event, messageType, messageText, messageDuration) {
    utils.writeConsoleMsg('warn', 'powerMonitorNotification ::: Main wants to show a notification of the type: _' + messageType + '_ and the message: _' + messageText + '_ with the duration: _' + messageDuration + '_.')
    utils.showNoty(messageType, messageText, messageDuration)
})

// ----------------------------------------------------------------------------
// IPC - by settings window closed
// ----------------------------------------------------------------------------

/**
* @name unblurMainUI
* @summary Triggers unbluring the UI
* @description Called via ipc from main.js when the settings UI got closed to trigger unblur'ing the main UI
* @memberof renderer

*/
require('electron').ipcRenderer.on('unblurMainUI', function () {
    ui.windowMainBlurSet(false)
})

/**
* @name blurMainUI
* @summary Triggers bluring the UI
* @description Called via ipc from main.js when the main window is ready-to-show
* @memberof renderer
*/
require('electron').ipcRenderer.on('blurMainUI', function () {
    ui.windowMainBlurSet(true)
})

/**
* @name countAppStarts
* @summary ..
* @description Called via ipc from main.js when the main window is ready-to-show
* @memberof renderer
*/
require('electron').ipcRenderer.on('countAppStarts', function () {
    sentry.countEvent('usageApplicationStarted')
})

// ----------------------------------------------------------------------------
// IPC - by mainwindow close event
// ----------------------------------------------------------------------------

/**
* @name todoListTryToSave
* @summary Triggers saving of the current todoList
* @description Called via ipc from main.js  when the application gets closed Should save existing todoList entries
* @memberof renderer
*/
require('electron').ipcRenderer.on('todoListTryToSave', function () {
    ui.windowMainToDoListSave()
})