mirror of
https://github.com/alexta69/metube.git
synced 2025-04-02 20:07:36 +03:00
add quality selection
This commit is contained in:
parent
34ec89485f
commit
13e690dd63
7 changed files with 38 additions and 16 deletions
|
@ -62,9 +62,10 @@ dqueue = DownloadQueue(config, Notifier())
|
||||||
async def add(request):
|
async def add(request):
|
||||||
post = await request.json()
|
post = await request.json()
|
||||||
url = post.get('url')
|
url = post.get('url')
|
||||||
if not url:
|
quality = post.get('quality')
|
||||||
|
if not url or not quality:
|
||||||
raise web.HTTPBadRequest()
|
raise web.HTTPBadRequest()
|
||||||
status = await dqueue.add(url)
|
status = await dqueue.add(url, quality)
|
||||||
return web.Response(text=serializer.encode(status))
|
return web.Response(text=serializer.encode(status))
|
||||||
|
|
||||||
@routes.post(config.URL_PREFIX + 'delete')
|
@routes.post(config.URL_PREFIX + 'delete')
|
||||||
|
|
22
app/ytdl.py
22
app/ytdl.py
|
@ -32,8 +32,15 @@ class DownloadInfo:
|
||||||
class Download:
|
class Download:
|
||||||
manager = None
|
manager = None
|
||||||
|
|
||||||
def __init__(self, download_dir, info):
|
def __init__(self, download_dir, quality, info):
|
||||||
self.download_dir = download_dir
|
self.download_dir = download_dir
|
||||||
|
if quality == 'best':
|
||||||
|
self.format = None
|
||||||
|
elif quality in ('1080p', '720p', '480p'):
|
||||||
|
res = quality[:-1]
|
||||||
|
self.format = f'bestvideo[height<={res}]+bestaudio/best[height<={res}]'
|
||||||
|
else:
|
||||||
|
raise Exception(f'unknown quality {quality}')
|
||||||
self.info = info
|
self.info = info
|
||||||
self.canceled = False
|
self.canceled = False
|
||||||
self.tmpfilename = None
|
self.tmpfilename = None
|
||||||
|
@ -49,6 +56,7 @@ class Download:
|
||||||
'no_color': True,
|
'no_color': True,
|
||||||
#'skip_download': True,
|
#'skip_download': True,
|
||||||
'outtmpl': os.path.join(self.download_dir, '%(title)s.%(ext)s'),
|
'outtmpl': os.path.join(self.download_dir, '%(title)s.%(ext)s'),
|
||||||
|
'format': self.format,
|
||||||
'cachedir': False,
|
'cachedir': False,
|
||||||
'socket_timeout': 30,
|
'socket_timeout': 30,
|
||||||
'progress_hooks': [lambda d: self.status_queue.put(d)],
|
'progress_hooks': [lambda d: self.status_queue.put(d)],
|
||||||
|
@ -121,29 +129,29 @@ class DownloadQueue:
|
||||||
'extract_flat': True,
|
'extract_flat': True,
|
||||||
}).extract_info(url, download=False)
|
}).extract_info(url, download=False)
|
||||||
|
|
||||||
async def __add_entry(self, entry, already):
|
async def __add_entry(self, entry, quality, already):
|
||||||
etype = entry.get('_type') or 'video'
|
etype = entry.get('_type') or 'video'
|
||||||
if etype == 'playlist':
|
if etype == 'playlist':
|
||||||
entries = entry['entries']
|
entries = entry['entries']
|
||||||
log.info(f'playlist detected with {len(entries)} entries')
|
log.info(f'playlist detected with {len(entries)} entries')
|
||||||
results = []
|
results = []
|
||||||
for etr in entries:
|
for etr in entries:
|
||||||
results.append(await self.__add_entry(etr, already))
|
results.append(await self.__add_entry(etr, quality, already))
|
||||||
if any(res['status'] == 'error' for res in results):
|
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': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)}
|
||||||
return {'status': 'ok'}
|
return {'status': 'ok'}
|
||||||
elif etype == 'video' or etype == 'url' and 'id' in entry:
|
elif etype == 'video' or etype == 'url' and 'id' in entry:
|
||||||
if entry['id'] not in self.queue:
|
if entry['id'] not in self.queue:
|
||||||
dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'])
|
dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'])
|
||||||
self.queue[entry['id']] = Download(self.config.DOWNLOAD_DIR, dl)
|
self.queue[entry['id']] = Download(self.config.DOWNLOAD_DIR, quality, dl)
|
||||||
self.event.set()
|
self.event.set()
|
||||||
await self.notifier.added(dl)
|
await self.notifier.added(dl)
|
||||||
return {'status': 'ok'}
|
return {'status': 'ok'}
|
||||||
elif etype == 'url':
|
elif etype == 'url':
|
||||||
return await self.add(entry['url'], already)
|
return await self.add(entry['url'], quality, already)
|
||||||
return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'}
|
return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'}
|
||||||
|
|
||||||
async def add(self, url, already=None):
|
async def add(self, url, quality, already=None):
|
||||||
log.info(f'adding {url}')
|
log.info(f'adding {url}')
|
||||||
already = set() if already is None else already
|
already = set() if already is None else already
|
||||||
if url in already:
|
if url in already:
|
||||||
|
@ -155,7 +163,7 @@ class DownloadQueue:
|
||||||
entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url)
|
entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url)
|
||||||
except youtube_dl.utils.YoutubeDLError as exc:
|
except youtube_dl.utils.YoutubeDLError as exc:
|
||||||
return {'status': 'error', 'msg': str(exc)}
|
return {'status': 'error', 'msg': str(exc)}
|
||||||
return await self.__add_entry(entry, already)
|
return await self.__add_entry(entry, quality, already)
|
||||||
|
|
||||||
async def cancel(self, ids):
|
async def cancel(self, ids):
|
||||||
for id in ids:
|
for id in ids:
|
||||||
|
|
BIN
screenshot.gif
BIN
screenshot.gif
Binary file not shown.
Before Width: | Height: | Size: 979 KiB After Width: | Height: | Size: 885 KiB |
|
@ -19,11 +19,14 @@
|
||||||
<div class="input-group add-url-box">
|
<div class="input-group add-url-box">
|
||||||
<input type="text" class="form-control" placeholder="Video or playlist URL" name="addUrl" [(ngModel)]="addUrl" [disabled]="addInProgress || downloads.loading">
|
<input type="text" class="form-control" placeholder="Video or playlist URL" name="addUrl" [(ngModel)]="addUrl" [disabled]="addInProgress || downloads.loading">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-primary" type="submit" (click)="addDownload()" [disabled]="addInProgress || downloads.loading">
|
<select class="custom-select" name="quality" [(ngModel)]="quality" [disabled]="addInProgress || downloads.loading">
|
||||||
<span class="spinner-border spinner-border-sm" role="status" id="add-spinner" *ngIf="addInProgress"></span>
|
<option *ngFor="let q of qualities" [ngValue]="q.id">{{ q.text }}</option>
|
||||||
{{ addInProgress ? "Adding..." : "Add" }}
|
</select>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<button class="btn btn-primary add-url" type="submit" (click)="addDownload()" [disabled]="addInProgress || downloads.loading">
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" id="add-spinner" *ngIf="addInProgress"></span>
|
||||||
|
{{ addInProgress ? "Adding..." : "Add" }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
max-width: 720px
|
max-width: 720px
|
||||||
margin: 4rem auto
|
margin: 4rem auto
|
||||||
|
|
||||||
|
button.add-url
|
||||||
|
min-width: 7rem
|
||||||
|
|
||||||
$metube-section-color-bg: rgba(0,0,0,.07)
|
$metube-section-color-bg: rgba(0,0,0,.07)
|
||||||
|
|
||||||
.metube-section-header
|
.metube-section-header
|
||||||
|
|
|
@ -11,6 +11,13 @@ import { MasterCheckboxComponent } from './master-checkbox.component';
|
||||||
})
|
})
|
||||||
export class AppComponent implements AfterViewInit {
|
export class AppComponent implements AfterViewInit {
|
||||||
addUrl: string;
|
addUrl: string;
|
||||||
|
qualities: Array<Object> = [
|
||||||
|
{id: "best", text: "Best"},
|
||||||
|
{id: "1080p", text: "1080p"},
|
||||||
|
{id: "720p", text: "720p"},
|
||||||
|
{id: "480p", text: "480p"}
|
||||||
|
];
|
||||||
|
quality: string = "best";
|
||||||
addInProgress = false;
|
addInProgress = false;
|
||||||
|
|
||||||
@ViewChild('queueMasterCheckbox', {static: false}) queueMasterCheckbox: MasterCheckboxComponent;
|
@ViewChild('queueMasterCheckbox', {static: false}) queueMasterCheckbox: MasterCheckboxComponent;
|
||||||
|
@ -61,7 +68,7 @@ export class AppComponent implements AfterViewInit {
|
||||||
|
|
||||||
addDownload() {
|
addDownload() {
|
||||||
this.addInProgress = true;
|
this.addInProgress = true;
|
||||||
this.downloads.add(this.addUrl).subscribe((status: Status) => {
|
this.downloads.add(this.addUrl, this.quality).subscribe((status: Status) => {
|
||||||
if (status.status === 'error') {
|
if (status.status === 'error') {
|
||||||
alert(`Error adding URL: ${status.msg}`);
|
alert(`Error adding URL: ${status.msg}`);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -79,8 +79,8 @@ export class DownloadsService {
|
||||||
return of({status: 'error', msg: msg})
|
return of({status: 'error', msg: msg})
|
||||||
}
|
}
|
||||||
|
|
||||||
public add(url: string) {
|
public add(url: string, quality: string) {
|
||||||
return this.http.post<Status>('add', {url: url}).pipe(
|
return this.http.post<Status>('add', {url: url, quality: quality}).pipe(
|
||||||
catchError(this.handleHTTPError)
|
catchError(this.handleHTTPError)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue