Skip to content

fix: QA issues #28-35 — SEO titles, favicon, accessibility, security headers#38

Merged
ralyodio merged 2 commits intomasterfrom
fix/qa-issues-28-35
Mar 3, 2026
Merged

fix: QA issues #28-35 — SEO titles, favicon, accessibility, security headers#38
ralyodio merged 2 commits intomasterfrom
fix/qa-issues-28-35

Conversation

@ralyodio
Copy link
Contributor

@ralyodio ralyodio commented Mar 3, 2026

Summary

Addresses QA issues #28-35 reported by Gendolf.

Fixed

#30 — favicon.ico returns 404

  • Added src/app/favicon.ico (Next.js app router convention)

#32 — Duplicate X-Frame-Options headers

  • Changed app-level header from DENY to SAMEORIGIN to match nginx config

#34 — user-scalable=no blocks accessibility zoom

  • Changed userScalable: falsetrue, maximumScale: 15
  • WCAG 2.1 SC 1.4.4 compliance

#35 — All pages share identical <title> tag

  • Added layout.tsx with unique metadata for: Movies, TV Shows, Music, Books, Search, Find Torrents, Trending, DHT Torrents

#31 — /live-tv silently redirects to login

  • Added reason=live-tv query param on redirect
  • Login page shows explanatory banner: "Sign in to access Live TV"

Investigated (no code fix needed)

#28 — Search pages return blank content

  • /search, /find-torrents: blank by design until user enters a query
  • /dht: renders content (verified)

#29 — Category pages show zero content to unauthenticated users

  • Browse API (/api/browse) works without auth (verified: returns 773 movies)
  • Category pages render correctly for unauthenticated users

Needs separate investigation

#33 — /trending page has no content

  • Depends on TheTVDB API which may be timing out or misconfigured
  • Trending API route exists but returns empty — needs TheTVDB API key/config debugging

Testing

  • Build passes ✅
  • All 2836 tests pass ✅

Closes #30, #31, #32, #34, #35

#30: Add favicon.ico (Next.js app router convention)
#32: Change X-Frame-Options from DENY to SAMEORIGIN (match nginx, avoid duplicate)
#34: Enable pinch-to-zoom (userScalable: true, maximumScale: 5) — WCAG 2.1 compliance
#35: Add page-specific <title> tags via layout.tsx for movies, tvshows, music, books, search, find-torrents, trending, dht
#31: Add reason message on login page when redirected from live-tv

#28, #29: Browse API works without auth — category pages render content for unauthenticated users (verified). Search pages require a query parameter by design.
#33: Trending page depends on TheTVDB API — separate investigation needed.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a set of QA-reported SEO/accessibility/header/polish issues in the Next.js App Router app by adding per-route metadata, fixing the /favicon.ico 404, adjusting viewport zoom settings, and improving the Live TV login redirect UX.

Changes:

  • Added route-segment layout.tsx files to provide unique page metadata (titles/descriptions) for several top-level routes.
  • Added src/app/favicon.ico to satisfy Next.js App Router favicon convention and prevent 404s.
  • Updated security headers and viewport settings, and added a login banner for the /live-tv auth redirect reason.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
next.config.ts Changes X-Frame-Options header value to address duplicate/conflicting headers.
src/app/layout.tsx Updates viewport to allow user zoom (accessibility).
src/app/favicon.ico Adds favicon asset so /favicon.ico no longer 404s.
src/app/live-tv/page.tsx Adds reason=live-tv to login redirect.
src/app/login/page.tsx Displays an explanatory banner when redirected from Live TV.
src/app/movies/layout.tsx Adds segment metadata for Movies route.
src/app/music/layout.tsx Adds segment metadata for Music route.
src/app/tvshows/layout.tsx Adds segment metadata for TV Shows route.
src/app/books/layout.tsx Adds segment metadata for Books route.
src/app/search/layout.tsx Adds segment metadata for Search route.
src/app/find-torrents/layout.tsx Adds segment metadata for Find Torrents route.
src/app/dht/layout.tsx Adds segment metadata for DHT route.
src/app/trending/layout.tsx Adds segment metadata for Trending route.
src/app/movies/page.tsx Minor formatting-only change (blank line).
src/app/music/page.tsx Minor formatting-only change (blank line).
src/app/tvshows/page.tsx Minor formatting-only change (blank line).
src/app/books/page.tsx Minor formatting-only change (blank line).
src/app/search/page.tsx Minor formatting-only change (blank line).
src/app/find-torrents/page.tsx Minor formatting-only change (blank line).
src/app/dht/page.tsx Minor formatting-only change (blank line).
src/app/trending/page.tsx Minor formatting-only change (blank line).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Find Torrents — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Find Torrents — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Find Torrents") or set an absolute title to bypass the template.

