mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Add estimated time remaining display and duration formatting to job progress cards
This commit is contained in:
@@ -35,6 +35,21 @@ def datetimeformat(value: float, fmt: str = "%Y-%m-%d %H:%M:%S") -> str:
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
return datetime.fromtimestamp(value).strftime(fmt)
|
return datetime.fromtimestamp(value).strftime(fmt)
|
||||||
|
|
||||||
|
@main_bp.app_template_filter("durationformat")
|
||||||
|
def durationformat(value: Optional[float]) -> str:
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
seconds = int(value)
|
||||||
|
if seconds < 60:
|
||||||
|
return f"{seconds}s"
|
||||||
|
minutes = seconds // 60
|
||||||
|
seconds = seconds % 60
|
||||||
|
if minutes < 60:
|
||||||
|
return f"{minutes}m {seconds}s"
|
||||||
|
hours = minutes // 60
|
||||||
|
minutes = minutes % 60
|
||||||
|
return f"{hours}h {minutes}m"
|
||||||
|
|
||||||
@main_bp.route("/")
|
@main_bp.route("/")
|
||||||
def index():
|
def index():
|
||||||
pending_id = request.args.get("pending_id")
|
pending_id = request.args.get("pending_id")
|
||||||
|
|||||||
+20
-9
@@ -148,13 +148,24 @@ class Job:
|
|||||||
generate_epub3: bool = False
|
generate_epub3: bool = False
|
||||||
speaker_analysis: Dict[str, Any] = field(default_factory=dict)
|
speaker_analysis: Dict[str, Any] = field(default_factory=dict)
|
||||||
speaker_analysis_threshold: int = 3
|
speaker_analysis_threshold: int = 3
|
||||||
analysis_requested: bool = False
|
|
||||||
speaker_voice_languages: List[str] = field(default_factory=list)
|
@property
|
||||||
applied_speaker_config: Optional[str] = None
|
def estimated_time_remaining(self) -> Optional[float]:
|
||||||
entity_summary: Dict[str, Any] = field(default_factory=dict)
|
"""
|
||||||
manual_overrides: List[Dict[str, Any]] = field(default_factory=list)
|
Returns the estimated seconds remaining based on current progress and elapsed time.
|
||||||
pronunciation_overrides: List[Dict[str, Any]] = field(default_factory=list)
|
Returns None if the job hasn't started, is finished, or progress is 0.
|
||||||
normalization_overrides: Dict[str, Any] = field(default_factory=dict)
|
"""
|
||||||
|
if self.status != JobStatus.RUNNING or not self.started_at or self.progress <= 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
elapsed = time.time() - self.started_at
|
||||||
|
if elapsed <= 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Estimate total time based on current progress
|
||||||
|
total_estimated = elapsed / self.progress
|
||||||
|
remaining = total_estimated - elapsed
|
||||||
|
return max(0.0, remaining)
|
||||||
|
|
||||||
def add_log(self, message: str, level: str = "info") -> None:
|
def add_log(self, message: str, level: str = "info") -> None:
|
||||||
entry = JobLog(timestamp=time.time(), message=message, level=level)
|
entry = JobLog(timestamp=time.time(), message=message, level=level)
|
||||||
@@ -1589,8 +1600,8 @@ def default_storage_root() -> Path:
|
|||||||
def build_service(
|
def build_service(
|
||||||
runner: Callable[[Job], None],
|
runner: Callable[[Job], None],
|
||||||
*,
|
*,
|
||||||
output_root: Optional[Path] = None,
|
output_root: Optional<Path] = None,
|
||||||
uploads_root: Optional[Path] = None,
|
uploads_root: Optional<Path] = None,
|
||||||
) -> ConversionService:
|
) -> ConversionService:
|
||||||
output_root = output_root or default_storage_root()
|
output_root = output_root or default_storage_root()
|
||||||
service = ConversionService(
|
service = ConversionService(
|
||||||
|
|||||||
@@ -1910,6 +1910,17 @@ button.step-indicator__item:focus-visible {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.job-card__progress-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-card__eta {
|
||||||
|
color: var(--muted);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
.job-card__footer {
|
.job-card__footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -26,7 +26,12 @@
|
|||||||
<div class="progress-bar" style="--progress: {{ progress_value }}%">
|
<div class="progress-bar" style="--progress: {{ progress_value }}%">
|
||||||
<div class="progress-bar__fill"></div>
|
<div class="progress-bar__fill"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="job-card__progress-meta">
|
||||||
<small>{{ progress_value }}% · {{ job.processed_characters }} / {{ job.total_characters or '—' }}</small>
|
<small>{{ progress_value }}% · {{ job.processed_characters }} / {{ job.total_characters or '—' }}</small>
|
||||||
|
{% if job.estimated_time_remaining %}
|
||||||
|
<small class="job-card__eta">~{{ job.estimated_time_remaining | durationformat }} remaining</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="job-card__footer">
|
<div class="job-card__footer">
|
||||||
<a class="button button--ghost" href="{{ url_for('jobs.job_detail', job_id=job.id) }}">Details</a>
|
<a class="button button--ghost" href="{{ url_for('jobs.job_detail', job_id=job.id) }}">Details</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user