ytptube/app/features/core/migration.py
2026-05-28 19:07:11 +03:00

71 lines
1.9 KiB
Python

from __future__ import annotations
import abc
import time
from pathlib import Path
from typing import TYPE_CHECKING
from app.library.log import get_logger
if TYPE_CHECKING:
from app.library.config import Config
LOG = get_logger()
class Migration(abc.ABC):
name: str = ""
def __init__(self, config: Config):
self._migrated_dir: Path = Path(config.config_path) / "migrated"
async def run(self) -> bool:
if not await self.should_run():
return False
self._migrated_dir.mkdir(parents=True, exist_ok=True)
try:
await self.migrate()
except Exception as exc:
LOG.exception(
"Feature migration '%s' failed.",
self.name,
extra={"feature": self.name, "exception_type": type(exc).__name__},
)
return False
return True
@abc.abstractmethod
async def should_run(self) -> bool:
raise NotImplementedError
@abc.abstractmethod
async def migrate(self) -> None:
raise NotImplementedError
async def _move_file(self, source: Path) -> Path:
destination: Path = self._migrated_dir / source.name
if destination.exists():
timestamp = int(time.time())
destination: Path = self._migrated_dir / f"{source.stem}_{timestamp}{source.suffix}"
source.rename(destination)
return destination
def _unique_name(self, name: str, seen_names: dict[str, int]) -> str:
base = name
count = seen_names.get(base, 0)
if count == 0:
seen_names[base] = 1
return base
suffix = count + 1
new_name = f"{base} ({suffix})"
while new_name in seen_names:
suffix += 1
new_name = f"{base} ({suffix})"
seen_names[base] = suffix
seen_names[new_name] = 1
return new_name