diff --git a/app/main.py b/app/main.py
index 3240744..9b43f4c 100644
--- a/app/main.py
+++ b/app/main.py
@@ -322,6 +322,11 @@ async def start(request):
status = await dqueue.start_pending(ids)
return web.Response(text=serializer.encode(status))
+@routes.post(config.URL_PREFIX + 'cancel-add')
+async def cancel_add(request):
+ dqueue.cancel_add()
+ return web.Response(text=serializer.encode({'status': 'ok'}), content_type='application/json')
+
@routes.get(config.URL_PREFIX + 'history')
async def history(request):
history = { 'done': [], 'queue': [], 'pending': []}
@@ -433,6 +438,7 @@ async def add_cors(request):
return web.Response(text=serializer.encode({"status": "ok"}))
app.router.add_route('OPTIONS', config.URL_PREFIX + 'add', add_cors)
+app.router.add_route('OPTIONS', config.URL_PREFIX + 'cancel-add', add_cors)
async def on_prepare(request, response):
if 'Origin' in request.headers:
diff --git a/app/ytdl.py b/app/ytdl.py
index 18e47ab..c141bc0 100644
--- a/app/ytdl.py
+++ b/app/ytdl.py
@@ -531,11 +531,16 @@ class DownloadQueue:
self.notifier = notifier
self.queue = PersistentQueue("queue", self.config.STATE_DIR + '/queue')
self.done = PersistentQueue("completed", self.config.STATE_DIR + '/completed')
+ self._add_canceled = False
self.pending = PersistentQueue("pending", self.config.STATE_DIR + '/pending')
self.active_downloads = set()
self.semaphore = asyncio.Semaphore(int(self.config.MAX_CONCURRENT_DOWNLOADS))
self.done.load()
+ def cancel_add(self):
+ self._add_canceled = True
+ log.info('Playlist add operation canceled by user')
+
async def __import_queue(self):
for k, v in self.queue.saved_items():
await self.__add_download(v, True)
@@ -699,6 +704,9 @@ class DownloadQueue:
log.info(f'Item limit is set. Processing only first {playlist_item_limit} entries')
entries = entries[:playlist_item_limit]
for index, etr in enumerate(entries, start=1):
+ if self._add_canceled:
+ log.info(f'Playlist add canceled after processing {len(already)} entries')
+ return {'status': 'ok', 'msg': f'Canceled - added {len(already)} items before cancel'}
etr["_type"] = "video"
etr[etype] = entry.get("id") or entry.get("channel_id") or entry.get("channel")
etr[f"{etype}_index"] = '{{0:0{0:d}d}}'.format(index_digits).format(index)
@@ -771,6 +779,8 @@ class DownloadQueue:
f'{playlist_item_limit=} {auto_start=} {split_by_chapters=} {chapter_template=} '
f'{subtitle_format=} {subtitle_language=} {subtitle_mode=}'
)
+ if already is None:
+ self._add_canceled = False
already = set() if already is None else already
if url in already:
log.info('recursion detected, skipping')
diff --git a/ui/src/app/app.html b/ui/src/app/app.html
index 8e7741e..eeb72f7 100644
--- a/ui/src/app/app.html
+++ b/ui/src/app/app.html
@@ -98,15 +98,17 @@
name="addUrl"
[(ngModel)]="addUrl"
[disabled]="addInProgress || downloads.loading">
-
+ @if (addInProgress) {
+
+ } @else {
+
+ }
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts
index e56fd2b..0a548f0 100644
--- a/ui/src/app/app.ts
+++ b/ui/src/app/app.ts
@@ -427,6 +427,13 @@ export class App implements AfterViewInit, OnInit {
});
}
+ cancelAdding() {
+ this.downloads.cancelAdd().subscribe({
+ next: () => { this.addInProgress = false; },
+ error: () => { this.addInProgress = false; }
+ });
+ }
+
downloadItemByKey(id: string) {
this.downloads.startById([id]).subscribe();
}
diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts
index 52690c8..2c0708a 100644
--- a/ui/src/app/services/downloads.service.ts
+++ b/ui/src/app/services/downloads.service.ts
@@ -208,6 +208,10 @@ export class DownloadsService {
public exportQueueUrls(): string[] {
return Array.from(this.queue.values()).map(download => download.url);
}
-
-
+
+ public cancelAdd() {
+ return this.http.post('cancel-add', {}).pipe(
+ catchError(this.handleHTTPError)
+ );
+ }
}