Suggested change
title: 'Find Torrents — BitTorrented',
title: 'Find Torrents',

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'DHT Torrents — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "DHT Torrents — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "DHT Torrents") or set an absolute title to bypass the template.

Suggested change
title: 'DHT Torrents — BitTorrented',
title: 'DHT Torrents',

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +61
{/* Reason message for redirects */}
{typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('reason') === 'live-tv' && (
<div className="mb-4 rounded-lg border border-accent-primary/30 bg-accent-primary/10 px-4 py-3 text-center text-sm text-text-primary">
Sign in to access Live TV — add your IPTV playlists and stream live channels.
</div>
)}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is a client component page that is still SSR'd, gating the banner on typeof window !== 'undefined' means the server render will never include the banner, but the client render can include it for ?reason=live-tv. That can trigger hydration mismatch warnings and causes a layout shift. Prefer deriving reason from searchParams in a server wrapper (and pass it as a prop), or render the banner only after mount via state set in useEffect (so the first client render matches SSR).

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Movies — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Movies — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Movies") or set an absolute title to bypass the template.

Suggested change
title: 'Movies — BitTorrented',
title: 'Movies',

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'TV Shows — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "TV Shows — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "TV Shows") or set an absolute title to bypass the template.

Suggested change
title: 'TV Shows — BitTorrented',
title: 'TV Shows',

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Search — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Search — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Search") or set an absolute title to bypass the template.

Suggested change
title: 'Search — BitTorrented',
title: 'Search',

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +6
export const metadata: Metadata = {
title: 'Trending — BitTorrented',
description: 'Browse Trending on BitTorrented',
};
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Trending — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Trending") or set an absolute title to bypass the template.

Copilot uses AI. Check for mistakes.
next.config.ts Outdated
Comment on lines 41 to 45
},
{
key: 'X-Frame-Options',
value: 'DENY',
value: 'SAMEORIGIN',
},
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes X-Frame-Options to SAMEORIGIN, but the repo docs show an example reverse-proxy header config setting X-Frame-Options DENY (docs/dht-crawler-prd.md). If production still sets DENY at the edge, this will reintroduce the original problem (duplicate/conflicting X-Frame-Options headers). Please confirm what the proxy actually sends and either align the value or remove the app-level X-Frame-Options header so there’s only a single source of truth.

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Music — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Music — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Music") or set an absolute title to bypass the template.

Suggested change
title: 'Music — BitTorrented',
title: 'Music',

Copilot uses AI. Check for mistakes.
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Books — BitTorrented',
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These segment titles include the site name ("— BitTorrented"), but the root metadata defines a title.template of %s | BitTorrented (src/app/layout.tsx). Next.js will apply that template to nested string titles, producing a duplicated site name (e.g., "Books — BitTorrented | BitTorrented"). Use a bare segment title (e.g., "Books") or set an absolute title to bypass the template.

Suggested change
title: 'Books — BitTorrented',
title: 'Books',

Copilot uses AI. Check for mistakes.
…ptions

1-2, 4-7, 9-10: Remove '— BitTorrented' from layout titles (root template adds it)
3: Fix login reason banner hydration mismatch - use useEffect + state
8: Remove app-level X-Frame-Options entirely (nginx is single source of truth)
@ralyodio ralyodio merged commit 3045e4b into master Mar 3, 2026
6 checks passed
@ralyodio ralyodio deleted the fix/qa-issues-28-35 branch March 3, 2026 02:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

favicon.ico returns 404

2 participants