Enforce PUBLIC_MODE client isolation and hide subscribe UI
This commit is contained in:
parent
d6fa81b5de
commit
fdecd20bbc
3 changed files with 62 additions and 7 deletions
62
app/main.py
62
app/main.py
|
|
@ -445,26 +445,62 @@ def _migrate_legacy_request(post: dict) -> dict:
|
||||||
|
|
||||||
return post
|
return post
|
||||||
|
|
||||||
|
|
||||||
|
public_client_downloads: dict[str, set[str]] = {}
|
||||||
|
public_download_owner: dict[str, str] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _register_public_download(sid: str, url: str):
|
||||||
|
if not sid or not url:
|
||||||
|
return
|
||||||
|
public_client_downloads.setdefault(sid, set()).add(url)
|
||||||
|
public_download_owner[url] = sid
|
||||||
|
|
||||||
|
|
||||||
|
def _forget_public_download(url: str):
|
||||||
|
sid = public_download_owner.pop(url, None)
|
||||||
|
if not sid:
|
||||||
|
return
|
||||||
|
urls = public_client_downloads.get(sid)
|
||||||
|
if not urls:
|
||||||
|
return
|
||||||
|
urls.discard(url)
|
||||||
|
if not urls:
|
||||||
|
public_client_downloads.pop(sid, None)
|
||||||
|
|
||||||
|
|
||||||
|
async def _emit_download_event(event: str, payload, *, url: str | None = None):
|
||||||
|
if not config.PUBLIC_MODE:
|
||||||
|
await sio.emit(event, payload)
|
||||||
|
return
|
||||||
|
if not url:
|
||||||
|
return
|
||||||
|
sid = public_download_owner.get(url)
|
||||||
|
if sid:
|
||||||
|
await sio.emit(event, payload, to=sid)
|
||||||
|
|
||||||
class Notifier(DownloadQueueNotifier):
|
class Notifier(DownloadQueueNotifier):
|
||||||
async def added(self, dl):
|
async def added(self, dl):
|
||||||
log.info(f"Notifier: Download added - {dl.title}")
|
log.info(f"Notifier: Download added - {dl.title}")
|
||||||
await sio.emit('added', serializer.encode(dl))
|
await _emit_download_event('added', serializer.encode(dl), url=dl.url)
|
||||||
|
|
||||||
async def updated(self, dl):
|
async def updated(self, dl):
|
||||||
log.debug(f"Notifier: Download updated - {dl.title}")
|
log.debug(f"Notifier: Download updated - {dl.title}")
|
||||||
await sio.emit('updated', serializer.encode(dl))
|
await _emit_download_event('updated', serializer.encode(dl), url=dl.url)
|
||||||
|
|
||||||
async def completed(self, dl):
|
async def completed(self, dl):
|
||||||
log.info(f"Notifier: Download completed - {dl.title}")
|
log.info(f"Notifier: Download completed - {dl.title}")
|
||||||
await sio.emit('completed', serializer.encode(dl))
|
await _emit_download_event('completed', serializer.encode(dl), url=dl.url)
|
||||||
|
_forget_public_download(dl.url)
|
||||||
|
|
||||||
async def canceled(self, id):
|
async def canceled(self, id):
|
||||||
log.info(f"Notifier: Download canceled - {id}")
|
log.info(f"Notifier: Download canceled - {id}")
|
||||||
await sio.emit('canceled', serializer.encode(id))
|
await _emit_download_event('canceled', serializer.encode(id), url=id)
|
||||||
|
_forget_public_download(id)
|
||||||
|
|
||||||
async def cleared(self, id):
|
async def cleared(self, id):
|
||||||
log.info(f"Notifier: Download cleared - {id}")
|
log.info(f"Notifier: Download cleared - {id}")
|
||||||
await sio.emit('cleared', serializer.encode(id))
|
await _emit_download_event('cleared', serializer.encode(id), url=id)
|
||||||
|
|
||||||
dqueue = DownloadQueue(config, Notifier())
|
dqueue = DownloadQueue(config, Notifier())
|
||||||
app.on_startup.append(lambda app: dqueue.initialize())
|
app.on_startup.append(lambda app: dqueue.initialize())
|
||||||
|
|
@ -725,6 +761,11 @@ async def add(request):
|
||||||
bool(o.get('folder')),
|
bool(o.get('folder')),
|
||||||
o['auto_start'],
|
o['auto_start'],
|
||||||
)
|
)
|
||||||
|
if config.PUBLIC_MODE:
|
||||||
|
sid = request.headers.get('X-Client-Id', '').strip()
|
||||||
|
if not sid:
|
||||||
|
raise web.HTTPBadRequest(reason='missing X-Client-Id in public mode')
|
||||||
|
_register_public_download(sid, o['url'])
|
||||||
status = await dqueue.add(
|
status = await dqueue.add(
|
||||||
o['url'],
|
o['url'],
|
||||||
o['download_type'],
|
o['download_type'],
|
||||||
|
|
@ -979,6 +1020,17 @@ async def connect(sid, environ):
|
||||||
if config.YTDL_OPTIONS_FILE:
|
if config.YTDL_OPTIONS_FILE:
|
||||||
await sio.emit('ytdl_options_changed', serializer.encode(get_options_update_time()), to=sid)
|
await sio.emit('ytdl_options_changed', serializer.encode(get_options_update_time()), to=sid)
|
||||||
|
|
||||||
|
@sio.event
|
||||||
|
async def disconnect(sid):
|
||||||
|
if not config.PUBLIC_MODE:
|
||||||
|
return
|
||||||
|
owned = list(public_client_downloads.pop(sid, set()))
|
||||||
|
for url in owned:
|
||||||
|
public_download_owner.pop(url, None)
|
||||||
|
if owned:
|
||||||
|
await dqueue.cancel(owned)
|
||||||
|
|
||||||
|
|
||||||
def get_custom_dirs():
|
def get_custom_dirs():
|
||||||
cache_ttl_seconds = 5
|
cache_ttl_seconds = 5
|
||||||
now = asyncio.get_running_loop().time()
|
now = asyncio.get_running_loop().time()
|
||||||
|
|
|
||||||
|
|
@ -121,11 +121,13 @@
|
||||||
[disabled]="downloads.loading">
|
[disabled]="downloads.loading">
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
@if (!isPublicMode()) {
|
||||||
<button class="btn btn-outline-secondary btn-lg px-3" type="button"
|
<button class="btn btn-outline-secondary btn-lg px-3" type="button"
|
||||||
(click)="addSubscription()"
|
(click)="addSubscription()"
|
||||||
[disabled]="downloads.loading || isPublicMode()">
|
[disabled]="downloads.loading">
|
||||||
Subscribe
|
Subscribe
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,8 @@ export class DownloadsService {
|
||||||
const ce = payload.clipEnd?.trim();
|
const ce = payload.clipEnd?.trim();
|
||||||
if (cs) body['clip_start'] = cs;
|
if (cs) body['clip_start'] = cs;
|
||||||
if (ce) body['clip_end'] = ce;
|
if (ce) body['clip_end'] = ce;
|
||||||
return this.http.post<Status>('add', body).pipe(
|
const headers = this.socket.ioSocket?.id ? { 'X-Client-Id': this.socket.ioSocket.id } : undefined;
|
||||||
|
return this.http.post<Status>('add', body, { headers }).pipe(
|
||||||
catchError(this.handleHTTPError)
|
catchError(this.handleHTTPError)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue