Dateiendung angepasst
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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__":
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user