refactor: remove ui_update_title and standardize page title

This commit is contained in:
arabcoders 2026-04-22 20:55:29 +03:00
parent c2cb5527f5
commit 29777ff928
13 changed files with 37 additions and 55 deletions

1
FAQ.md
View file

@ -30,7 +30,6 @@ or the `environment:` section in `compose.yaml` file.
| YTP_DEBUG | Whether to turn on debug mode | `false` |
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
| 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_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
| YTP_PICTURES_BACKENDS | A comma separated list of pictures urls to use | `(not_set)` |

View file

@ -129,9 +129,6 @@ class Config(metaclass=Singleton):
apprise_config: str = "{config_path}{os_sep}apprise.yml"
"""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 = ""
"""The pip packages to install."""
@ -284,7 +281,6 @@ class Config(metaclass=Singleton):
"access_log",
"remove_files",
"ignore_ui",
"ui_update_title",
"pip_ignore_updates",
"file_logging",
"console_enabled",
@ -308,7 +304,6 @@ class Config(metaclass=Singleton):
"output_template",
"started",
"remove_files",
"ui_update_title",
"max_workers",
"max_workers_per_extractor",
"default_preset",

View file

@ -285,7 +285,7 @@
import { useStorage } from '@vueuse/core';
import { watch } from 'vue';
import Hls from 'hls.js';
import { disableOpacity, enableOpacity } from '~/utils';
import { disableOpacity, enableOpacity, formatPageTitle } from '~/utils';
import { useKeyboardShortcuts } from '~/composables/useKeyboardShortcuts';
import type { StoreItem } from '~/types/store';
@ -319,6 +319,8 @@ const havePoster = ref(false);
const showHelp = ref(false);
const usingHls = ref(false);
useHead(() => (title.value ? { title: formatPageTitle(`Playing: ${title.value}`) } : {}));
let unbindMediaSessionListeners: null | (() => void) = null;
let hls: Hls | null = null;
let cleanupKeyboardShortcuts: null | (() => void) = null;
@ -679,10 +681,6 @@ onBeforeUnmount(() => {
unbindMediaSessionListeners = null;
}
if (title.value) {
window.document.title = 'YTPTube';
}
if (!video.value) {
return;
}
@ -706,10 +704,6 @@ const prepareVideoPlayer = () => {
applyMediaSessionMetadata();
if (title.value) {
window.document.title = `YTPTube - Playing: ${title.value}`;
}
if (!video.value) {
return;
}

View file

@ -15,7 +15,6 @@ const state = reactive<ConfigState>({
app: {
download_path: '/downloads',
remove_files: false,
ui_update_title: true,
output_template: '',
ytdlp_version: '',
max_workers: 20,

View file

@ -497,6 +497,7 @@ import { useStorage } from '@vueuse/core';
import moment from 'moment';
import { useMediaQuery } from '~/composables/useMediaQuery';
import type { toastPosition } from '~/composables/useNotification';
import { formatPageTitle } from '~/utils';
import type { YTDLPOption } from '~/types/ytdlp';
import { useDialog } from '~/composables/useDialog';
import Dialog from '~/components/Dialog.vue';
@ -750,10 +751,17 @@ const sidebarItems = computed<
.filter((section) => section.items.some((group) => group.length > 0));
});
const pageTitle = computed(() => {
const match = getActiveNavItem(route, navigationAvailability.value);
return match?.navbarTitle || match?.label || 'YTPTube';
});
const activeNavItem = computed(() => getActiveNavItem(route, navigationAvailability.value));
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(
() =>

View file

@ -527,6 +527,7 @@ import moment from 'moment';
import { useStorage } from '@vueuse/core';
import type { DropdownMenuItem } from '@nuxt/ui';
import type { FileItem } from '~/types/filebrowser';
import { formatPageTitle } from '~/utils';
import { requirePageShell } from '~/utils/topLevelNavigation';
const route = useRoute();
@ -559,6 +560,7 @@ const pageShell = requirePageShell('files');
const hasItems = computed(() => filteredItems.value.length > 0);
const hasSelected = computed(() => selectedElms.value.length > 0);
const displayedItemPaths = computed(() => filteredItems.value.map((item) => item.path));
const browserPageTitle = computed(() => `File Browser: /${sTrim(browserPath.value || '/', '/')}`);
const currentDirectoryName = computed(
() => 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);
};
useHead(() => ({
title: formatPageTitle(decodeURIComponent(browserPageTitle.value)),
}));
const updateUrl = (dir: string, page?: number): void => {
const normalizedDir = dir.replace(/^\/+/, '').replace(/\/+$/, '');
const displayDir = normalizedDir ? normalizedDir : '/';
@ -762,8 +768,6 @@ const updateUrl = (dir: string, page?: number): void => {
if (fullUrl !== window.location.href) {
history.replaceState({ path: normalizedDir || '/', title }, title, stateUrl);
}
useHead({ title: decodeURIComponent(title) });
};
const handleClick = (item: FileItem): void => {

View file

@ -193,8 +193,6 @@ const toast = useNotification();
const config = useYtpConfig();
const pageShell = requirePageShell('changelog');
useHead({ title: 'CHANGELOG' });
const PROJECT = 'ytptube';
const REPO = `https://github.com/arabcoders/${PROJECT}`;
const REPO_URL = `https://arabcoders.github.io/${PROJECT}/CHANGELOG.json?version={version}`;

View file

@ -327,8 +327,6 @@ import type { AutoCompleteOptions } from '~/types/autocomplete';
import { disableOpacity, enableOpacity } from '~/utils';
import { requirePageShell } from '~/utils/topLevelNavigation';
useHead({ title: 'Console' });
const MAX_HISTORY_ITEMS = 50;
const ACTIVE_SESSION_STATUSES = ['starting', 'running', 'reconnecting'];

View file

@ -49,6 +49,7 @@
<script setup lang="ts">
import Markdown from '~/components/Markdown.vue';
import { DOCS_ENTRIES, getDocsEntryBySlug } from '~/composables/useDocs';
import { formatPageTitle } from '~/utils';
import { requirePageShell } from '~/utils/topLevelNavigation';
const route = useRoute();
@ -71,7 +72,7 @@ const docEntry = computed(() => {
const pageShell = computed(() => requirePageShell(docEntry.value.id));
useHead(() => ({
title: docEntry.value.title,
title: formatPageTitle(docEntry.value.title),
}));
const pageCardUi = {

View file

@ -781,8 +781,7 @@ const embed_url = ref('');
const isRefreshing = ref(false);
const autoRefreshInterval = ref<ReturnType<typeof setInterval> | null>(null);
const queueCount = computed(() => stateStore.count());
const hasQueueContent = computed(() => queueCount.value > 0 || query.value.trim().length > 0);
const hasQueueContent = computed(() => stateStore.count() > 0 || query.value.trim().length > 0);
const contentStyle = computed<'grid' | 'list'>(() =>
isMobile.value ? 'grid' : display_style.value,
);
@ -812,14 +811,6 @@ const thumbnailRatioClass = computed(() =>
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> => {
if (isRefreshing.value) {
return;
@ -871,8 +862,6 @@ onMounted(async () => {
window.history.replaceState({}, '', url.toString());
}
useHead({ title: getTitle() });
if (Object.keys(pendingDownloadFormItem.value).length > 0) {
await toNewDownload(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(
() => socket.isConnected,
(connected) => {

View file

@ -497,8 +497,6 @@ onBeforeUnmount(() => {
scrollTimeout = null;
}
});
useHead({ title: 'Logs' });
</script>
<style scoped>

View file

@ -7,8 +7,6 @@ type AppConfig = {
download_path: string;
/** Indicates if files should be removed after download */
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: string;
/** yt-dlp version, e.g. "2023.10.01" */

View file

@ -3,6 +3,7 @@ import type { convert_args_response, Paginated } from '~/types/responses';
import type { StoreItem } from '~/types/store';
const AG_SEPARATOR = '.';
const APP_TITLE = 'YTPTube';
const separators = [
{ name: 'Comma', value: ',' },
@ -971,7 +972,18 @@ const parse_api_error = async (json: unknown): Promise<string> => {
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 {
APP_TITLE,
separators,
convertCliOptions,
getSeparatorsName,
@ -1019,4 +1031,5 @@ export {
parse_list_response,
parse_api_response,
parse_api_error,
formatPageTitle,
};