@@ -40,7 +40,6 @@ from PySide6.QtWidgets import (
QSizePolicy ,
QSpinBox ,
QSplitter ,
QStackedWidget ,
QTableWidget ,
QTableWidgetItem ,
QTabWidget ,
@@ -369,7 +368,6 @@ def dialog(title, parent):
class AccountCard ( QFrame ) :
selected = Signal ( object )
context_requested = Signal ( object , object )
checked = Signal ( str , bool )
expand_requested = Signal ( )
def __init__ (
@@ -379,7 +377,6 @@ class AccountCard(QFrame):
meta ,
status ,
color ,
checkable = False ,
expandable = False ,
parent = None ,
) :
@@ -405,14 +402,6 @@ class AccountCard(QFrame):
layout . addWidget ( self . toggle_button )
self . set_expandable ( expandable )
self . check_box = QCheckBox ( ) if checkable else None
if self . check_box is not None :
self . check_box . setToolTip ( " 勾选后可批量启动该大号 " )
self . check_box . toggled . connect (
lambda checked : self . checked . emit ( self . account_id , checked )
)
layout . addWidget ( self . check_box )
self . status_dot = QLabel ( " ● " )
self . status_dot . setStyleSheet ( f " color: { color } ; font-size: 15px; " )
layout . addWidget ( self . status_dot )
@@ -460,7 +449,7 @@ class AccountCard(QFrame):
return True
if (
event . button ( ) == Qt . MouseButton . LeftButton
and watched not in ( self . check_box , self . toggle_button )
and watched is not self . toggle_button
and self . account_id is not None
) :
self . selected . emit ( self . account_id )
@@ -543,7 +532,6 @@ class AccountCardTree(QScrollArea):
self . layout_box . addStretch ( )
self . setWidget ( self . host )
self . cards = { }
self . groups = { }
self . nodes = { }
self . _selected_id = None
self . viewport ( ) . installEventFilter ( self )
@@ -563,10 +551,9 @@ class AccountCardTree(QScrollArea):
card . selected . connect ( self . _select )
card . context_requested . connect ( self . context_menu_requested )
def set_accounts ( self , accounts , display , checked , selected_id ) :
def set_accounts ( self , accounts , display , selected_id ) :
self . _clear ( )
self . cards = { }
self . groups = { }
self . nodes = { }
self . _selected_id = selected_id
main_nodes = { }
@@ -576,16 +563,12 @@ class AccountCardTree(QScrollArea):
card = AccountCard (
account [ " id " ] ,
* * display [ account [ " id " ] ] ,
checkable = True ,
parent = self . host ,
)
if card . check_box is not None :
card . check_box . setChecked ( account [ " id " ] in checked )
self . _connect ( card )
node = AccountTreeNode ( card , self . host )
self . _add_root ( node )
self . cards [ account [ " id " ] ] = card
self . groups [ account [ " id " ] ] = card
self . nodes [ account [ " id " ] ] = node
main_nodes [ account [ " id " ] ] = node
@@ -637,13 +620,6 @@ class AccountCardTree(QScrollArea):
def set_current_id ( self , account_id ) :
self . _select ( account_id )
def checked_groups ( self ) :
return [
account_id
for account_id , card in self . groups . items ( )
if card . check_box is not None and card . check_box . isChecked ( )
]
def eventFilter ( self , watched , event ) :
if watched is self . viewport ( ) and event . type ( ) == QEvent . Type . ContextMenu :
self . context_menu_requested . emit ( None , event . globalPos ( ) )
@@ -664,7 +640,6 @@ class Window(QMainWindow):
" browser " : { } ,
" browser_overrides " : { } ,
}
self . selection_loaded = False
self . allow_close = False
self . quitting = False
self . signature = None
@@ -675,8 +650,6 @@ class Window(QMainWindow):
self . detail_account_id = None
self . history_payload = None
self . works_payload = None
self . exception_tasks = [ ]
self . exception_names = { }
self . work_cover_cache = Path ( self . backend . root ) / " work_covers "
self . network = QNetworkAccessManager ( self )
self . setWindowTitle ( " 抖音账号助手 · 自动操作默认关闭 " )
@@ -684,40 +657,43 @@ class Window(QMainWindow):
central = QWidget ( )
self . setCentralWidget ( central )
layout = QVBoxLayout ( central )
note = QLabel (
" 左侧账号树只显示别名、名称和状态;点击账号查看详情,账号操作请使用右键菜单。 "
" 恢复选择不会启动业务,启动前会先检查旧 pending 任务。 "
)
note . setWordWrap ( True )
layout . addWidget ( note )
row = QHBoxLayout ( )
layout . addLayout ( row )
self . buttons = [ ]
for text , handler in [
( " 添加大 号 " , lambda : self . add ( " main " ) ) ,
( " 添加小号 " , lambda : self . add ( " worker " ) ) ,
( " 恢复上次选择 " , self . restore_selection ) ,
( " 启动勾选组 " , self . start_groups ) ,
( " 添加账 号 " , lambda : self . add ( " main " ) ) ,
( " 停止全部 " , lambda : self . send ( " stop " ) ) ,
] :
button = self . button ( row , text , handler )
if text == " 停止全部 " :
self . stop_button = button
self . exception_button = QPushButton ( " 异常任务 " )
self . exception_button . clicked . connect ( self . show_exception_tasks )
row . addWidget ( self . exception_button )
self . button ( row , " 设置 " , lambda : self . browser_settings ( False ) )
row . addStretch ( )
self . workspace_stack = QStackedWidget ( )
accounts = QWidget ( )
account_layout = QHBoxLayout ( accounts )
splitter = QSplitter ( Qt . Orientation . Horizontal )
splitter . setHandleWidth ( 8 )
splitter . setChildrenCollapsible ( False )
splitter . setStyleSheet (
" QSplitter::handle:horizontal { background: #d9dde5; margin: 4px 0; } "
" QSplitter::handle:horizontal:hover { background: #1677ff; } "
)
account_layout . addWidget ( splitter )
left = QWidget ( )
left . setObjectName ( " accountTreePane " )
left . setStyleSheet (
" #accountTreePane { "
" background: #f5f7fa; border: 1px solid #e5e7eb; border-radius: 8px; "
" } "
)
left_layout = QVBoxLayout ( left )
left_layout . setContentsMargins ( 8 , 8 , 8 , 8 )
self . card_table = AccountCardTree ( )
self . card_table . setMinimumWidth ( 320 )
self . card_table . setStyleSheet ( " background: transparent; border: 0; " )
self . card_table . viewport ( ) . setStyleSheet ( " background: transparent; " )
self . card_table . host . setStyleSheet ( " background: transparent; " )
self . card_table . context_menu_requested . connect ( self . account_menu )
self . card_table . selection_changed . connect ( self . show_profile )
left_layout . addWidget ( self . card_table )
@@ -876,24 +852,30 @@ class Window(QMainWindow):
works_select . addStretch ( )
main_works_layout . addLayout ( works_select )
exception_tab = self . _build_exception_tab ( )
self . detail_tabs . addTab ( main_history_tab , " 历史事件 " )
self . detail_tabs . addTab ( main_works_tab , " 作品列表 " )
self . detail_tabs . addTab ( exception_tab , " 异常任务 " )
right_layout . addWidget ( self . detail_tabs , 1 )
splitter . addWidget ( right )
splitter . setStretchFactor ( 0 , 2 )
splitter . setStretchFactor ( 1 , 5 )
splitter . setSizes ( [ 390 , 840 ] )
self . account_page = accounts
self . workspace_stack . addWidget ( accounts )
self . exception_page = self . _build_exception_page ( )
self . workspace_stack . addWidget ( self . exception_page )
splitter . setStretchFactor ( 0 , 3 )
splitter . setStretchFactor ( 1 , 7 )
try :
splitter . setSizes ( [ int ( self . width ( ) * 0.3 ) , int ( self . width ( ) * 0.7 ) ] )
except ( TypeError , ValueError ) :
splitter . setStretchFactor ( 0 , 3 )
splitter . setStretchFactor ( 1 , 7 )
splitter . handle ( 1 ) . setCursor ( Qt . CursorShape . SplitHCursor )
splitter . handle ( 1 ) . setToolTip ( " 拖动调整左侧账号树宽度 " )
logs = QWidget ( )
logs_layout = QVBoxLayout ( logs )
self . log_info = QLabel ( " 正在加载日志;按天保存完整记录,界面仅保留最近 2000 条 " )
self . log_info . setWordWrap ( True )
logs_layout . addWidget ( self . log_info )
controls = QHBoxLayout ( )
self . log_info = QLabel ( " 正在加载日志;按天保存完整记录,界面仅保留最近 2000 条 " )
self . log_info . setWordWrap ( False )
self . log_info . setSizePolicy (
QSizePolicy . Policy . Expanding , QSizePolicy . Policy . Preferred
)
controls . addWidget ( self . log_info , 1 )
self . auto_scroll = QCheckBox ( " 自动滚动到最新记录 " )
self . auto_scroll . setChecked ( True )
controls . addWidget ( self . auto_scroll )
@@ -907,7 +889,7 @@ class Window(QMainWindow):
self . log . setMaximumBlockCount ( UI_LIMIT )
logs_layout . addWidget ( self . log )
vertical = QSplitter ( Qt . Orientation . Vertical )
vertical . addWidget ( self . workspace_stack )
vertical . addWidget ( accounts )
vertical . addWidget ( logs )
vertical . setStretchFactor ( 0 , 5 )
vertical . setStretchFactor ( 1 , 2 )
@@ -1043,37 +1025,20 @@ class Window(QMainWindow):
self . works_table . setDisabled ( checked )
self . works_card_area . setDisabled ( checked )
def _build_exception_page ( self ) :
page = QWidget ( )
layout = QVBoxLayout ( page )
heading = QHBoxLayout ( )
heading . addWidget ( QLabel ( " 异常任务(仅显示 unknown,只读) " ) )
back = QPushButton ( " 返回账号工作台 " )
back . clicked . connect (
lambda : self . workspace_stack . setCurrentWidget ( self . account_page )
)
heading . addWidget ( back )
layout . addLayout ( heading )
filters = QHBoxLayout ( )
filters . addWidget ( QLabel ( " 原所属大号 " ) )
self . exception_source_filter = QComboBox ( )
filters . addWidget ( self . exception_source_filter )
filters . addWidget ( QLabel ( " 原执行小号 " ) )
self . exception_worker_filter = QComboBox ( )
filters . addWidget ( self . exception_worker_filter )
self . button ( filters , " 刷新异常任务 " , self . query_exception_tasks )
filters . addStretch ( )
layout . addLayout ( filters )
def _build_exception_tab ( self ) :
tab = QWidget ( )
layout = QVBoxLayout ( tab )
layout . addWidget ( QLabel ( " 异常任务(当前大号所属分组;仅显示 unknown,只读) " ) )
layout . addWidget (
QLabel (
" unknown 只保留查询和保护占用 ,不提供人工核定、重发、重置、改派或状态修改。"
" 结果不确定仅作只读记录 ,不提供人工核定、重发、重置、改派或状态修改。"
)
)
self . exception_table = self . _task_table ( checkable = False )
layout . addWidget ( self . exception_table , 1 )
self . exception_counts = QLabel ( " 结果不确定 0 条 " )
layout . addWidget ( self . exception_counts )
return page
return tab
def _task_names ( self ) :
return self . data . get ( " task_names " ) or {
@@ -1117,55 +1082,16 @@ class Window(QMainWindow):
)
self . history_task_summary . setText ( f " 历史任务 { len ( history ) } 条 " )
def refresh_exception_filters ( self ) :
source = self . exception_source_filter . currentData ( )
worker = self . exception_worker_filter . currentData ( )
accounts = self . data . get ( " accounts " , [ ] )
self . exception_source_filter . blockSignals ( True )
self . exception_worker_filter . blockSignals ( True )
self . exception_source_filter . clear ( )
self . exception_source_filter . addItem ( " 全部原组 " , " " )
for account in accounts :
if account [ " role " ] == " main " :
self . exception_source_filter . addItem ( account [ " name " ] , account [ " id " ] )
self . exception_worker_filter . clear ( )
self . exception_worker_filter . addItem ( " 全部原执行小号 " , " " )
for ident , name in sorted (
self . _task_names ( ) . items ( ) , key = lambda value : value [ 1 ]
) :
if any ( a [ " id " ] == ident and a [ " role " ] == " main " for a in accounts ) :
continue
self . exception_worker_filter . addItem ( name , ident )
self . exception_source_filter . setCurrentIndex (
max ( 0 , self . exception_source_filter . findData ( source ) )
)
self . exception_worker_filter . setCurrentIndex (
max ( 0 , self . exception_worker_filter . findData ( worker ) )
)
self . exception_source_filter . blockSignals ( False )
self . exception_worker_filter . blockSignals ( False )
def show_exception_tasks ( self ) :
self . workspace_stack . setCurrentWidget ( self . exception_page )
self . refresh_exception_filters ( )
self . query_exception_tasks ( )
def query_exception_tasks ( self ) :
self . send (
" task_query " ,
{
" source " : self . exception_source_filter . currentData ( ) or None ,
" worker " : self . exception_worker_filter . currentData ( ) or None ,
} ,
)
def render_exception_tasks ( self , payload ) :
self . exception_tasks = payload . get ( " tasks " , [ ] )
self . exception_names = payload . get ( " task_names " ) or self . _task_names ( )
self . _fill_task_table (
self . exception_table , self . exception_tasks , self . exception_names
)
self . exception_counts . setText ( f " 结果不确定 { len ( self . exception_tasks ) } 条 " )
def _refresh_detail_exceptions ( self , account ) :
tasks = [ ]
if account and account [ " role " ] == " main " :
tasks = [
task
for task in self . data . get ( " tasks " , [ ] )
if task [ " source " ] == account [ " id " ] and task [ " status " ] == " unknown "
]
self . _fill_task_table ( self . exception_table , tasks , self . _task_names ( ) )
self . exception_counts . setText ( f " 结果不确定 { len ( tasks ) } 条 " )
def history_full ( self ) :
account = self . selected ( )
@@ -1340,7 +1266,7 @@ class Window(QMainWindow):
data [ " _request_id " ] = request_id
if data . get ( " id " ) :
self . latest_requests [ ( name , data [ " id " ] ) ] = request_id
if name in ( " task_query " , " start_preview " ) :
if name == " start_preview " :
self . latest_global_requests [ name ] = request_id
for button in self . buttons :
button . setEnabled ( False )
@@ -1418,7 +1344,7 @@ class Window(QMainWindow):
) :
return
if (
name in ( " task_query " , " start_preview " )
name == " start_preview "
and request_id
and self . latest_global_requests . get ( name ) != request_id
) :
@@ -1427,8 +1353,6 @@ class Window(QMainWindow):
self . history_dialog ( payload )
elif name in ( " works_open " , " works_refresh " , " works_full " ) :
self . works_dialog ( payload )
elif name == " task_query " :
self . render_exception_tasks ( payload )
elif name == " start_preview " :
self . show_start_preview ( payload )
elif name == " cancel_pending " :
@@ -1648,12 +1572,6 @@ class Window(QMainWindow):
load_cover ( item )
self . _toggle_works_selection ( self . works_all_mode . isChecked ( ) )
def restore_selection ( self ) :
self . selection_loaded = False
self . signature = None
self . refresh ( self . data )
self . statusBar ( ) . showMessage ( " 已恢复上次选择;不会启动业务 " )
def account_menu ( self , account_id , global_pos ) :
if account_id :
self . card_table . set_current_id ( account_id )
@@ -1663,6 +1581,7 @@ class Window(QMainWindow):
menu . addAction ( " 登录 / 刷新账号信息 " , lambda : self . selected_command ( " open " ) )
menu . addAction ( " 打开账号浏览器 " , lambda : self . selected_command ( " open " ) )
if account [ " role " ] == " main " :
menu . addAction ( " 添加小号 " , lambda : self . add ( " worker " , account [ " id " ] ) )
menu . addAction (
" 启动本组 " , lambda : self . start_group_request ( [ account [ " id " ] ] )
)
@@ -1697,6 +1616,7 @@ class Window(QMainWindow):
self . history_table . setRowCount ( 0 )
self . works_table . setRowCount ( 0 )
self . _clear_layout ( self . works_card_grid )
self . _refresh_detail_exceptions ( None )
return
account_changed = self . detail_account_id != account [ " id " ]
if account_changed :
@@ -1755,6 +1675,7 @@ class Window(QMainWindow):
f " { self . _sync_text ( sync . get ( ' notices ' ) , ' 历史事件 ' ) } · { self . _sync_text ( sync . get ( ' works ' ) , ' 作品缓存 ' ) } "
)
self . _refresh_detail_tasks ( account )
self . _refresh_detail_exceptions ( account )
def selected_command ( self , name ) :
account = self . selected ( )
@@ -1763,18 +1684,6 @@ class Window(QMainWindow):
else :
QMessageBox . information ( self , " 提示 " , " 请先选择一个账号。 " )
def checked_groups ( self ) :
return self . card_table . checked_groups ( )
def start_groups ( self ) :
groups = self . checked_groups ( )
if not groups :
QMessageBox . information (
self , " 提示 " , " 请勾选至少一个大号。上次选择会在重启后自动勾选。 "
)
return
self . start_group_request ( groups )
def stop_selected_group ( self ) :
account = self . selected ( )
if account :
@@ -1807,7 +1716,7 @@ class Window(QMainWindow):
{ " id " : account_box . currentData ( ) , " owner " : owner_box . currentData ( ) } ,
)
def add ( self , role ) :
def add ( self , role , owner_id = None ) :
win , form , buttons = dialog ( " 添加大号 " if role == " main " else " 添加小号 " , self )
name = QLineEdit ( )
name . setPlaceholderText ( " 例如:主账号、客服一号 " )
@@ -1819,6 +1728,10 @@ class Window(QMainWindow):
owner . addItem ( a [ " name " ] , a [ " id " ] )
if role == " worker " :
form . addRow ( " 归属大号 " , owner )
if owner_id is not None :
index = owner . findData ( owner_id )
if index > = 0 :
owner . setCurrentIndex ( index )
restore = QPushButton ( " 恢复已删除小号 " )
def open_restore ( ) :
@@ -2048,7 +1961,6 @@ class Window(QMainWindow):
new_accounts = [ a for a in data [ " accounts " ] if a [ " id " ] not in known ]
if new_accounts :
selected_id = new_accounts [ - 1 ] [ " id " ]
checked = self . checked_groups ( ) if self . selection_loaded else data [ " selected " ]
self . data = data
self . log_info . setText (
data . get ( " log_file_error " )
@@ -2059,7 +1971,6 @@ class Window(QMainWindow):
)
)
self . signature = signature
self . selection_loaded = True
display = { }
for account in data [ " accounts " ] :
identity = account . get ( " identity " , " " )
@@ -2078,9 +1989,8 @@ class Window(QMainWindow):
" status " : f " { ' 监听 ' if account [ ' role ' ] == ' main ' else ' 执行 ' } : { state } " ,
" color " : color ,
}
self . card_table . set_accounts ( data [ " accounts " ] , display , checked , selected_id )
self . card_table . set_accounts ( data [ " accounts " ] , display , selected_id )
self . show_profile ( )
self . refresh_exception_filters ( )
def closeEvent ( self , event ) :
if self . allow_close :