Add alot of feats
This commit is contained in:
@@ -65,33 +65,53 @@
|
||||
</div>
|
||||
<div class="card shadow mt-3">
|
||||
<div class="card-content p-3">
|
||||
<form [formGroup]="processingForm" (ngSubmit)="onSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label>MP4 Filename</label>
|
||||
<input type="text" formControlName="mp4Filename" class="form-control" placeholder="ex: S01E01">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label>Wanted Resolution</label>
|
||||
<select formControlName="wantedResolution" class="form-select">
|
||||
<option value="4k">4K</option>
|
||||
<option value="1080p" selected>1080p</option>
|
||||
<option value="720p">720p</option>
|
||||
<option value="480p">480p</option>
|
||||
<option value="360p">360p</option>
|
||||
<option value="240p">240p</option>
|
||||
</select>
|
||||
</div>
|
||||
<form [formGroup]="loadForm" (ngSubmit)="onSubmitLoad()">
|
||||
<div class="flex flex-col mt-2">
|
||||
<label>MP4 Filename</label>
|
||||
<input type="text" formControlName="mp4Filename" class="form-control" placeholder="ex: S01E01">
|
||||
</div>
|
||||
<div class="flex flex-col mt-2">
|
||||
<label>MPD URL</label>
|
||||
<input type="text" formControlName="mpdUrl" class="form-control" placeholder="ex: https://....">
|
||||
</div>
|
||||
<button type="submit" [disabled]="!loadForm.valid" class="btn btn-primary float-end mt-2"><i class="fa fa-download me-2"></i>Load</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow mt-3" *ngIf="loaded">
|
||||
<div class="card-content p-3">
|
||||
<form [formGroup]="processingForm" (ngSubmit)="onSubmit()">
|
||||
<div class="flex flex-col mt-2">
|
||||
<label>Wanted Resolution</label>
|
||||
<select formControlName="wantedResolution" class="form-select">
|
||||
<option [value]="res.name" *ngFor="let res of loaded.videoTracks">{{res.name}} ({{res.codec}} - {{humanFileSize(res.bandwidth)}})</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-8">
|
||||
<label class="mb-2">Wanted Audio Tracks</label>
|
||||
<div class="form-check form-switch" *ngFor="let audio of loaded.audioTracks; let index = index">
|
||||
<input [ngModelOptions]="{standalone: true}" [(ngModel)]="audio.selected" class="form-check-input" type="checkbox" role="switch" [id]="'audio_track_' + index">
|
||||
<label class="form-check-label" [for]="'audio_track_' + index">
|
||||
{{audio.name}} ({{audio.language}} - {{audio.attributes.CODECS}} - {{humanFileSize(audio.attributes.BANDWIDTH)}})
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label class="mb-2">Wanted Subtitles Tracks</label>
|
||||
<div class="form-check form-switch" *ngFor="let sub of loaded.subtitles; let index = index">
|
||||
<input [ngModelOptions]="{standalone: true}" [(ngModel)]="sub.selected" class="form-check-input" type="checkbox" role="switch" [id]="'sub_track_' + index">
|
||||
<label class="form-check-label" [for]="'sub_track_' + index">
|
||||
{{sub.name}} ({{sub.language}} - {{sub.attributes.CODECS}} - {{humanFileSize(sub.attributes.BANDWIDTH)}})
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col mt-2">
|
||||
<label>Widevine L3 Decryption Keys (one per line, key:value)</label>
|
||||
<textarea formControlName="keys" rows="4" class="form-control" placeholder="ex: ca2c28ab6c6a4e91935e12b6bb37a952:779afc2503a1ea7947b4747d24fbeb63"></textarea>
|
||||
</div>
|
||||
<button type="submit" [disabled]="!processingForm.valid" class="btn btn-primary float-end mt-2">Add to Queue</button>
|
||||
<button type="submit" [disabled]="!processingForm.valid" class="btn btn-primary float-end mt-2"><i class="fa fa-cog me-2"></i>Add to Queue</button>
|
||||
</form>
|
||||
|
||||
<!-- <div *ngIf="jobId" class="mt-6">
|
||||
@@ -111,7 +131,7 @@
|
||||
Jobs Queue ({{jobs.length}})
|
||||
<i [ngClass]="{'fa-beat-fade text-success': lastJobSuccess, 'text-danger': !lastJobSuccess}" class="fa fa-circle float-end mt-1"></i>
|
||||
</div>
|
||||
<div class="card-content p-2" style="max-height: 90vh;overflow-y:scroll">
|
||||
<div class="card-content p-2 px-3" style="max-height: 90vh;overflow-y:scroll">
|
||||
<div class="row" *ngIf="jobs.length === 0">
|
||||
<div class="col-12 text-center py-3">
|
||||
<!-- <i class="fa fa-circle-notch fa-spin fa-2x"></i> -->
|
||||
@@ -120,11 +140,21 @@
|
||||
</div>
|
||||
<div *ngFor="let job of jobs | orderBy: 'addedOn':true; let last = last" class="pb-2" [ngClass]="{'border-bottom mb-2': !last}">
|
||||
<div class="row">
|
||||
<div class="col-8 text-center border-right mb-2">
|
||||
<b>{{job.data.mp4Filename}}</b><br>
|
||||
</div>
|
||||
<div class="col-4 text-center mb-2">
|
||||
<i>{{job.data.wantedResolution || '1080p'}}</i>
|
||||
<div class="col-12">
|
||||
<div class="card my-2 border-0" style="background-color: #f5f6fa;">
|
||||
<div class="card-content p-2">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center mb-2">
|
||||
<h5 class="mb-0"><b>{{job.data.mp4Filename}}</b></h5>
|
||||
</div>
|
||||
<div class="col-12 mb-1">
|
||||
<i class="fa fa-display me-2 text-primary"></i><i>{{job.data.wantedResolution?.name || 'Unknown'}} @{{humanFileSize(job.data.wantedResolution.bandwidth)}} ({{job.data.wantedResolution.codec}})</i><br>
|
||||
<i class="fa fa-volume-high me-2 text-success"></i><i>{{displayJobAudio(job.data.wantedAudioTracks) || 'Unknown'}}</i><br>
|
||||
<i class="fa fa-closed-captioning me-2 text-warning"></i><i>{{displayJobAudio(job.data.wantedSubtitles) || 'Unknown'}}</i><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p class="text-sm mb-1">Job ID: {{ job.id }}</p>
|
||||
@@ -142,7 +172,7 @@
|
||||
<p class="text-sm mb-1">Duration: {{ ((job.finishedOn - job.processedOn)/1000) | amDuration:'seconds' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger mt-2" *ngIf="job.state === 'failed' && job.failedReason">
|
||||
<div class="alert alert-danger mt-2 mb-1" *ngIf="job.state === 'failed' && job.failedReason">
|
||||
{{job.failedReason}}
|
||||
</div>
|
||||
<div class="alert alert-success mt-2 mb-4" *ngIf="job.state === 'completed' && job.returnValue?.message">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators, FormsModule } from '@angular/forms';
|
||||
import { VideoProcessingService } from './video-processing.service';
|
||||
import { MomentModule } from 'ngx-moment';
|
||||
import { OrderModule } from 'ngx-order-pipe';
|
||||
@@ -8,35 +8,41 @@ import { OrderModule } from 'ngx-order-pipe';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, MomentModule, OrderModule],
|
||||
imports: [CommonModule, ReactiveFormsModule, MomentModule, OrderModule, FormsModule],
|
||||
templateUrl: './app.component.html',
|
||||
styles: []
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
processingForm: FormGroup;
|
||||
loadForm: FormGroup;
|
||||
jobId: string | null = null;
|
||||
jobStatus: string | null = null;
|
||||
jobProgress: number = 0;
|
||||
jobs: Array<any> = [];
|
||||
lastJobSuccess: boolean = false;
|
||||
welcomeHeader: any = null;
|
||||
loaded: any = null;
|
||||
|
||||
initForm() {
|
||||
this.processingForm = this.fb.group({
|
||||
mp4Filename: ['', Validators.required],
|
||||
mpdUrl: ['', Validators.required],
|
||||
keys: ['', Validators.required],
|
||||
wantedResolution: ['1080p', Validators.required]
|
||||
wantedResolution: ['1920x1080', Validators.required]
|
||||
});
|
||||
this.loadForm = this.fb.group({
|
||||
mp4Filename: ['test', Validators.required],
|
||||
mpdUrl: ['https://bakery.pplus.paramount.tech/l(de,it,no,fi,da,sv,es-MX,pt-BR,es-mx,pt-br)/paramountplus/2023/11/06/2279898691624/2416513_cenc_precon_dash/stream.mpd?CMCD=ot%3Dm%2Csf%3Dd%2Csid%3D%22295d7a15-c79d-4229-b593-7abdacd727c9%22%2Csu', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
constructor(private fb: FormBuilder,
|
||||
private videoProcessingService: VideoProcessingService) {
|
||||
this.processingForm = this.fb.group({
|
||||
mp4Filename: ['', Validators.required],
|
||||
mpdUrl: ['', Validators.required],
|
||||
keys: ['', Validators.required],
|
||||
wantedResolution: ['1080p', Validators.required]
|
||||
wantedResolution: ['1920x1080', Validators.required]
|
||||
});
|
||||
this.loadForm = this.fb.group({
|
||||
mp4Filename: ['test', Validators.required],
|
||||
mpdUrl: ['https://bakery.pplus.paramount.tech/l(de,it,no,fi,da,sv,es-MX,pt-BR,es-mx,pt-br)/paramountplus/2023/11/06/2279898691624/2416513_cenc_precon_dash/stream.mpd?CMCD=ot%3Dm%2Csf%3Dd%2Csid%3D%22295d7a15-c79d-4229-b593-7abdacd727c9%22%2Csu', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,13 +99,24 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
humanFileSize(size: number) {
|
||||
var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
|
||||
return +((size / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['bps', 'kbps', 'mbps', 'gbps', 'tbps'][i];
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.processingForm.valid) {
|
||||
const formData = this.processingForm.value;
|
||||
|
||||
this.videoProcessingService.startProcess(Object.assign(formData, {
|
||||
keys: this.parseKeys(formData.keys)
|
||||
})).subscribe({
|
||||
const data = Object.assign(formData, {
|
||||
keys: this.parseKeys(formData.keys),
|
||||
wantedAudioTracks: this.loaded.audioTracks.filter((track: any) => track.selected),
|
||||
wantedSubtitles: this.loaded.subtitles.filter((sub: any) => sub.selected),
|
||||
wantedResolution: this.loaded.videoTracks.find((res: any) => res.name === formData.wantedResolution),
|
||||
mpdUrl: this.loadForm.get('mpdUrl')?.value,
|
||||
mp4Filename: this.loadForm.get('mp4Filename')?.value,
|
||||
});
|
||||
console.log(data);
|
||||
this.videoProcessingService.startProcess(data).subscribe({
|
||||
next: (response) => {
|
||||
//this.jobId = response.jobId;
|
||||
},
|
||||
@@ -107,7 +124,31 @@ export class AppComponent implements OnInit {
|
||||
console.error('Error starting process:', error);
|
||||
}
|
||||
});
|
||||
this.initForm();
|
||||
//this.initForm();
|
||||
}
|
||||
}
|
||||
|
||||
displayJobAudio(tracks: any) {
|
||||
if (tracks.length === 0)
|
||||
return 'None';
|
||||
return tracks.map((elem: any) => `${elem.name} (${elem.attributes.CODECS})`).join(' + ');
|
||||
}
|
||||
|
||||
onSubmitLoad() {
|
||||
if (this.loadForm.valid) {
|
||||
this.loaded = null;
|
||||
const formData = this.loadForm.value;
|
||||
|
||||
this.videoProcessingService.load(formData).subscribe({
|
||||
next: (response) => {
|
||||
console.log(response);
|
||||
this.loaded = response;
|
||||
//this.jobId = response.jobId;
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error starting process:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ export class VideoProcessingService {
|
||||
return this.http.post<{ jobId: string }>(`${this.apiUrl}/start-process`, data);
|
||||
}
|
||||
|
||||
load(data: any): Observable<any> {
|
||||
return this.http.post<any>(`${this.apiUrl}/processMPD`, data);
|
||||
}
|
||||
|
||||
getJobsStatus(): Observable<Array<any>> {
|
||||
return this.http.get<Array<any>>(`${this.apiUrl}/jobs-status`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user