150 lines
5.8 KiB
HTML
150 lines
5.8 KiB
HTML
<div class="app">
|
|
<app-sidebar
|
|
[status]="status"
|
|
[selectedFolder]="selectedFolder"
|
|
[statusCounts]="statusCounts"
|
|
[folders]="folderNavs"
|
|
[allCount]="allCount"
|
|
[metubeVersion]="metubeVersion"
|
|
[themes]="themes"
|
|
[activeTheme]="activeTheme"
|
|
[open]="sidebarOpen"
|
|
(statusChange)="onStatusChange($event)"
|
|
(folderChange)="onFolderChange($event)"
|
|
(openAdvanced)="toggleAdvanced(true)"
|
|
(openActivity)="activityOpen = !activityOpen"
|
|
(themeChanged)="themeChanged($event)"
|
|
(close)="sidebarOpen = false">
|
|
</app-sidebar>
|
|
|
|
<main class="main">
|
|
<app-command-bar
|
|
[url]="addUrl"
|
|
(urlChange)="addUrl = $event"
|
|
[quality]="quality"
|
|
[format]="format"
|
|
[folder]="folder"
|
|
[qualities]="qualities"
|
|
[formats]="formats"
|
|
[customDirs$]="customDirs$"
|
|
[allowCustomDir]="allowCustomDir.bind(this)"
|
|
[disabled]="addInProgress || downloads.loading"
|
|
(add)="addDownload($event)"
|
|
(qualityChange)="setQuality($event)"
|
|
(formatChange)="setFormat($event)"
|
|
(folderChange)="setFolder($event)"
|
|
(openAdvanced)="toggleAdvanced(true)"
|
|
(menu)="sidebarOpen = true">
|
|
</app-command-bar>
|
|
|
|
<app-stats-strip
|
|
[active]="activeDownloads"
|
|
[speed]="totalSpeed"
|
|
[queued]="queuedDownloads"
|
|
[storage]="librarySize">
|
|
</app-stats-strip>
|
|
|
|
<div class="content">
|
|
<app-active-downloads
|
|
[items]="downloadingRows"
|
|
[totalSpeed]="totalSpeed"
|
|
[loading]="downloads.loading"
|
|
(cancel)="cancelDownload($event)">
|
|
</app-active-downloads>
|
|
|
|
<app-download-list
|
|
[status]="status"
|
|
[rows]="filteredRows"
|
|
[folderName]="folderName"
|
|
[filterText]="filter"
|
|
(filterChange)="filter = $event"
|
|
[sort]="sort"
|
|
(sortChange)="sort = $event"
|
|
[sortAscending]="sortAscending"
|
|
(sortAscendingChange)="setSortDirection($event)"
|
|
[view]="view"
|
|
(viewChange)="setView($event)"
|
|
[loading]="downloads.loading"
|
|
[customDirsFor]="customDirsForDownload.bind(this)"
|
|
[allowCustomDir]="allowCustomDir.bind(this)"
|
|
[downloadUrlFor]="buildDownloadLink.bind(this)"
|
|
(start)="startItems($event)"
|
|
(del)="delItems($event, status === 'completed' ? 'done' : 'queue')"
|
|
(move)="moveItems($event.ids, $event.folder)"
|
|
(retry)="retryDownload($event.key, $event.dl)"
|
|
(folderEdit)="folderChanged($event.key, $event.folder)"
|
|
(clearCompleted)="clearCompletedDownloads()"
|
|
(clearFailed)="clearFailedDownloads()"
|
|
(retryFailed)="retryFailedDownloads()"
|
|
(downloadSelected)="downloadSelectedFiles($event)">
|
|
</app-download-list>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Advanced options slide-over -->
|
|
<app-advanced-options
|
|
[open]="isAdvancedOpen"
|
|
[autoStart]="autoStart"
|
|
(autoStartChange)="setAutoStart($event)"
|
|
[playlistStrictMode]="playlistStrictMode"
|
|
(strictChange)="playlistStrictMode = $event"
|
|
[playlistItemLimit]="playlistItemLimit"
|
|
(limitChange)="playlistItemLimit = $event"
|
|
[customNamePrefix]="customNamePrefix"
|
|
(prefixChange)="customNamePrefix = $event"
|
|
[metubeVersion]="metubeVersion"
|
|
[ytDlpVersion]="ytDlpVersion"
|
|
[ytDlpOptionsUpdateTime]="ytDlpOptionsUpdateTime"
|
|
(close)="toggleAdvanced(false)"
|
|
(import)="openBatchImportModal()"
|
|
(export)="exportBatchUrls('all')"
|
|
(copy)="copyBatchUrls('all')"
|
|
(cookie)="saveCookie($event.domain, $event.cookie)">
|
|
</app-advanced-options>
|
|
|
|
<!-- Batch import modal -->
|
|
<div class="modal fade" tabindex="-1" role="dialog" [ngClass]="{'show': batchImportModalOpen}" [ngStyle]="{'display': batchImportModalOpen ? 'block' : 'none'}">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">批量导入 URL</h5>
|
|
<button type="button" class="btn-close" aria-label="关闭" (click)="closeBatchImportModal()"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<textarea [(ngModel)]="batchImportText" class="form-control" rows="6" placeholder="每行粘贴一个视频 URL"></textarea>
|
|
<div class="mt-2">
|
|
<small *ngIf="batchImportStatus" class="batch-status">{{ batchImportStatus }}</small>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-danger me-auto" *ngIf="importInProgress" (click)="cancelBatchImport()">取消导入</button>
|
|
<button type="button" class="btn btn-secondary" (click)="closeBatchImportModal()">关闭</button>
|
|
<button type="button" class="btn btn-primary" (click)="startBatchImport()" [disabled]="importInProgress">导入 URL</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Activity drawer -->
|
|
<div class="scrim" *ngIf="activityOpen" (click)="activityOpen = false"></div>
|
|
<aside class="activity" [class.show]="activityOpen" aria-label="活动日志">
|
|
<div class="activity-head">
|
|
<span class="activity-title">活动日志</span>
|
|
<button type="button" class="icon-btn neutral" (click)="activityOpen = false" aria-label="关闭">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="activity-body">
|
|
<div *ngIf="events.length === 0" class="activity-empty">暂无事件</div>
|
|
<div *ngFor="let event of events" class="activity-item">
|
|
<div class="activity-msg">{{ event.message }}</div>
|
|
<div class="activity-url" *ngIf="event.url" [title]="event.url">{{ event.url }}</div>
|
|
<div class="activity-time num">{{ getRelativeTime(event.timestamp) }}</div>
|
|
</div>
|
|
<button type="button" class="ghost-btn activity-clear" (click)="clearEvents()">清空日志</button>
|
|
</div>
|
|
</aside>
|
|
|
|
<app-toasts></app-toasts>
|