fix: adapt to v0.85.0 changes

This commit is contained in:
ihmily
2026-05-13 15:04:10 +08:00
parent 42bc84e5a7
commit ffe0cc3f0c
14 changed files with 101 additions and 49 deletions

View File

@@ -105,7 +105,7 @@ async def handle_app_close(page: ft.Page, app, save_progress_overlay) -> None:
color=ft.Colors.GREY_500,
text_align=ft.TextAlign.CENTER,
),
padding=ft.padding.all(8),
padding=ft.Padding.all(8),
border_radius=5,
bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.BLUE_GREY),
)
@@ -154,7 +154,7 @@ async def handle_app_close(page: ft.Page, app, save_progress_overlay) -> None:
tight=True,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
),
padding=ft.padding.symmetric(horizontal=20, vertical=10),
padding=ft.Padding.symmetric(horizontal=20, vertical=10),
width=400 if page.platform.value != "macos" else None,
),
actions=close_confirm_actions,

View File

@@ -151,7 +151,7 @@ class RecordingCardManager:
on_click=lambda e, rec=recording: self.app.page.run_task(self.recording_card_on_click, e, rec),
bgcolor=self.get_card_background_color(recording),
border_radius=5,
border=ft.border.all(2, self.get_card_border_color(recording)),
border=ft.Border.all(2, self.get_card_border_color(recording)),
)
card = ft.Card(key=str(recording.rec_id), content=card_container)
@@ -239,7 +239,7 @@ class RecordingCardManager:
if recording_card["card"] and recording_card["card"].content:
recording_card["card"].content.bgcolor = self.get_card_background_color(recording)
recording_card["card"].content.border = ft.border.all(2, self.get_card_border_color(recording))
recording_card["card"].content.border = ft.Border.all(2, self.get_card_border_color(recording))
try:
self.app.page.update()
except (ft.FletPageDisconnectedException, AssertionError) as e:
@@ -401,6 +401,11 @@ class RecordingCardManager:
if not recording or recording.rec_id not in self.cards_obj: # Stop task if card is removed
break
# Skip update when not on recordings page (cards are detached from page tree)
current_page = getattr(self.app, "current_page", None)
if not current_page or getattr(current_page, "page_name", None) != "recordings":
continue
if recording.is_recording:
try:
duration_label = self.cards_obj[recording.rec_id]["duration_label"]

View File

@@ -329,7 +329,7 @@ class RecordingDialog:
ft.Container(
content=ft.Column(
[
ft.Container(margin=ft.margin.only(top=10)),
ft.Container(margin=ft.Margin.only(top=10)),
url_field,
streamer_name_field,
format_row,
@@ -347,7 +347,7 @@ class RecordingDialog:
scroll=ft.ScrollMode.AUTO,
)
),
ft.Container(content=batch_input, margin=ft.margin.only(top=15)),
ft.Container(content=batch_input, margin=ft.Margin.only(top=15)),
],
expand=True,
),

View File

@@ -1,4 +1,5 @@
import os
from datetime import datetime
from pathlib import Path
from urllib.parse import parse_qs, urlparse
@@ -55,8 +56,13 @@ class VideoPlayer:
async def open_in_browser(_):
self.app.page.launch_url(room_url)
async def take_screenshot(_):
await self._take_screenshot(video, video_source, is_file_path)
actions = [ft.TextButton(self._["close"], on_click=close_dialog)]
actions.insert(0, ft.TextButton(self._["screenshot"], on_click=take_screenshot))
if room_url:
actions.insert(0, ft.TextButton(self._["open_live_room_page"], on_click=open_in_browser))
if not is_file_path:
@@ -91,8 +97,8 @@ class VideoPlayer:
tight=True,
),
actions=[],
inset_padding=ft.padding.only(left=10, right=10, top=5, bottom=5),
content_padding=ft.padding.only(left=5, right=5, top=5, bottom=0),
inset_padding=ft.Padding.only(left=10, right=10, top=5, bottom=5),
content_padding=ft.Padding.only(left=5, right=5, top=5, bottom=0),
)
else:
dialog = ft.AlertDialog(
@@ -106,6 +112,41 @@ class VideoPlayer:
self.app.dialog_area.content = dialog
self.app.dialog_area.update()
async def _take_screenshot(self, video: ftv.Video, video_source: str, is_file_path: bool):
try:
image_bytes = await video.take_screenshot(format="image/png")
except Exception as e:
logger.error(f"Failed to take screenshot: {e}")
await self.app.snack_bar.show_snack_bar(self._["screenshot_failed"])
return
if not image_bytes:
await self.app.snack_bar.show_snack_bar(self._["screenshot_failed"])
return
try:
if is_file_path and video_source and os.path.isfile(video_source):
screenshot_dir = os.path.join(os.path.dirname(os.path.abspath(video_source)), "screenshots")
base_name = Path(video_source).stem
else:
save_root = self.app.settings.get_video_save_path()
screenshot_dir = os.path.join(save_root, "screenshots")
base_name = "screenshot"
os.makedirs(screenshot_dir, exist_ok=True)
filename = f"{base_name}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.png"
file_path = os.path.join(screenshot_dir, filename)
with open(file_path, "wb") as f:
f.write(image_bytes)
except Exception as e:
logger.error(f"Failed to save screenshot: {e}")
await self.app.snack_bar.show_snack_bar(self._["screenshot_failed"])
return
logger.info(f"Screenshot saved: {file_path}")
await self.app.snack_bar.show_snack_bar(
f"{self._['screenshot_success']}", bgcolor=ft.Colors.PRIMARY, duration=3000
)
async def preview_video(self, source: str, is_file_path: bool = True, room_url: str | None = None):
"""
Preview video

View File

@@ -42,7 +42,7 @@ class SaveProgressOverlay:
bgcolor="#FF5252",
shape=ft.RoundedRectangleBorder(radius=8),
elevation=0,
padding=ft.padding.symmetric(horizontal=20, vertical=10),
padding=ft.Padding.symmetric(horizontal=20, vertical=10),
),
tooltip=self._["force_close_tooltip"],
visible=False,
@@ -63,7 +63,7 @@ class SaveProgressOverlay:
self.content_container = ft.Container(
content=ft.Column(
[
ft.Container(content=self.progress_ring, margin=ft.margin.only(bottom=20)),
ft.Container(content=self.progress_ring, margin=ft.Margin.only(bottom=20)),
self.message_text,
ft.Container(height=25),
self.cancel_button,
@@ -76,7 +76,7 @@ class SaveProgressOverlay:
),
width=400,
height=280,
padding=ft.padding.all(30),
padding=ft.Padding.all(30),
alignment=ft.alignment.Alignment.CENTER,
bgcolor=ft.Colors.with_opacity(0.95, "#212121"),
border_radius=16,
@@ -91,7 +91,7 @@ class SaveProgressOverlay:
self.simple_container = ft.Container(
content=ft.Column(
[
ft.Container(content=self.simple_progress_ring, margin=ft.margin.only(bottom=15)),
ft.Container(content=self.simple_progress_ring, margin=ft.Margin.only(bottom=15)),
self.message_text,
],
alignment=ft.MainAxisAlignment.CENTER,
@@ -100,7 +100,7 @@ class SaveProgressOverlay:
),
width=300,
height=180,
padding=ft.padding.all(25),
padding=ft.Padding.all(25),
alignment=ft.alignment.Alignment.CENTER,
bgcolor=ft.Colors.with_opacity(0.95, "#212121"),
border_radius=16,

View File

@@ -46,7 +46,7 @@ class ShowSnackBar:
if not self.app.is_mobile:
snack_bar_width = 350
snack_bar.margin = ft.margin.only(left=self.app.page.width - snack_bar_width, top=0, right=10, bottom=10)
snack_bar.margin = ft.Margin.only(left=self.app.page.width - snack_bar_width, top=0, right=10, bottom=10)
snack_bar.open = True
self.app.snack_bar_area.content = snack_bar

View File

@@ -24,7 +24,7 @@ class SearchDialog(ft.AlertDialog):
super().__init__(
title=ft.Text(search_title, size=20, weight=ft.FontWeight.BOLD),
content_padding=ft.padding.only(left=20, top=15, right=20, bottom=20),
content_padding=ft.Padding.only(left=20, top=15, right=20, bottom=20),
)
self.query = ft.TextField(
hint_text=self._["search_keyword"],

View File

@@ -270,7 +270,7 @@ class AboutPage(PageBase):
expand=True,
),
padding=20,
margin=ft.margin.symmetric(0, 10),
margin=ft.Margin.symmetric(vertical=0, horizontal=10),
bgcolor=card_bg_color,
border_radius=15,
shadow=ft.BoxShadow(

View File

@@ -64,7 +64,7 @@ class HomePage(PageBase):
ft.Container(
content=logo,
alignment=ft.alignment.Alignment.CENTER,
margin=ft.margin.only(top=30, bottom=10),
margin=ft.Margin.only(top=30, bottom=10),
),
ft.Text(
f"{greeting}{self._['welcome']}",
@@ -94,7 +94,7 @@ class HomePage(PageBase):
spacing=10,
),
alignment=ft.alignment.Alignment.CENTER,
padding=ft.padding.only(bottom=20),
padding=ft.Padding.only(bottom=20),
)
def create_quick_action_area(self):
@@ -170,7 +170,7 @@ class HomePage(PageBase):
spacing=10,
),
alignment=ft.alignment.Alignment.CENTER,
padding=ft.padding.only(bottom=20),
padding=ft.Padding.only(bottom=20),
)
else:
return ft.Container(
@@ -199,7 +199,7 @@ class HomePage(PageBase):
alignment=ft.MainAxisAlignment.CENTER,
spacing=button_spacing,
),
margin=ft.margin.only(bottom=10),
margin=ft.Margin.only(bottom=10),
),
ft.Container(
content=ft.Row(
@@ -238,7 +238,7 @@ class HomePage(PageBase):
alignment=ft.MainAxisAlignment.CENTER,
),
alignment=ft.alignment.Alignment.CENTER,
padding=ft.padding.only(bottom=20),
padding=ft.Padding.only(bottom=20),
)
@staticmethod
@@ -259,7 +259,7 @@ class HomePage(PageBase):
),
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=10),
padding=ft.padding.all(15),
padding=ft.Padding.all(15),
bgcolor=color,
elevation=5,
),
@@ -295,15 +295,15 @@ class HomePage(PageBase):
else ft.Colors.WHITE70
),
),
margin=ft.margin.only(left=34),
margin=ft.Margin.only(left=34),
),
],
spacing=5,
),
padding=ft.padding.all(15),
padding=ft.Padding.all(15),
),
elevation=2,
margin=ft.margin.only(bottom=5),
margin=ft.Margin.only(bottom=5),
)
announcement_list = self.app.about.about_config["version_updates"][0]["announcement"][self.app.language_code]
@@ -341,12 +341,12 @@ class HomePage(PageBase):
controls=announcements,
spacing=5,
),
padding=ft.padding.only(top=5),
padding=ft.Padding.only(top=5),
),
],
spacing=5,
),
padding=ft.padding.only(left=20, right=20),
padding=ft.Padding.only(left=20, right=20),
)
def create_stats_area(self):
@@ -366,7 +366,7 @@ class HomePage(PageBase):
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
spacing=5,
),
padding=ft.padding.all(15),
padding=ft.Padding.all(15),
border_radius=10,
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
width=150,
@@ -435,14 +435,14 @@ class HomePage(PageBase):
controls=recent_recordings,
spacing=8,
),
padding=ft.padding.only(top=5),
padding=ft.Padding.only(top=5),
),
],
spacing=5,
alignment=ft.MainAxisAlignment.START,
horizontal_alignment=ft.CrossAxisAlignment.START,
),
padding=ft.padding.all(15),
padding=ft.Padding.all(15),
border_radius=10,
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
expand=True,
@@ -529,12 +529,12 @@ class HomePage(PageBase):
),
ft.Container(
content=stats_content,
padding=ft.padding.only(top=10),
padding=ft.Padding.only(top=10),
),
],
spacing=5,
),
padding=ft.padding.only(left=20, right=20),
padding=ft.Padding.only(left=20, right=20),
)
def create_features_area(self):
@@ -571,7 +571,7 @@ class HomePage(PageBase):
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
alignment=ft.MainAxisAlignment.CENTER,
),
padding=ft.padding.all(15),
padding=ft.Padding.all(15),
alignment=ft.alignment.Alignment.CENTER,
width=None if is_mobile else 220,
expand=is_mobile,
@@ -653,13 +653,13 @@ class HomePage(PageBase):
),
ft.Container(
content=feature_cards,
padding=ft.padding.only(top=10),
padding=ft.Padding.only(top=10),
),
],
horizontal_alignment=ft.CrossAxisAlignment.START,
),
alignment=ft.alignment.Alignment.BOTTOM_LEFT,
padding=ft.padding.only(left=20, right=20, bottom=30),
padding=ft.Padding.only(left=20, right=20, bottom=30),
)
async def on_start_recording_click(self, _):

View File

@@ -56,7 +56,7 @@ class LoginPage:
color="#ffffff",
bgcolor="#0078d4",
elevation=0,
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
animation_duration=300,
),
)
@@ -79,7 +79,7 @@ class LoginPage:
ft.Container(
content=self.logo,
alignment=ft.alignment.Alignment.CENTER,
margin=ft.margin.only(bottom=10),
margin=ft.Margin.only(bottom=10),
),
ft.Text(
"StreamCap",
@@ -100,7 +100,7 @@ class LoginPage:
self.password_field,
ft.Container(
content=self.error_text,
margin=ft.margin.only(top=10),
margin=ft.Margin.only(top=10),
alignment=ft.alignment.Alignment.CENTER,
),
ft.Container(height=20),

View File

@@ -194,7 +194,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "all" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
ft.Button(
@@ -204,7 +204,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "recording" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
ft.Button(
@@ -214,7 +214,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "living" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
ft.Button(
@@ -224,7 +224,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "offline" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
ft.Button(
@@ -234,7 +234,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "error" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
ft.Button(
@@ -244,7 +244,7 @@ class RecordingsPage(PageBase):
color=ft.Colors.WHITE if self.current_filter == "stopped" else None,
style=ft.ButtonStyle(
shape=ft.RoundedRectangleBorder(radius=5),
padding=ft.padding.symmetric(horizontal=10, vertical=4),
padding=ft.Padding.symmetric(horizontal=10, vertical=4),
),
),
]
@@ -270,7 +270,7 @@ class RecordingsPage(PageBase):
hover_color=ft.Colors.PRIMARY,
width=120,
text_size=14,
content_padding=ft.padding.only(top=8, bottom=8, left=10, right=10),
content_padding=ft.Padding.only(top=8, bottom=8, left=10, right=10),
border_radius=5,
border_color=ft.Colors.OUTLINE,
focused_border_color=ft.Colors.PRIMARY,

View File

@@ -1113,7 +1113,7 @@ class SettingsPage(PageBase):
ft.Text(label, text_align=ft.TextAlign.LEFT, weight=ft.FontWeight.BOLD),
ft.Container(
content=checkbox_grid,
margin=ft.margin.only(top=5, bottom=10),
margin=ft.Margin.only(top=5, bottom=10),
),
],
spacing=5,
@@ -1215,7 +1215,7 @@ class SettingsPage(PageBase):
ft.Text(label, text_align=ft.TextAlign.LEFT),
ft.Container(
content=control,
margin=ft.margin.only(top=5, bottom=10),
margin=ft.Margin.only(top=5, bottom=10),
expand=True,
width=float("inf"),
),

View File

@@ -456,7 +456,10 @@
"stream_source": "Stream Source",
"previewing": "Previewing",
"view_stream_source_now": "Accessing live stream source",
"unsupported_play_on_web": "Only MP4 files can be previewed on the web ⚠️ "
"unsupported_play_on_web": "Only MP4 files can be previewed on the web ⚠️ ",
"screenshot": "Screenshot",
"screenshot_success": "Screenshot saved",
"screenshot_failed": "Screenshot failed ⚠️"
},
"update": {
"new_version": "New Version {version} Available",

View File

@@ -456,7 +456,10 @@
"stream_source": "直播源",
"previewing":"正在预览",
"view_stream_source_now": "正在访问直播源",
"unsupported_play_on_web": "Web端只支持预览MP4文件 ⚠️"
"unsupported_play_on_web": "Web端只支持预览MP4文件 ⚠️",
"screenshot": "截图",
"screenshot_success": "截图已保存",
"screenshot_failed": "截图失败 ⚠️"
},
"update": {
"new_version": "发现新版本 {version}",