refactor: remove ui_update_title and standardize page title
This commit is contained in:
parent
c2cb5527f5
commit
29777ff928
13 changed files with 37 additions and 55 deletions
1
FAQ.md
1
FAQ.md
|
|
@ -30,7 +30,6 @@ or the `environment:` section in `compose.yaml` file.
|
||||||
| YTP_DEBUG | Whether to turn on debug mode | `false` |
|
| YTP_DEBUG | Whether to turn on debug mode | `false` |
|
||||||
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
|
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
|
||||||
| YTP_EXTRACT_INFO_TIMEOUT | The timeout for extracting video information | `70` |
|
| YTP_EXTRACT_INFO_TIMEOUT | The timeout for extracting video information | `70` |
|
||||||
| YTP_UI_UPDATE_TITLE | Whether to update the title of the page with the current stats | `true` |
|
|
||||||
| YTP_PIP_PACKAGES | A space separated list of pip packages to install | `(not_set)` |
|
| YTP_PIP_PACKAGES | A space separated list of pip packages to install | `(not_set)` |
|
||||||
| YTP_PIP_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
|
| YTP_PIP_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
|
||||||
| YTP_PICTURES_BACKENDS | A comma separated list of pictures urls to use | `(not_set)` |
|
| YTP_PICTURES_BACKENDS | A comma separated list of pictures urls to use | `(not_set)` |
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,6 @@ class Config(metaclass=Singleton):
|
||||||
apprise_config: str = "{config_path}{os_sep}apprise.yml"
|
apprise_config: str = "{config_path}{os_sep}apprise.yml"
|
||||||
"""The path to the Apprise configuration file."""
|
"""The path to the Apprise configuration file."""
|
||||||
|
|
||||||
ui_update_title: bool = True
|
|
||||||
"""Update the title of the browser tab with the current status."""
|
|
||||||
|
|
||||||
pip_packages: str = ""
|
pip_packages: str = ""
|
||||||
"""The pip packages to install."""
|
"""The pip packages to install."""
|
||||||
|
|
||||||
|
|
@ -284,7 +281,6 @@ class Config(metaclass=Singleton):
|
||||||
"access_log",
|
"access_log",
|
||||||
"remove_files",
|
"remove_files",
|
||||||
"ignore_ui",
|
"ignore_ui",
|
||||||
"ui_update_title",
|
|
||||||
"pip_ignore_updates",
|
"pip_ignore_updates",
|
||||||
"file_logging",
|
"file_logging",
|
||||||
"console_enabled",
|
"console_enabled",
|
||||||
|
|
@ -308,7 +304,6 @@ class Config(metaclass=Singleton):
|
||||||
"output_template",
|
"output_template",
|
||||||
"started",
|
"started",
|
||||||
"remove_files",
|
"remove_files",
|
||||||
"ui_update_title",
|
|
||||||
"max_workers",
|
"max_workers",
|
||||||
"max_workers_per_extractor",
|
"max_workers_per_extractor",
|
||||||
"default_preset",
|
"default_preset",
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import Hls from 'hls.js';
|
import Hls from 'hls.js';
|
||||||
import { disableOpacity, enableOpacity } from '~/utils';
|
import { disableOpacity, enableOpacity, formatPageTitle } from '~/utils';
|
||||||
import { useKeyboardShortcuts } from '~/composables/useKeyboardShortcuts';
|
import { useKeyboardShortcuts } from '~/composables/useKeyboardShortcuts';
|
||||||
|
|
||||||
import type { StoreItem } from '~/types/store';
|
import type { StoreItem } from '~/types/store';
|
||||||
|
|
@ -319,6 +319,8 @@ const havePoster = ref(false);
|
||||||
const showHelp = ref(false);
|
const showHelp = ref(false);
|
||||||
const usingHls = ref(false);
|
const usingHls = ref(false);
|
||||||
|
|
||||||
|
useHead(() => (title.value ? { title: formatPageTitle(`Playing: ${title.value}`) } : {}));
|
||||||
|
|
||||||
let unbindMediaSessionListeners: null | (() => void) = null;
|
let unbindMediaSessionListeners: null | (() => void) = null;
|
||||||
let hls: Hls | null = null;
|
let hls: Hls | null = null;
|
||||||
let cleanupKeyboardShortcuts: null | (() => void) = null;
|
let cleanupKeyboardShortcuts: null | (() => void) = null;
|
||||||
|
|
@ -679,10 +681,6 @@ onBeforeUnmount(() => {
|
||||||
unbindMediaSessionListeners = null;
|
unbindMediaSessionListeners = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title.value) {
|
|
||||||
window.document.title = 'YTPTube';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!video.value) {
|
if (!video.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -706,10 +704,6 @@ const prepareVideoPlayer = () => {
|
||||||
|
|
||||||
applyMediaSessionMetadata();
|
applyMediaSessionMetadata();
|
||||||
|
|
||||||
if (title.value) {
|
|
||||||
window.document.title = `YTPTube - Playing: ${title.value}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!video.value) {
|
if (!video.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ const state = reactive<ConfigState>({
|
||||||
app: {
|
app: {
|
||||||
download_path: '/downloads',
|
download_path: '/downloads',
|
||||||
remove_files: false,
|
remove_files: false,
|
||||||
ui_update_title: true,
|
|
||||||
output_template: '',
|
output_template: '',
|
||||||
ytdlp_version: '',
|
ytdlp_version: '',
|
||||||
max_workers: 20,
|
max_workers: 20,
|
||||||
|
|
|
||||||
|
|
@ -497,6 +497,7 @@ import { useStorage } from '@vueuse/core';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { useMediaQuery } from '~/composables/useMediaQuery';
|
import { useMediaQuery } from '~/composables/useMediaQuery';
|
||||||
import type { toastPosition } from '~/composables/useNotification';
|
import type { toastPosition } from '~/composables/useNotification';
|
||||||
|
import { formatPageTitle } from '~/utils';
|
||||||
import type { YTDLPOption } from '~/types/ytdlp';
|
import type { YTDLPOption } from '~/types/ytdlp';
|
||||||
import { useDialog } from '~/composables/useDialog';
|
import { useDialog } from '~/composables/useDialog';
|
||||||
import Dialog from '~/components/Dialog.vue';
|
import Dialog from '~/components/Dialog.vue';
|
||||||
|
|
@ -750,10 +751,17 @@ const sidebarItems = computed<
|
||||||
.filter((section) => section.items.some((group) => group.length > 0));
|
.filter((section) => section.items.some((group) => group.length > 0));
|
||||||
});
|
});
|
||||||
|
|
||||||
const pageTitle = computed(() => {
|
const activeNavItem = computed(() => getActiveNavItem(route, navigationAvailability.value));
|
||||||
const match = getActiveNavItem(route, navigationAvailability.value);
|
|
||||||
return match?.navbarTitle || match?.label || 'YTPTube';
|
const pageTitle = computed(
|
||||||
});
|
() => activeNavItem.value?.navbarTitle || activeNavItem.value?.label || 'YTPTube',
|
||||||
|
);
|
||||||
|
|
||||||
|
const documentTitle = computed(() => activeNavItem.value?.pageLabel || pageTitle.value);
|
||||||
|
|
||||||
|
useHead(() => ({
|
||||||
|
title: formatPageTitle(documentTitle.value),
|
||||||
|
}));
|
||||||
|
|
||||||
const buildTooltip = computed(
|
const buildTooltip = computed(
|
||||||
() =>
|
() =>
|
||||||
|
|
|
||||||
|
|
@ -527,6 +527,7 @@ import moment from 'moment';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
import type { DropdownMenuItem } from '@nuxt/ui';
|
import type { DropdownMenuItem } from '@nuxt/ui';
|
||||||
import type { FileItem } from '~/types/filebrowser';
|
import type { FileItem } from '~/types/filebrowser';
|
||||||
|
import { formatPageTitle } from '~/utils';
|
||||||
import { requirePageShell } from '~/utils/topLevelNavigation';
|
import { requirePageShell } from '~/utils/topLevelNavigation';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
@ -559,6 +560,7 @@ const pageShell = requirePageShell('files');
|
||||||
const hasItems = computed(() => filteredItems.value.length > 0);
|
const hasItems = computed(() => filteredItems.value.length > 0);
|
||||||
const hasSelected = computed(() => selectedElms.value.length > 0);
|
const hasSelected = computed(() => selectedElms.value.length > 0);
|
||||||
const displayedItemPaths = computed(() => filteredItems.value.map((item) => item.path));
|
const displayedItemPaths = computed(() => filteredItems.value.map((item) => item.path));
|
||||||
|
const browserPageTitle = computed(() => `File Browser: /${sTrim(browserPath.value || '/', '/')}`);
|
||||||
const currentDirectoryName = computed(
|
const currentDirectoryName = computed(
|
||||||
() => breadcrumbItems.value[breadcrumbItems.value.length - 1]?.name || 'Home',
|
() => breadcrumbItems.value[breadcrumbItems.value.length - 1]?.name || 'Home',
|
||||||
);
|
);
|
||||||
|
|
@ -752,6 +754,10 @@ const itemSizeLabel = (item: FileItem): string => {
|
||||||
return item.type === 'file' ? formatBytes(item.size) : ucFirst(item.type);
|
return item.type === 'file' ? formatBytes(item.size) : ucFirst(item.type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useHead(() => ({
|
||||||
|
title: formatPageTitle(decodeURIComponent(browserPageTitle.value)),
|
||||||
|
}));
|
||||||
|
|
||||||
const updateUrl = (dir: string, page?: number): void => {
|
const updateUrl = (dir: string, page?: number): void => {
|
||||||
const normalizedDir = dir.replace(/^\/+/, '').replace(/\/+$/, '');
|
const normalizedDir = dir.replace(/^\/+/, '').replace(/\/+$/, '');
|
||||||
const displayDir = normalizedDir ? normalizedDir : '/';
|
const displayDir = normalizedDir ? normalizedDir : '/';
|
||||||
|
|
@ -762,8 +768,6 @@ const updateUrl = (dir: string, page?: number): void => {
|
||||||
if (fullUrl !== window.location.href) {
|
if (fullUrl !== window.location.href) {
|
||||||
history.replaceState({ path: normalizedDir || '/', title }, title, stateUrl);
|
history.replaceState({ path: normalizedDir || '/', title }, title, stateUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
useHead({ title: decodeURIComponent(title) });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (item: FileItem): void => {
|
const handleClick = (item: FileItem): void => {
|
||||||
|
|
|
||||||
|
|
@ -193,8 +193,6 @@ const toast = useNotification();
|
||||||
const config = useYtpConfig();
|
const config = useYtpConfig();
|
||||||
const pageShell = requirePageShell('changelog');
|
const pageShell = requirePageShell('changelog');
|
||||||
|
|
||||||
useHead({ title: 'CHANGELOG' });
|
|
||||||
|
|
||||||
const PROJECT = 'ytptube';
|
const PROJECT = 'ytptube';
|
||||||
const REPO = `https://github.com/arabcoders/${PROJECT}`;
|
const REPO = `https://github.com/arabcoders/${PROJECT}`;
|
||||||
const REPO_URL = `https://arabcoders.github.io/${PROJECT}/CHANGELOG.json?version={version}`;
|
const REPO_URL = `https://arabcoders.github.io/${PROJECT}/CHANGELOG.json?version={version}`;
|
||||||
|
|
|
||||||
|
|
@ -327,8 +327,6 @@ import type { AutoCompleteOptions } from '~/types/autocomplete';
|
||||||
import { disableOpacity, enableOpacity } from '~/utils';
|
import { disableOpacity, enableOpacity } from '~/utils';
|
||||||
import { requirePageShell } from '~/utils/topLevelNavigation';
|
import { requirePageShell } from '~/utils/topLevelNavigation';
|
||||||
|
|
||||||
useHead({ title: 'Console' });
|
|
||||||
|
|
||||||
const MAX_HISTORY_ITEMS = 50;
|
const MAX_HISTORY_ITEMS = 50;
|
||||||
const ACTIVE_SESSION_STATUSES = ['starting', 'running', 'reconnecting'];
|
const ACTIVE_SESSION_STATUSES = ['starting', 'running', 'reconnecting'];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Markdown from '~/components/Markdown.vue';
|
import Markdown from '~/components/Markdown.vue';
|
||||||
import { DOCS_ENTRIES, getDocsEntryBySlug } from '~/composables/useDocs';
|
import { DOCS_ENTRIES, getDocsEntryBySlug } from '~/composables/useDocs';
|
||||||
|
import { formatPageTitle } from '~/utils';
|
||||||
import { requirePageShell } from '~/utils/topLevelNavigation';
|
import { requirePageShell } from '~/utils/topLevelNavigation';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
@ -71,7 +72,7 @@ const docEntry = computed(() => {
|
||||||
const pageShell = computed(() => requirePageShell(docEntry.value.id));
|
const pageShell = computed(() => requirePageShell(docEntry.value.id));
|
||||||
|
|
||||||
useHead(() => ({
|
useHead(() => ({
|
||||||
title: docEntry.value.title,
|
title: formatPageTitle(docEntry.value.title),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const pageCardUi = {
|
const pageCardUi = {
|
||||||
|
|
|
||||||
|
|
@ -781,8 +781,7 @@ const embed_url = ref('');
|
||||||
const isRefreshing = ref(false);
|
const isRefreshing = ref(false);
|
||||||
const autoRefreshInterval = ref<ReturnType<typeof setInterval> | null>(null);
|
const autoRefreshInterval = ref<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
const queueCount = computed(() => stateStore.count());
|
const hasQueueContent = computed(() => stateStore.count() > 0 || query.value.trim().length > 0);
|
||||||
const hasQueueContent = computed(() => queueCount.value > 0 || query.value.trim().length > 0);
|
|
||||||
const contentStyle = computed<'grid' | 'list'>(() =>
|
const contentStyle = computed<'grid' | 'list'>(() =>
|
||||||
isMobile.value ? 'grid' : display_style.value,
|
isMobile.value ? 'grid' : display_style.value,
|
||||||
);
|
);
|
||||||
|
|
@ -812,14 +811,6 @@ const thumbnailRatioClass = computed(() =>
|
||||||
thumbnail_ratio.value === 'is-16by9' ? 'aspect-video' : 'aspect-[3/1]',
|
thumbnail_ratio.value === 'is-16by9' ? 'aspect-video' : 'aspect-[3/1]',
|
||||||
);
|
);
|
||||||
|
|
||||||
const getTitle = (): string => {
|
|
||||||
if (!config.app.ui_update_title) {
|
|
||||||
return 'YTPTube';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `YTPTube: ( ${stateStore.count()}/${config.app.max_workers}:${config.app.max_workers_per_extractor} )`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshQueue = async (): Promise<void> => {
|
const refreshQueue = async (): Promise<void> => {
|
||||||
if (isRefreshing.value) {
|
if (isRefreshing.value) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -871,8 +862,6 @@ onMounted(async () => {
|
||||||
window.history.replaceState({}, '', url.toString());
|
window.history.replaceState({}, '', url.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
useHead({ title: getTitle() });
|
|
||||||
|
|
||||||
if (Object.keys(pendingDownloadFormItem.value).length > 0) {
|
if (Object.keys(pendingDownloadFormItem.value).length > 0) {
|
||||||
await toNewDownload(pendingDownloadFormItem.value);
|
await toNewDownload(pendingDownloadFormItem.value);
|
||||||
pendingDownloadFormItem.value = {};
|
pendingDownloadFormItem.value = {};
|
||||||
|
|
@ -891,18 +880,6 @@ watch(toggleFilter, () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
|
||||||
() => stateStore.queue,
|
|
||||||
() => {
|
|
||||||
if (!config.app.ui_update_title) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
useHead({ title: getTitle() });
|
|
||||||
},
|
|
||||||
{ deep: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => socket.isConnected,
|
() => socket.isConnected,
|
||||||
(connected) => {
|
(connected) => {
|
||||||
|
|
|
||||||
|
|
@ -497,8 +497,6 @@ onBeforeUnmount(() => {
|
||||||
scrollTimeout = null;
|
scrollTimeout = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useHead({ title: 'Logs' });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
2
ui/app/types/config.d.ts
vendored
2
ui/app/types/config.d.ts
vendored
|
|
@ -7,8 +7,6 @@ type AppConfig = {
|
||||||
download_path: string;
|
download_path: string;
|
||||||
/** Indicates if files should be removed after download */
|
/** Indicates if files should be removed after download */
|
||||||
remove_files: boolean;
|
remove_files: boolean;
|
||||||
/** Indicates if the UI should update the title with the current download status */
|
|
||||||
ui_update_title: boolean;
|
|
||||||
/** Output template for yt-dlp, e.g. "%(title)s.%(ext)s" */
|
/** Output template for yt-dlp, e.g. "%(title)s.%(ext)s" */
|
||||||
output_template: string;
|
output_template: string;
|
||||||
/** yt-dlp version, e.g. "2023.10.01" */
|
/** yt-dlp version, e.g. "2023.10.01" */
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import type { convert_args_response, Paginated } from '~/types/responses';
|
||||||
import type { StoreItem } from '~/types/store';
|
import type { StoreItem } from '~/types/store';
|
||||||
|
|
||||||
const AG_SEPARATOR = '.';
|
const AG_SEPARATOR = '.';
|
||||||
|
const APP_TITLE = 'YTPTube';
|
||||||
|
|
||||||
const separators = [
|
const separators = [
|
||||||
{ name: 'Comma', value: ',' },
|
{ name: 'Comma', value: ',' },
|
||||||
|
|
@ -971,7 +972,18 @@ const parse_api_error = async (json: unknown): Promise<string> => {
|
||||||
return 'Unknown error occurred';
|
return 'Unknown error occurred';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatPageTitle = (title?: string | null): string => {
|
||||||
|
const normalized = title?.trim();
|
||||||
|
|
||||||
|
if (!normalized || normalized === APP_TITLE) {
|
||||||
|
return APP_TITLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${normalized} | ${APP_TITLE}`;
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
APP_TITLE,
|
||||||
separators,
|
separators,
|
||||||
convertCliOptions,
|
convertCliOptions,
|
||||||
getSeparatorsName,
|
getSeparatorsName,
|
||||||
|
|
@ -1019,4 +1031,5 @@ export {
|
||||||
parse_list_response,
|
parse_list_response,
|
||||||
parse_api_response,
|
parse_api_response,
|
||||||
parse_api_error,
|
parse_api_error,
|
||||||
|
formatPageTitle,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue