Dateiendung angepasst

This commit is contained in:
2026-04-10 16:24:58 +02:00
parent 5d603bded7
commit b3006978b3
3 changed files with 102 additions and 8 deletions

View File

@@ -41,12 +41,18 @@ mdlink .
## Interactive Redirect Rewrite ## Interactive Redirect Rewrite
When a Markdown link redirects, `mdlink` prompts: Step 1: When a Markdown link redirects, `mdlink` prompts:
```text ```text
Replace old URL with final URL? [y/N] Replace old URL with final URL? [y/N]
``` ```
Step 2: For `http://` Markdown links without redirect, `mdlink` can test an `https://` variant and prompt:
```text
Replace HTTP URL with HTTPS variant? [y/N]
```
Only confirmed links are updated. Only confirmed links are updated.
## Test File And Script ## Test File And Script

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import argparse import argparse
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import Optional
from rich.console import Console from rich.console import Console
from rich.table import Table from rich.table import Table
@@ -56,7 +57,51 @@ def _collect_redirects(records: list[LinkRecord], checks: dict[str, LinkCheckRes
return redirects return redirects
def _is_http_url(url: str) -> bool:
return url.startswith("http://")
def _to_https(url: str) -> str:
return "https://" + url[len("http://") :]
def _cached_check(
checker: LinkChecker,
cache: dict[str, LinkCheckResult],
url: str,
) -> LinkCheckResult:
result = cache.get(url)
if result is None:
result = checker.check(url)
cache[url] = result
return result
def _collect_https_candidates(
records: list[LinkRecord],
checks: dict[str, LinkCheckResult],
) -> list[tuple[LinkRecord, str]]:
candidates: list[tuple[LinkRecord, str]] = []
seen: set[tuple[Path, str]] = set()
for record in records:
if record.kind != "markdown":
continue
if not _is_http_url(record.url):
continue
original_check = checks.get(record.url)
if original_check and original_check.redirected:
continue
key = (record.file_path, record.url)
if key in seen:
continue
seen.add(key)
candidates.append((record, _to_https(record.url)))
return candidates
def _handle_rewrites( def _handle_rewrites(
records: list[LinkRecord],
checks: dict[str, LinkCheckResult],
redirects: list[tuple[LinkRecord, LinkCheckResult]], redirects: list[tuple[LinkRecord, LinkCheckResult]],
checker: LinkChecker, checker: LinkChecker,
editor: ASTMarkdownEditor, editor: ASTMarkdownEditor,
@@ -64,6 +109,10 @@ def _handle_rewrites(
) -> None: ) -> None:
replacements_by_file: dict[Path, dict[str, str]] = defaultdict(dict) replacements_by_file: dict[Path, dict[str, str]] = defaultdict(dict)
seen_pairs: set[tuple[Path, str, str]] = set() seen_pairs: set[tuple[Path, str, str]] = set()
check_cache: dict[str, LinkCheckResult] = {}
if redirects:
console.print("\n[bold]Redirect replacements[/bold]")
for record, result in redirects: for record, result in redirects:
if record.kind != "markdown": if record.kind != "markdown":
@@ -82,7 +131,7 @@ def _handle_rewrites(
if answer != "y": if answer != "y":
continue continue
verification = checker.check(final_url) verification = _cached_check(checker=checker, cache=check_cache, url=final_url)
if verification.status_code != 200: if verification.status_code != 200:
console.print( console.print(
f"[red]Skip:[/red] final URL no longer valid ({verification.status_code or verification.error})" f"[red]Skip:[/red] final URL no longer valid ({verification.status_code or verification.error})"
@@ -90,6 +139,39 @@ def _handle_rewrites(
continue continue
replacements_by_file[record.file_path][record.url] = final_url replacements_by_file[record.file_path][record.url] = final_url
https_candidates = _collect_https_candidates(records=records, checks=checks)
if https_candidates:
console.print("\n[bold]HTTPS upgrade candidates[/bold]")
for record, https_url in https_candidates:
if replacements_by_file[record.file_path].get(record.url):
continue
https_check = _cached_check(checker=checker, cache=check_cache, url=https_url)
if https_check.status_code != 200:
continue
final_url: Optional[str] = https_check.final_url or https_url
if final_url == record.url:
continue
pair = (record.file_path, record.url, final_url)
if pair in seen_pairs:
continue
seen_pairs.add(pair)
console.print(f"\n[cyan]{record.file_path}:{record.line}[/cyan]")
console.print(f"[yellow]{record.url}[/yellow] -> [green]{final_url}[/green]")
answer = console.input("Replace HTTP URL with HTTPS variant? [y/N] ").strip().lower()
if answer != "y":
continue
verification = _cached_check(checker=checker, cache=check_cache, url=final_url)
if verification.status_code != 200:
console.print(
f"[red]Skip:[/red] HTTPS URL no longer valid ({verification.status_code or verification.error})"
)
continue
replacements_by_file[record.file_path][record.url] = final_url
for file_path, replacements in replacements_by_file.items(): for file_path, replacements in replacements_by_file.items():
content = file_path.read_text(encoding="utf-8") content = file_path.read_text(encoding="utf-8")
updated = editor.replace_links(content, replacements) updated = editor.replace_links(content, replacements)
@@ -118,9 +200,15 @@ def main() -> None:
console.print("No non-200 links found.") console.print("No non-200 links found.")
redirects = _collect_redirects(records, checks) redirects = _collect_redirects(records, checks)
if redirects: editor = ASTMarkdownEditor()
editor = ASTMarkdownEditor() _handle_rewrites(
_handle_rewrites(redirects=redirects, checker=checker, editor=editor, console=console) records=records,
checks=checks,
redirects=redirects,
checker=checker,
editor=editor,
console=console,
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -5,7 +5,7 @@ from typing import Iterable, Iterator
from urllib.parse import urlparse from urllib.parse import urlparse
MARKDOWN_EXTENSIONS = {".md"} MARKDOWN_EXTENSIONS = {".md", ".markdown", ".mdown", ".mkd"}
def iter_markdown_files(target: Path) -> Iterator[Path]: def iter_markdown_files(target: Path) -> Iterator[Path]:
@@ -13,8 +13,8 @@ def iter_markdown_files(target: Path) -> Iterator[Path]:
if target.suffix.lower() in MARKDOWN_EXTENSIONS: if target.suffix.lower() in MARKDOWN_EXTENSIONS:
yield target yield target
return return
for path in sorted(target.rglob("*.md")): for path in sorted(target.rglob("*")):
if path.is_file(): if path.is_file() and path.suffix.lower() in MARKDOWN_EXTENSIONS:
yield path yield path