commit
084def9346
9 changed files with 88 additions and 38 deletions
24
README.md
24
README.md
|
|
@ -19,6 +19,7 @@ YTPTube started as a fork of [meTube](https://github.com/alexta69/metube), Since
|
||||||
* Queue multiple URLs separated by comma.
|
* Queue multiple URLs separated by comma.
|
||||||
* Basic Authentication support.
|
* Basic Authentication support.
|
||||||
* Support for curl_cffi, see [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#impersonation)
|
* Support for curl_cffi, see [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#impersonation)
|
||||||
|
* Support for both advanced and basic mode for WebUI.
|
||||||
|
|
||||||
### Tips
|
### Tips
|
||||||
Your `yt-dlp` config should include the following options for optimal working conditions.
|
Your `yt-dlp` config should include the following options for optimal working conditions.
|
||||||
|
|
@ -94,6 +95,8 @@ Certain values can be set via environment variables, using the `-e` parameter on
|
||||||
* __YTP_UI_UPDATE_TITLE__: Whether to update the title of the page with the current stats. Defaults to `true`.
|
* __YTP_UI_UPDATE_TITLE__: Whether to update the title of the page with the current stats. Defaults to `true`.
|
||||||
* __YTP_PIP_PACKAGES__: a space separated list of pip packages to install. Defaults to `""`, you can also use `{config_path}/pip.txt` to install the packages.
|
* __YTP_PIP_PACKAGES__: a space separated list of pip packages to install. Defaults to `""`, you can also use `{config_path}/pip.txt` to install the packages.
|
||||||
* __YTP_PIP_IGNORE_UPDATES__: Do not update the custom pip packages. Defaults to `false`.
|
* __YTP_PIP_IGNORE_UPDATES__: Do not update the custom pip packages. Defaults to `false`.
|
||||||
|
* __YTP_BASIC_MODE__: Whether to run WebUI in basic mode. Defaults to `false`. In basic mode, A minimal UI will be shown, the majority of the features will be disabled.
|
||||||
|
* __YTP_DEFAULT_PRESET__: The default preset to use for the download. Defaults to `default`.
|
||||||
|
|
||||||
## Running behind a reverse proxy
|
## Running behind a reverse proxy
|
||||||
|
|
||||||
|
|
@ -341,6 +344,27 @@ As this is a simple basic authentication, if your browser doesn't show the promp
|
||||||
|
|
||||||
`http://username:password@your_ytptube_url:port`
|
`http://username:password@your_ytptube_url:port`
|
||||||
|
|
||||||
|
|
||||||
|
## Basic mode
|
||||||
|
|
||||||
|
What does the basic mode do? it hides the the following features from the WebUI.
|
||||||
|
|
||||||
|
### Header
|
||||||
|
|
||||||
|
It disables the `Check cookies`, `Console`, `Tasks` and `Add` buttons.
|
||||||
|
|
||||||
|
### Add form
|
||||||
|
|
||||||
|
Disables everything except the `URL` and `Add` button. the default preset `YTP_DEFAULT_PRESET` will be used. The folder will be
|
||||||
|
the root download path `YTP_DOWNLOAD_PATH`.
|
||||||
|
|
||||||
|
The add form will always be visible and un-collapsible.
|
||||||
|
|
||||||
|
### Queue & History
|
||||||
|
|
||||||
|
Disables the `Information` button.
|
||||||
|
|
||||||
|
|
||||||
# Social contact
|
# Social contact
|
||||||
|
|
||||||
If you have short or quick questions, you are free to join my [discord server](https://discord.gg/G3GpVR8xpb) and ask
|
If you have short or quick questions, you are free to join my [discord server](https://discord.gg/G3GpVR8xpb) and ask
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,14 @@ class Config:
|
||||||
ytdlp_version: str = YTDLP_VERSION
|
ytdlp_version: str = YTDLP_VERSION
|
||||||
started: int = 0
|
started: int = 0
|
||||||
ignore_ui: bool = False
|
ignore_ui: bool = False
|
||||||
|
basic_mode: bool = False
|
||||||
|
|
||||||
presets: list = [
|
presets: list = [
|
||||||
{"name": "default", "format": "default"},
|
{"name": "default", "format": "default"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
default_preset: str = "default"
|
||||||
|
|
||||||
_manual_vars: tuple = (
|
_manual_vars: tuple = (
|
||||||
"temp_path",
|
"temp_path",
|
||||||
"config_path",
|
"config_path",
|
||||||
|
|
@ -108,6 +112,7 @@ class Config:
|
||||||
"ignore_ui",
|
"ignore_ui",
|
||||||
"ui_update_title",
|
"ui_update_title",
|
||||||
"pip_ignore_updates",
|
"pip_ignore_updates",
|
||||||
|
"basic_mode",
|
||||||
)
|
)
|
||||||
|
|
||||||
_frontend_vars: tuple = (
|
_frontend_vars: tuple = (
|
||||||
|
|
@ -122,6 +127,8 @@ class Config:
|
||||||
"remove_files",
|
"remove_files",
|
||||||
"ui_update_title",
|
"ui_update_title",
|
||||||
"max_workers",
|
"max_workers",
|
||||||
|
"basic_mode",
|
||||||
|
"default_preset",
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -286,6 +293,9 @@ class Config:
|
||||||
if self.auth_password and self.auth_username:
|
if self.auth_password and self.auth_username:
|
||||||
LOG.warning(f"Basic authentication enabled with username '{self.auth_username}'.")
|
LOG.warning(f"Basic authentication enabled with username '{self.auth_username}'.")
|
||||||
|
|
||||||
|
if self.basic_mode:
|
||||||
|
LOG.info("The frontend is running in basic mode.")
|
||||||
|
|
||||||
self.started = time.time()
|
self.started = time.time()
|
||||||
|
|
||||||
def _getAttributes(self) -> dict:
|
def _getAttributes(self) -> dict:
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-half-mobile" v-if="item.url">
|
<div class="column is-half-mobile" v-if="item.url && !config.app.basic_mode">
|
||||||
<button class="button is-info is-fullwidth" @click="emitter('getInfo', item.url)">
|
<button class="button is-info is-fullwidth" @click="emitter('getInfo', item.url)">
|
||||||
<span class="icon-text is-block">
|
<span class="icon-text is-block">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4-tablet is-12-mobile">
|
<div class="column is-4-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="#" class="button is-static">Preset</a>
|
<a href="#" class="button is-static">Preset</a>
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-6-tablet is-12-mobile">
|
<div class="column is-6-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||||
<div class="field has-addons" v-tooltip="'Folder relative to ' + config.app.download_path">
|
<div class="field has-addons" v-tooltip="'Folder relative to ' + config.app.download_path">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="#" class="button is-static">Save in</a>
|
<a href="#" class="button is-static">Save in</a>
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
<span>Add</span>
|
<span>Add</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column" v-if="!config.app.basic_mode">
|
||||||
<button type="submit" class="button is-info" @click="showAdvanced = !showAdvanced"
|
<button type="submit" class="button is-info" @click="showAdvanced = !showAdvanced"
|
||||||
:class="{ 'is-loading': !socket.isConnected }" :disabled="!socket.isConnected">
|
:class="{ 'is-loading': !socket.isConnected }" :disabled="!socket.isConnected">
|
||||||
<span class="icon"><i class="fa-solid fa-cog" /></span>
|
<span class="icon"><i class="fa-solid fa-cog" /></span>
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns is-multiline is-mobile" v-if="showAdvanced">
|
<div class="columns is-multiline is-mobile" v-if="showAdvanced && !config.app.basic_mode">
|
||||||
<div class="column is-12">
|
<div class="column is-12">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="output_format"
|
<label class="label is-inline" for="output_format"
|
||||||
|
|
@ -139,11 +139,11 @@
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo'])
|
const emitter = defineEmits(['getInfo'])
|
||||||
const config = useConfigStore();
|
const config = useConfigStore()
|
||||||
const socket = useSocketStore();
|
const socket = useSocketStore()
|
||||||
const toast = useToast();
|
const toast = useToast()
|
||||||
|
|
||||||
const selectedPreset = useStorage('selectedPreset', '')
|
const selectedPreset = useStorage('selectedPreset', config.app.default_preset)
|
||||||
const ytdlpConfig = useStorage('ytdlp_config', '')
|
const ytdlpConfig = useStorage('ytdlp_config', '')
|
||||||
const ytdlpCookies = useStorage('ytdlp_cookies', '')
|
const ytdlpCookies = useStorage('ytdlp_cookies', '')
|
||||||
const output_template = useStorage('output_template', null)
|
const output_template = useStorage('output_template', null)
|
||||||
|
|
@ -153,36 +153,36 @@ const showAdvanced = useStorage('show_advanced', false)
|
||||||
const addInProgress = ref(false)
|
const addInProgress = ref(false)
|
||||||
|
|
||||||
const addDownload = () => {
|
const addDownload = () => {
|
||||||
addInProgress.value = true;
|
addInProgress.value = true
|
||||||
url.value.split(',').forEach(url => {
|
url.value.split(',').forEach(url => {
|
||||||
if (!url.trim()) {
|
if (!url.trim()) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
socket.emit('add_url', {
|
socket.emit('add_url', {
|
||||||
url: url,
|
url: url,
|
||||||
preset: selectedPreset.value,
|
preset: config.app.basic_mode ? config.app.default_preset : selectedPreset.value,
|
||||||
folder: downloadPath.value,
|
folder: config.app.basic_mode ? null : downloadPath.value,
|
||||||
ytdlp_config: ytdlpConfig.value,
|
ytdlp_config: config.app.basic_mode ? '' : ytdlpConfig.value,
|
||||||
ytdlp_cookies: ytdlpCookies.value,
|
ytdlp_cookies: config.app.basic_mode ? '' : ytdlpCookies.value,
|
||||||
output_template: output_template.value,
|
output_template: config.app.basic_mode ? null : output_template.value,
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetConfig = () => {
|
const resetConfig = () => {
|
||||||
if (true !== confirm('Reset your local configuration?')) {
|
if (true !== confirm('Reset your local configuration?')) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedPreset.value = 'default';
|
selectedPreset.value = config.app.default_preset.value
|
||||||
ytdlpConfig.value = '';
|
ytdlpConfig.value = ''
|
||||||
ytdlpCookies.value = '';
|
ytdlpCookies.value = ''
|
||||||
output_template.value = null;
|
output_template.value = null
|
||||||
url.value = null;
|
url.value = null
|
||||||
downloadPath.value = null;
|
downloadPath.value = null
|
||||||
showAdvanced.value = false;
|
showAdvanced.value = false
|
||||||
|
|
||||||
toast.success('Local configuration has been reset.');
|
toast.success('Local configuration has been reset.')
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusHandler = async data => {
|
const statusHandler = async data => {
|
||||||
|
|
@ -195,13 +195,13 @@ const statusHandler = async data => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url.value = '';
|
url.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
socket.on('status', statusHandler)
|
socket.on('status', statusHandler)
|
||||||
if ('' === selectedPreset.value) {
|
if ('' === selectedPreset.value) {
|
||||||
selectedPreset.value = 'default'
|
selectedPreset.value = config.app.default_preset.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onUnmounted(() => socket.off('status', statusHandler))
|
onUnmounted(() => socket.off('status', statusHandler))
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-half-mobile" v-if="item.url">
|
<div class="column is-half-mobile" v-if="item.url && !config.app.basic_mode">
|
||||||
<button class="button is-info is-fullwidth" @click="emitter('getInfo', item.url)">
|
<button class="button is-info is-fullwidth" @click="emitter('getInfo', item.url)">
|
||||||
<span class="icon-text is-block">
|
<span class="icon-text is-block">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-end is-flex">
|
<div class="navbar-end is-flex">
|
||||||
|
|
||||||
<div class="navbar-item" v-if="socket.isConnected && config.app.has_cookies">
|
<div class="navbar-item" v-if="socket.isConnected && config.app.has_cookies && !config.app.basic_mode">
|
||||||
<button class="button is-dark" @click="checkCookies" v-tooltip="'Check youtube cookies status.'"
|
<button class="button is-dark" @click="checkCookies" v-tooltip="'Check youtube cookies status.'"
|
||||||
:disabled="isChecking">
|
:disabled="isChecking">
|
||||||
<span class="icon has-text-info"><i class="fas fa-cookie"></i></span>
|
<span class="icon has-text-info"><i class="fas fa-cookie"></i></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-item" v-if="socket.isConnected">
|
<div class="navbar-item" v-if="socket.isConnected && !config.app.basic_mode">
|
||||||
<button class="button is-dark" @click="pauseDownload" v-if="false === config.paused"
|
<button class="button is-dark" @click="pauseDownload" v-if="false === config.paused"
|
||||||
v-tooltip="'Pause non-active downloads.'">
|
v-tooltip="'Pause non-active downloads.'">
|
||||||
<span class="icon has-text-warning"><i class="fas fa-pause"></i></span>
|
<span class="icon has-text-warning"><i class="fas fa-pause"></i></span>
|
||||||
|
|
@ -29,20 +29,20 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-item">
|
<div class="navbar-item" v-if="!config.app.basic_mode">
|
||||||
<NuxtLink class="button is-dark has-tooltip-bottom" to="/console" v-tooltip.bottom="'Terminal'">
|
<NuxtLink class="button is-dark has-tooltip-bottom" to="/console" v-tooltip.bottom="'Terminal'">
|
||||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-item">
|
<div class="navbar-item" v-if="!config.app.basic_mode">
|
||||||
<button v-tooltip.bottom="'Toggle Add Form'" class="button is-dark has-tooltip-bottom"
|
<button v-tooltip.bottom="'Toggle Add Form'" class="button is-dark has-tooltip-bottom"
|
||||||
@click="config.showForm = !config.showForm">
|
@click="config.showForm = !config.showForm">
|
||||||
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-item" v-if="config.tasks.length > 0" v-tooltip.bottom="'Tasks'">
|
<div class="navbar-item" v-if="!config.app.basic_mode && config.tasks.length > 0" v-tooltip.bottom="'Tasks'">
|
||||||
<NuxtLink class="button is-dark has-tooltip-bottom" to="/tasks">
|
<NuxtLink class="button is-dark has-tooltip-bottom" to="/tasks">
|
||||||
<span class="icon"><i class="fa-solid fa-tasks" /></span>
|
<span class="icon"><i class="fa-solid fa-tasks" /></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<NewDownload v-if="config.showForm" @getInfo="url => get_info = url" />
|
<NewDownload v-if="config.showForm || config.app.basic_mode" @getInfo="url => get_info = url" />
|
||||||
<NuxtPage @getInfo="url => get_info = url" />
|
<NuxtPage @getInfo="url => get_info = url" />
|
||||||
<GetInfo v-if="get_info" :link="get_info" @closeModel="get_info = ''" />
|
<GetInfo v-if="get_info" :link="get_info" @closeModel="get_info = ''" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const command = ref('')
|
||||||
const terminal_window = ref()
|
const terminal_window = ref()
|
||||||
const command_input = ref()
|
const command_input = ref()
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
const config = useConfigStore()
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
|
||||||
watch(() => isLoading.value, async value => {
|
watch(() => isLoading.value, async value => {
|
||||||
|
|
@ -149,6 +149,13 @@ const writer = s => {
|
||||||
|
|
||||||
const loader = () => isLoading.value = false
|
const loader = () => isLoading.value = false
|
||||||
|
|
||||||
|
watch(() => config.app.basic_mode, async () => {
|
||||||
|
if (!config.app.basic_mode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await navigateTo('/')
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
window.addEventListener('resize', reSizeTerminal);
|
window.addEventListener('resize', reSizeTerminal);
|
||||||
focusInput()
|
focusInput()
|
||||||
|
|
|
||||||
|
|
@ -64,4 +64,11 @@ div.table-container {
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { parseExpression } from 'cron-parser'
|
import { parseExpression } from 'cron-parser'
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|
||||||
|
watch(() => config.app.basic_mode, async () => {
|
||||||
|
if (!config.app.basic_mode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await navigateTo('/')
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@ const CONFIG_KEYS = {
|
||||||
url_host: '',
|
url_host: '',
|
||||||
url_prefix: '',
|
url_prefix: '',
|
||||||
has_cookies: false,
|
has_cookies: false,
|
||||||
|
basic_mode: true,
|
||||||
|
default_preset: 'default',
|
||||||
},
|
},
|
||||||
presets: [
|
presets: [
|
||||||
{
|
{
|
||||||
'name': 'Default',
|
'name': 'default',
|
||||||
'format': 'default'
|
'format': 'default'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue