Fix __add_entry parameters; remove duplicated code between channel and playlist processing
This commit is contained in:
committed by
Alex Shnitman
parent
393add34b1
commit
ffe1112dc6
85
app/ytdl.py
85
app/ytdl.py
@@ -109,7 +109,7 @@ class Download:
|
|||||||
else:
|
else:
|
||||||
filename = d['info_dict']['filepath']
|
filename = d['info_dict']['filepath']
|
||||||
self.status_queue.put({'status': 'finished', 'filename': filename})
|
self.status_queue.put({'status': 'finished', 'filename': filename})
|
||||||
|
|
||||||
# Capture all chapter files when SplitChapters finishes
|
# Capture all chapter files when SplitChapters finishes
|
||||||
elif d.get('postprocessor') == 'SplitChapters' and d.get('status') == 'finished':
|
elif d.get('postprocessor') == 'SplitChapters' and d.get('status') == 'finished':
|
||||||
chapters = d.get('info_dict', {}).get('chapters', [])
|
chapters = d.get('info_dict', {}).get('chapters', [])
|
||||||
@@ -134,7 +134,7 @@ class Download:
|
|||||||
'postprocessor_hooks': [put_status_postprocessor],
|
'postprocessor_hooks': [put_status_postprocessor],
|
||||||
**self.ytdl_opts,
|
**self.ytdl_opts,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add chapter splitting options if enabled
|
# Add chapter splitting options if enabled
|
||||||
if self.info.split_by_chapters:
|
if self.info.split_by_chapters:
|
||||||
ytdl_params['outtmpl']['chapter'] = self.info.chapter_template
|
ytdl_params['outtmpl']['chapter'] = self.info.chapter_template
|
||||||
@@ -144,7 +144,7 @@ class Download:
|
|||||||
'key': 'FFmpegSplitChapters',
|
'key': 'FFmpegSplitChapters',
|
||||||
'force_keyframes': False
|
'force_keyframes': False
|
||||||
})
|
})
|
||||||
|
|
||||||
ret = yt_dlp.YoutubeDL(params=ytdl_params).download([self.info.url])
|
ret = yt_dlp.YoutubeDL(params=ytdl_params).download([self.info.url])
|
||||||
self.status_queue.put({'status': 'finished' if ret == 0 else 'error'})
|
self.status_queue.put({'status': 'finished' if ret == 0 else 'error'})
|
||||||
log.info(f"Finished download for: {self.info.title}")
|
log.info(f"Finished download for: {self.info.title}")
|
||||||
@@ -211,7 +211,7 @@ class Download:
|
|||||||
self.info.filename = re.sub(r'\.webm$', '.jpg', self.info.filename)
|
self.info.filename = re.sub(r'\.webm$', '.jpg', self.info.filename)
|
||||||
|
|
||||||
# Handle chapter files
|
# Handle chapter files
|
||||||
log.debug(f"Update status for {self.info.title}: {status}")
|
log.debug(f"Update status for {self.info.title}: {status}")
|
||||||
if 'chapter_file' in status:
|
if 'chapter_file' in status:
|
||||||
chapter_file = status.get('chapter_file')
|
chapter_file = status.get('chapter_file')
|
||||||
if not hasattr(self.info, 'chapter_files'):
|
if not hasattr(self.info, 'chapter_files'):
|
||||||
@@ -224,7 +224,7 @@ class Download:
|
|||||||
self.info.chapter_files.append({'filename': rel_path, 'size': file_size})
|
self.info.chapter_files.append({'filename': rel_path, 'size': file_size})
|
||||||
# Skip the rest of status processing for chapter files
|
# Skip the rest of status processing for chapter files
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.info.status = status['status']
|
self.info.status = status['status']
|
||||||
self.info.msg = status.get('msg')
|
self.info.msg = status.get('msg')
|
||||||
if 'downloaded_bytes' in status:
|
if 'downloaded_bytes' in status:
|
||||||
@@ -355,7 +355,7 @@ class PersistentQueue:
|
|||||||
log.debug(f"{log_prefix}{result.stdout or " was successful, no output"}")
|
log.debug(f"{log_prefix}{result.stdout or " was successful, no output"}")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
log.debug(f"{log_prefix} failed: 'sqlite3' was not found")
|
log.debug(f"{log_prefix} failed: 'sqlite3' was not found")
|
||||||
|
|
||||||
class DownloadQueue:
|
class DownloadQueue:
|
||||||
def __init__(self, config, notifier):
|
def __init__(self, config, notifier):
|
||||||
self.config = config
|
self.config = config
|
||||||
@@ -471,6 +471,28 @@ class DownloadQueue:
|
|||||||
self.pending.put(download)
|
self.pending.put(download)
|
||||||
await self.notifier.added(dl)
|
await self.notifier.added(dl)
|
||||||
|
|
||||||
|
async def __process_playlist_or_channel_entry(self, etype, entry, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already):
|
||||||
|
entries = entry['entries']
|
||||||
|
# Convert generator to list if needed (for len() and slicing operations)
|
||||||
|
if isinstance(entries, types.GeneratorType):
|
||||||
|
entries = list(entries)
|
||||||
|
log.info(f'{etype} detected with {len(entries)} entries')
|
||||||
|
index_digits = len(str(len(entries)))
|
||||||
|
results = []
|
||||||
|
if playlist_item_limit > 0:
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
for property in ("id", "title", "uploader", "uploader_id"):
|
||||||
|
if property in entry:
|
||||||
|
etr[f"{etype}_{property}"] = entry[property]
|
||||||
|
results.append(await self.__add_entry(etr, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already))
|
||||||
|
if any(res['status'] == 'error' for res in results):
|
||||||
|
return {'status': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)}
|
||||||
|
|
||||||
async def __add_entry(self, entry, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already):
|
async def __add_entry(self, entry, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already):
|
||||||
if not entry:
|
if not entry:
|
||||||
return {'status': 'error', 'msg': "Invalid/empty data was given."}
|
return {'status': 'error', 'msg': "Invalid/empty data was given."}
|
||||||
@@ -486,54 +508,11 @@ class DownloadQueue:
|
|||||||
etype = entry.get('_type') or 'video'
|
etype = entry.get('_type') or 'video'
|
||||||
|
|
||||||
if etype.startswith('url'):
|
if etype.startswith('url'):
|
||||||
log.debug('Processing as an url')
|
log.debug('Processing as a url')
|
||||||
return await self.add(entry['url'], quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already)
|
return await self.add(entry['url'], quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already)
|
||||||
elif etype == 'playlist':
|
elif etype == 'playlist' or etype == 'channel':
|
||||||
log.debug('Processing as a playlist')
|
log.debug(f'Processing as a {etype}')
|
||||||
entries = entry['entries']
|
return await self.__process_playlist_or_channel_entry(etype, entry, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already)
|
||||||
# Convert generator to list if needed (for len() and slicing operations)
|
|
||||||
if isinstance(entries, types.GeneratorType):
|
|
||||||
entries = list(entries)
|
|
||||||
log.info(f'playlist detected with {len(entries)} entries')
|
|
||||||
playlist_index_digits = len(str(len(entries)))
|
|
||||||
results = []
|
|
||||||
if playlist_item_limit > 0:
|
|
||||||
log.info(f'Playlist item limit is set. Processing only first {playlist_item_limit} entries')
|
|
||||||
entries = entries[:playlist_item_limit]
|
|
||||||
for index, etr in enumerate(entries, start=1):
|
|
||||||
etr["_type"] = "video"
|
|
||||||
etr["playlist"] = entry["id"]
|
|
||||||
etr["playlist_index"] = '{{0:0{0:d}d}}'.format(playlist_index_digits).format(index)
|
|
||||||
for property in ("id", "title", "uploader", "uploader_id"):
|
|
||||||
if property in entry:
|
|
||||||
etr[f"playlist_{property}"] = entry[property]
|
|
||||||
results.append(await self.__add_entry(etr, quality, format, folder, custom_name_prefix, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already))
|
|
||||||
if any(res['status'] == 'error' for res in results):
|
|
||||||
return {'status': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)}
|
|
||||||
return {'status': 'ok'}
|
|
||||||
elif etype == 'channel':
|
|
||||||
log.debug('Processing as a channel')
|
|
||||||
entries = entry['entries']
|
|
||||||
# Convert generator to list if needed (for len() and slicing operations)
|
|
||||||
if isinstance(entries, types.GeneratorType):
|
|
||||||
entries = list(entries)
|
|
||||||
log.info(f'channel detected with {len(entries)} entries')
|
|
||||||
channel_index_digits = len(str(len(entries)))
|
|
||||||
results = []
|
|
||||||
if playlist_item_limit > 0:
|
|
||||||
log.info(f'Channel item limit is set. Processing only first {playlist_item_limit} entries')
|
|
||||||
entries = entries[:playlist_item_limit]
|
|
||||||
for index, etr in enumerate(entries, start=1):
|
|
||||||
etr["_type"] = "video"
|
|
||||||
etr["channel"] = entry.get("id") or entry.get("channel_id") or entry.get("channel")
|
|
||||||
etr["channel_index"] = '{{0:0{0:d}d}}'.format(channel_index_digits).format(index)
|
|
||||||
for property in ("id", "title", "uploader", "uploader_id", "channel", "channel_id"):
|
|
||||||
if property in entry:
|
|
||||||
etr[f"channel_{property}"] = entry[property]
|
|
||||||
results.append(await self.__add_entry(etr, quality, format, folder, custom_name_prefix, playlist_strict_mode, playlist_item_limit, auto_start, split_by_chapters, chapter_template, already))
|
|
||||||
if any(res['status'] == 'error' for res in results):
|
|
||||||
return {'status': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)}
|
|
||||||
return {'status': 'ok'}
|
|
||||||
elif etype == 'video' or (etype.startswith('url') and 'id' in entry and 'title' in entry):
|
elif etype == 'video' or (etype.startswith('url') and 'id' in entry and 'title' in entry):
|
||||||
log.debug('Processing as a video')
|
log.debug('Processing as a video')
|
||||||
key = entry.get('webpage_url') or entry['url']
|
key = entry.get('webpage_url') or entry['url']
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<ul class="dropdown-menu dropdown-menu-end position-absolute" aria-labelledby="theme-select">
|
<ul class="dropdown-menu dropdown-menu-end position-absolute" aria-labelledby="theme-select">
|
||||||
@for (theme of themes; track theme) {
|
@for (theme of themes; track theme) {
|
||||||
<li>
|
<li>
|
||||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||||
[class.active]="activeTheme === theme"
|
[class.active]="activeTheme === theme"
|
||||||
(click)="themeChanged(theme)">
|
(click)="themeChanged(theme)">
|
||||||
<span class="me-2 opacity-50">
|
<span class="me-2 opacity-50">
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
ngbTooltip="Choose where to save downloads. Type to create a new folder." />
|
ngbTooltip="Choose where to save downloads. Type to create a new folder." />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
(keydown)="isNumber($event)"
|
(keydown)="isNumber($event)"
|
||||||
[(ngModel)]="playlistItemLimit"
|
[(ngModel)]="playlistItemLimit"
|
||||||
[disabled]="addInProgress || downloads.loading"
|
[disabled]="addInProgress || downloads.loading"
|
||||||
ngbTooltip="Maximum number of items to download from a playlist (0 = no limit)">
|
ngbTooltip="Maximum number of items to download from a playlist or channel (0 = no limit)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Batch Import Modal -->
|
<!-- Batch Import Modal -->
|
||||||
<div class="modal fade" tabindex="-1" role="dialog"
|
<div class="modal fade" tabindex="-1" role="dialog"
|
||||||
[class.show]="batchImportModalOpen"
|
[class.show]="batchImportModalOpen"
|
||||||
[style.display]="batchImportModalOpen ? 'block' : 'none'">
|
[style.display]="batchImportModalOpen ? 'block' : 'none'">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
@@ -349,7 +349,7 @@
|
|||||||
<td title="{{ download.value.filename }}">
|
<td title="{{ download.value.filename }}">
|
||||||
<div class="d-flex flex-column flex-sm-row align-items-center row-gap-2 column-gap-3">
|
<div class="d-flex flex-column flex-sm-row align-items-center row-gap-2 column-gap-3">
|
||||||
<div>{{ download.value.title }} </div>
|
<div>{{ download.value.title }} </div>
|
||||||
<ngb-progressbar height="1.5rem" [showValue]="download.value.status !== 'preparing'" [striped]="download.value.status === 'preparing'" [animated]="download.value.status === 'preparing'" type="success"
|
<ngb-progressbar height="1.5rem" [showValue]="download.value.status !== 'preparing'" [striped]="download.value.status === 'preparing'" [animated]="download.value.status === 'preparing'" type="success"
|
||||||
[value]="download.value.status === 'preparing' ? 100 : download.value.percent" class="download-progressbar" />
|
[value]="download.value.status === 'preparing' ? 100 : download.value.percent" class="download-progressbar" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user