99 lines
3.8 KiB
Python
99 lines
3.8 KiB
Python
"""Открыть в браузере ссылки из TXT для скачивания через браузерную сессию."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import time
|
||
import webbrowser
|
||
from pathlib import Path
|
||
from urllib.parse import urlparse
|
||
|
||
|
||
def read_urls(txt_file: Path) -> list[str]:
|
||
"""Прочитать непустые HTTP/HTTPS-ссылки, кроме комментариев с #."""
|
||
urls: list[str] = []
|
||
with txt_file.open("r", encoding="utf-8-sig") as file:
|
||
for line_number, line in enumerate(file, start=1):
|
||
value = line.strip()
|
||
if not value or value.startswith("#"):
|
||
continue
|
||
if urlparse(value).scheme not in {"http", "https"}:
|
||
print(f"Строка {line_number} пропущена: это не HTTP/HTTPS-ссылка")
|
||
continue
|
||
urls.append(value)
|
||
return urls
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser(
|
||
description="Последовательно открыть ссылки из TXT в системном браузере"
|
||
)
|
||
parser.add_argument("txt_file", type=Path, help="TXT-файл: одна ссылка на строку")
|
||
parser.add_argument(
|
||
"--delay",
|
||
type=float,
|
||
default=0.2,
|
||
help="Пауза между ссылками в секундах (по умолчанию 1.5)",
|
||
)
|
||
parser.add_argument(
|
||
"--start",
|
||
type=int,
|
||
default=1,
|
||
help="Начать с указанного номера строки списка (по умолчанию 1)",
|
||
)
|
||
parser.add_argument(
|
||
"--limit",
|
||
type=int,
|
||
help="Открыть не больше указанного числа ссылок",
|
||
)
|
||
args = parser.parse_args()
|
||
|
||
if not args.txt_file.is_file():
|
||
parser.error(f"файл не найден: {args.txt_file}")
|
||
if args.delay < 0:
|
||
parser.error("--delay не может быть отрицательным")
|
||
if args.start < 1:
|
||
parser.error("--start должен быть не меньше 1")
|
||
if args.limit is not None and args.limit < 1:
|
||
parser.error("--limit должен быть не меньше 1")
|
||
|
||
all_urls = read_urls(args.txt_file)
|
||
urls = all_urls[args.start - 1 :]
|
||
if args.limit is not None:
|
||
urls = urls[: args.limit]
|
||
if not urls:
|
||
raise SystemExit("Нет ссылок для открытия.")
|
||
|
||
print(
|
||
f"Будет открыто ссылок: {len(urls)} из {len(all_urls)}. "
|
||
f"Начало с номера: {args.start}."
|
||
)
|
||
print(
|
||
"Браузер сохраняет оригинальные имена и расширения. "
|
||
"Не закрывайте его до завершения."
|
||
)
|
||
|
||
failed = 0
|
||
for offset, url in enumerate(urls):
|
||
number = args.start + offset
|
||
try:
|
||
# new=0 просит браузер использовать существующее окно. Для download-
|
||
# ответа браузер обычно начинает загрузку, не оставляя новую вкладку.
|
||
opened = webbrowser.open(url, new=0, autoraise=False)
|
||
if not opened:
|
||
raise RuntimeError("системный браузер не принял ссылку")
|
||
print(f"[{offset + 1}/{len(urls)}] Открыта ссылка №{number}")
|
||
except Exception as error:
|
||
failed += 1
|
||
print(f"[{offset + 1}/{len(urls)}] ОШИБКА №{number}: {error}")
|
||
if offset + 1 < len(urls):
|
||
time.sleep(args.delay)
|
||
|
||
print(f"Готово. Передано браузеру: {len(urls) - failed}, ошибок: {failed}.")
|
||
if failed:
|
||
raise SystemExit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|