fix queue
This commit is contained in:
@@ -1167,61 +1167,123 @@ def apply_startup_overrides(profile: dict) -> dict:
|
||||
return set_config(profile, values, apply_now=True, apply_on_start=True)
|
||||
|
||||
|
||||
def start_or_resume_hash(c: ScgiRtorrentClient, torrent_hash: str) -> dict:
|
||||
"""Start stopped torrents and resume torrents paused with d.pause."""
|
||||
def _int_rpc(c: ScgiRtorrentClient, method: str, h: str, default: int = 0) -> int:
|
||||
try:
|
||||
return int(c.call(method, h) or 0)
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
def _str_rpc(c: ScgiRtorrentClient, method: str, h: str, default: str = '') -> str:
|
||||
try:
|
||||
return str(c.call(method, h) or '')
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
def _download_runtime_state(c: ScgiRtorrentClient, h: str) -> dict:
|
||||
"""Read rTorrent state using the native pause model: stopped, paused or active."""
|
||||
state = _int_rpc(c, 'd.state', h)
|
||||
active = _int_rpc(c, 'd.is_active', h)
|
||||
opened = _int_rpc(c, 'd.is_open', h)
|
||||
# Note: W rTorrent pauza nie zmienia d.state. Paused to state=1, open=1, active=0.
|
||||
return {
|
||||
'state': state,
|
||||
'open': opened,
|
||||
'active': active,
|
||||
'paused': bool(state and opened and not active),
|
||||
'stopped': not bool(state),
|
||||
'message': _str_rpc(c, 'd.message', h),
|
||||
}
|
||||
|
||||
|
||||
def pause_hash(c: ScgiRtorrentClient, torrent_hash: str) -> dict:
|
||||
"""Pause an active rTorrent item without stopping or closing it."""
|
||||
h = str(torrent_hash or '')
|
||||
if not h:
|
||||
return {'hash': h, 'ok': False, 'error': 'missing hash'}
|
||||
|
||||
result: dict = {'hash': h, 'commands': []}
|
||||
before = _download_runtime_state(c, h)
|
||||
result = {'hash': h, 'before': before, 'commands': []}
|
||||
try:
|
||||
result['state_before'] = int(c.call('d.state', h) or 0)
|
||||
# Note: Smart Queue zatrzymuje slot przez d.pause, nie przez d.stop, żeby późniejsze d.resume działało jak w ruTorrent.
|
||||
c.call('d.pause', h)
|
||||
result['commands'].append('d.pause')
|
||||
result['after'] = _download_runtime_state(c, h)
|
||||
result['ok'] = True
|
||||
except Exception as exc:
|
||||
result['state_before_error'] = str(exc)
|
||||
result['state_before'] = 0
|
||||
result.update({'ok': False, 'error': str(exc), 'after': _download_runtime_state(c, h)})
|
||||
return result
|
||||
|
||||
# Note: Ręczne Start i Smart Queue muszą zdejmować pause przez d.resume; samo d.start
|
||||
# nie rusza torrentów zatrzymanych wcześniej komendą d.pause.
|
||||
for method in ('d.resume',):
|
||||
try:
|
||||
c.call(method, h)
|
||||
result['commands'].append(method)
|
||||
except Exception as exc:
|
||||
result.setdefault('ignored_errors', []).append(f'{method}: {exc}')
|
||||
|
||||
# Note: d.open bywa potrzebne po całkowitym stop/close; dla już otwartych torrentów jest bezpiecznie ignorowane.
|
||||
def resume_paused_hash(c: ScgiRtorrentClient, torrent_hash: str) -> dict:
|
||||
"""Resume only a paused rTorrent item; never convert it through stop/start."""
|
||||
h = str(torrent_hash or '')
|
||||
if not h:
|
||||
return {'hash': h, 'ok': False, 'error': 'missing hash'}
|
||||
before = _download_runtime_state(c, h)
|
||||
result: dict = {'hash': h, 'before': before, 'commands': []}
|
||||
if before.get('stopped'):
|
||||
result.update({'ok': False, 'skipped': 'stopped_not_paused', 'after': before})
|
||||
return result
|
||||
if before.get('active'):
|
||||
result.update({'ok': True, 'skipped': 'already_active', 'after': before})
|
||||
return result
|
||||
try:
|
||||
# Note: ruTorrent dla od-pauzowania wysyła odpowiednik unpause/d.resume. Nie dokładamy d.start/d.open,
|
||||
# bo to są komendy dla stanu Stopped/Open, a nie dla czystego Paused.
|
||||
c.call('d.resume', h)
|
||||
result['commands'].append('d.resume')
|
||||
result['after'] = _download_runtime_state(c, h)
|
||||
result['ok'] = True
|
||||
except Exception as exc:
|
||||
result.update({'ok': False, 'error': str(exc), 'after': _download_runtime_state(c, h)})
|
||||
return result
|
||||
|
||||
|
||||
def start_or_resume_hash(c: ScgiRtorrentClient, torrent_hash: str) -> dict:
|
||||
"""Start stopped torrents or resume torrents paused with d.pause, without mixing both paths."""
|
||||
h = str(torrent_hash or '')
|
||||
if not h:
|
||||
return {'hash': h, 'ok': False, 'error': 'missing hash'}
|
||||
before = _download_runtime_state(c, h)
|
||||
result: dict = {'hash': h, 'before': before, 'commands': []}
|
||||
|
||||
if before.get('active'):
|
||||
result.update({'ok': True, 'skipped': 'already_active', 'after': before})
|
||||
return result
|
||||
|
||||
if before.get('paused') or (before.get('state') and not before.get('active')):
|
||||
# Note: Paused w rTorrent wznawiamy tylko przez d.resume; d.start jest tu celowo pomijane.
|
||||
resumed = resume_paused_hash(c, h)
|
||||
resumed['mode'] = 'resume_paused'
|
||||
return resumed
|
||||
|
||||
try:
|
||||
# Note: d.start zostaje wyłącznie dla Stopped/closed, czyli dla stanu innego niż pause->resume.
|
||||
c.call('d.open', h)
|
||||
result['commands'].append('d.open')
|
||||
except Exception as exc:
|
||||
result.setdefault('ignored_errors', []).append(f'd.open: {exc}')
|
||||
|
||||
for method in ('d.start', 'd.resume', 'd.try_start'):
|
||||
try:
|
||||
c.call('d.start', h)
|
||||
result['commands'].append('d.start')
|
||||
except Exception as exc:
|
||||
result.setdefault('ignored_errors', []).append(f'd.start: {exc}')
|
||||
try:
|
||||
c.call(method, h)
|
||||
result['commands'].append(method)
|
||||
except Exception as exc:
|
||||
result.setdefault('ignored_errors', []).append(f'{method}: {exc}')
|
||||
|
||||
try:
|
||||
result['state_after'] = int(c.call('d.state', h) or 0)
|
||||
except Exception as exc:
|
||||
result['state_after_error'] = str(exc)
|
||||
try:
|
||||
result['active_after'] = int(c.call('d.is_active', h) or 0)
|
||||
except Exception as exc:
|
||||
result['active_after_error'] = str(exc)
|
||||
result['ok'] = True
|
||||
c.call('d.try_start', h)
|
||||
result['commands'].append('d.try_start')
|
||||
except Exception as exc2:
|
||||
result.setdefault('ignored_errors', []).append(f'd.try_start: {exc2}')
|
||||
result['ok'] = False
|
||||
result['after'] = _download_runtime_state(c, h)
|
||||
result['ok'] = result.get('ok', True)
|
||||
return result
|
||||
|
||||
def action(profile: dict, torrent_hashes: list[str], name: str, payload: dict | None = None) -> dict:
|
||||
payload = payload or {}
|
||||
c = client_for(profile)
|
||||
methods = {
|
||||
"start": "d.start",
|
||||
"pause": "d.pause",
|
||||
"stop": "d.stop",
|
||||
"resume": "d.resume",
|
||||
"recheck": "d.check_hash",
|
||||
"reannounce": "d.tracker_announce",
|
||||
"remove": "d.erase",
|
||||
@@ -1289,8 +1351,16 @@ def action(profile: dict, torrent_hashes: list[str], name: str, payload: dict |
|
||||
c.call("d.directory.set", h, path)
|
||||
results.append(item)
|
||||
return {"ok": True, "count": len(torrent_hashes), "move_data": move_data, "results": results}
|
||||
if name in {"start", "resume"}:
|
||||
# Note: Start działa teraz także dla pozycji Paused, bo wykonuje pełną sekwencję resume/open/start.
|
||||
if name == "pause":
|
||||
# Note: Pauza aplikacji jest teraz czystym d.pause, żeby późniejszy resume działał bez stop/start.
|
||||
results = [pause_hash(c, h) for h in torrent_hashes]
|
||||
return {"ok": True, "count": len(torrent_hashes), "remove_data": False, "results": results}
|
||||
if name in {"resume", "unpause"}:
|
||||
# Note: Resume/Unpause używa wyłącznie d.resume dla stanu Paused.
|
||||
results = [resume_paused_hash(c, h) for h in torrent_hashes]
|
||||
return {"ok": True, "count": len(torrent_hashes), "remove_data": False, "results": results}
|
||||
if name == "start":
|
||||
# Note: Start rozdziela Stopped od Paused; paused idzie przez d.resume, stopped przez d.start.
|
||||
results = [start_or_resume_hash(c, h) for h in torrent_hashes]
|
||||
return {"ok": True, "count": len(torrent_hashes), "remove_data": False, "results": results}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user