Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(representation): implement video player incl. preview (DEV-701) #698

Merged
merged 28 commits into from Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e956a92
feat(representation): implement video player incl. preview
kilchenmann Mar 30, 2022
97d673f
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Mar 30, 2022
75a9e8a
refactor: format code
kilchenmann Mar 30, 2022
e6fa819
refactor(av): better host handler
kilchenmann Mar 31, 2022
95893c0
refactor(representation): clean up code in video components
kilchenmann Mar 31, 2022
865a52c
test(pipe): test time pipe
kilchenmann Mar 31, 2022
9811081
test(representation): fix and write video tests
kilchenmann Mar 31, 2022
e68a2fa
refactor(representation): video details
kilchenmann Mar 31, 2022
0601d68
chore(deps): bump js-lib to latest
kilchenmann Apr 2, 2022
581bc59
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Apr 6, 2022
54f04c3
feat(video): replace moving image file functionality
kilchenmann Apr 6, 2022
d49af26
fix(video): set correct preview position on timeline
kilchenmann Apr 8, 2022
ff0f265
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Apr 8, 2022
7730655
style(preview): hide preview on click
kilchenmann Apr 8, 2022
d6b223c
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Apr 8, 2022
5e3de1c
refactor(av-timeline): clean up code and add more comments
kilchenmann Apr 8, 2022
c35825b
refactor(video-preview): clean up code and add more comments
kilchenmann Apr 8, 2022
ea031a7
refactor(video): clean up code and add more comments
kilchenmann Apr 8, 2022
5d7ca35
refactor(video): clean up code and add more comments
kilchenmann Apr 8, 2022
9a0574d
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Apr 13, 2022
a9df369
feat(video): matrix file error handler and code refactoring
kilchenmann Apr 21, 2022
f6825fb
fix(video): better calc
kilchenmann Apr 21, 2022
28c5760
Merge branch 'main' into wip/dev-701-video-player
kilchenmann Apr 21, 2022
e5db7c7
refactor(video): clean up code
kilchenmann Apr 21, 2022
b91ea16
Merge branch 'wip/dev-701-video-player' of https://github.com/dasch-s…
kilchenmann Apr 21, 2022
392677e
refactor(video): remove commented code and fix typo
kilchenmann Apr 21, 2022
e4e740b
fix(video): update time after replacing video file
kilchenmann Apr 21, 2022
a6527d4
fix(representation): replace file only, if data exists
kilchenmann Apr 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/app/app.module.ts
Expand Up @@ -30,6 +30,7 @@ import { DspApiConfigToken, DspApiConnectionToken, DspAppConfigToken, DspInstrum
import { DialogHeaderComponent } from './main/dialog/dialog-header/dialog-header.component';
import { DialogComponent } from './main/dialog/dialog.component';
import { AdminImageDirective } from './main/directive/admin-image/admin-image.directive';
import { DisableContextMenuDirective } from './main/directive/disable-context-menu.directive';
import { ExistingNameDirective } from './main/directive/existing-name/existing-name.directive';
import { ExternalLinksDirective } from './main/directive/external-links.directive';
import { GndDirective } from './main/directive/gnd/gnd.directive';
Expand All @@ -47,6 +48,7 @@ import { LinkifyPipe } from './main/pipes/string-transformation/linkify.pipe';
import { StringifyStringLiteralPipe } from './main/pipes/string-transformation/stringify-string-literal.pipe';
import { TitleFromCamelCasePipe } from './main/pipes/string-transformation/title-from-camel-case.pipe';
import { TruncatePipe } from './main/pipes/string-transformation/truncate.pipe';
import { TimePipe } from './main/pipes/time.pipe';
import { SelectLanguageComponent } from './main/select-language/select-language.component';
import { DatadogRumService } from './main/services/datadog-rum.service';
import { MaterialModule } from './material-module';
Expand Down Expand Up @@ -98,9 +100,13 @@ import { PropertiesComponent } from './workspace/resource/properties/properties.
import { AddRegionFormComponent } from './workspace/resource/representation/add-region-form/add-region-form.component';
import { ArchiveComponent } from './workspace/resource/representation/archive/archive.component';
import { AudioComponent } from './workspace/resource/representation/audio/audio.component';
import { AvTimelineComponent } from './workspace/resource/representation/av-timeline/av-timeline.component';
import { DocumentComponent } from './workspace/resource/representation/document/document.component';
import { ReplaceFileFormComponent } from './workspace/resource/representation/replace-file-form/replace-file-form.component';
import { StillImageComponent } from './workspace/resource/representation/still-image/still-image.component';
import { UploadComponent } from './workspace/resource/representation/upload/upload.component';
import { VideoPreviewComponent } from './workspace/resource/representation/video/video-preview/video-preview.component';
import { VideoComponent } from './workspace/resource/representation/video/video.component';
import { ResourceInstanceFormComponent } from './workspace/resource/resource-instance-form/resource-instance-form.component';
import { SelectOntologyComponent } from './workspace/resource/resource-instance-form/select-ontology/select-ontology.component';
import { SelectProjectComponent } from './workspace/resource/resource-instance-form/select-project/select-project.component';
Expand Down Expand Up @@ -153,7 +159,6 @@ import { SearchSelectOntologyComponent } from './workspace/search/advanced-searc
import { ExpertSearchComponent } from './workspace/search/expert-search/expert-search.component';
import { FulltextSearchComponent } from './workspace/search/fulltext-search/fulltext-search.component';
import { SearchPanelComponent } from './workspace/search/search-panel/search-panel.component';
import { ReplaceFileFormComponent } from './workspace/resource/representation/replace-file-form/replace-file-form.component';

// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
Expand All @@ -172,6 +177,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
AppComponent,
ArchiveComponent,
AudioComponent,
AvTimelineComponent,
BoardComponent,
BooleanValueComponent,
CollaborationComponent,
Expand All @@ -190,6 +196,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
DecimalValueComponent,
DialogComponent,
DialogHeaderComponent,
DisableContextMenuDirective,
DisplayEditComponent,
DocumentComponent,
DragDropDirective,
Expand Down Expand Up @@ -241,6 +248,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
PropertiesComponent,
PropertyFormComponent,
PropertyInfoComponent,
ReplaceFileFormComponent,
ResourceAndPropertySelectionComponent,
ResourceClassFormComponent,
ResourceClassInfoComponent,
Expand Down Expand Up @@ -285,6 +293,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
TextValueAsXMLComponent,
TextValueHtmlLinkDirective,
TimeInputComponent,
TimePipe,
TimeValueComponent,
TitleFromCamelCasePipe,
TruncatePipe,
Expand All @@ -295,7 +304,8 @@ export function httpLoaderFactory(httpClient: HttpClient) {
UserMenuComponent,
UsersComponent,
UsersListComponent,
ReplaceFileFormComponent,
VideoComponent,
VideoPreviewComponent
],
imports: [
AngularSplitModule.forRoot(),
Expand Down
2 changes: 1 addition & 1 deletion src/app/main/dialog/dialog.component.html
Expand Up @@ -394,7 +394,7 @@

<div *ngSwitchCase="'replaceFile'">
<app-dialog-header [title]="data.title" [subtitle]="data.subtitle"></app-dialog-header>
<mat-dialog-content>
<mat-dialog-content class="form-content">
<app-replace-file-form [representation]="data.representation" [propId]="data.id" (closeDialog)="dialogRef.close($event)"></app-replace-file-form>
</mat-dialog-content>
</div>
Expand Down
8 changes: 8 additions & 0 deletions src/app/main/directive/disable-context-menu.directive.spec.ts
@@ -0,0 +1,8 @@
import { DisableContextMenuDirective } from './disable-context-menu.directive';

describe('DisableContextMenuDirective', () => {
it('should create an instance', () => {
const directive = new DisableContextMenuDirective();
expect(directive).toBeTruthy();
});
});
17 changes: 17 additions & 0 deletions src/app/main/directive/disable-context-menu.directive.ts
@@ -0,0 +1,17 @@
import { Directive, HostListener } from '@angular/core';

@Directive({
selector: '[appDisableContextMenu]'
})
export class DisableContextMenuDirective {

constructor() { }

@HostListener('contextmenu', ['$event'])

onRightClick(event: Event) {
event.preventDefault();
}


}
27 changes: 27 additions & 0 deletions src/app/main/pipes/time.pipe.spec.ts
@@ -0,0 +1,27 @@
import { TimePipe } from './time.pipe';

describe('TimePipe', () => {

let pipe = new TimePipe();

beforeEach(() => {
pipe = new TimePipe();
});

it('create an instance', () => {
expect(pipe).toBeTruthy();
});


it('should convert 123 seconds into 2 minutes and 3 seconds', () => {
const seconds = 123;
const time = pipe.transform(seconds);
expect(time).toEqual('02:03');
});

it('should convert 12342 seconds into 3 hours 25 minutes and 42 seconds', () => {
const seconds = 12342;
const time = pipe.transform(seconds);
expect(time).toEqual('03:25:42');
});
});
30 changes: 30 additions & 0 deletions src/app/main/pipes/time.pipe.ts
@@ -0,0 +1,30 @@
import { Pipe, PipeTransform } from '@angular/core';

/**
* the TimePipe transforms n seconds to hh:mm:ss
* or in case of zero hours to mm:ss
*/
@Pipe({
name: 'appTime'
})
export class TimePipe implements PipeTransform {

transform(value: number): string {

const dateObj: Date = new Date(value * 1000);
const hours: number = dateObj.getUTCHours();
const minutes = dateObj.getUTCMinutes();
const seconds = dateObj.getSeconds();

if (hours === 0) {
return minutes.toString().padStart(2, '0') + ':' +
seconds.toString().padStart(2, '0');
} else {
return hours.toString().padStart(2, '0') + ':' +
minutes.toString().padStart(2, '0') + ':' +
seconds.toString().padStart(2, '0');
}

}

}
6 changes: 3 additions & 3 deletions src/app/main/services/notification.service.ts
Expand Up @@ -16,7 +16,7 @@ export class NotificationService {
// todo: maybe we can add more parameters like:
// action: string = 'x', duration: number = 4200
// and / or type: 'note' | 'warning' | 'error' | 'success'; which can be used for the panelClass
openSnackBar(notification: string | ApiResponseError): void {
openSnackBar(notification: string | ApiResponseError, type?: 'success' | 'error'): void {
let duration = 5000;
let message: string;
let panelClass: string;
Expand All @@ -30,10 +30,10 @@ export class NotificationService {
const defaultStatusMsg = this._statusMsg.default;
message = `${defaultStatusMsg[notification.status].message} (${notification.status}): ${defaultStatusMsg[notification.status].description}`;
}
panelClass = 'error';
panelClass = type ? type : 'error';
} else {
message = notification;
panelClass = 'success';
panelClass = type ? type : 'success';
}

this._snackBar.open(message, 'x', {
Expand Down
Expand Up @@ -20,7 +20,6 @@ export class ArchiveComponent implements OnInit {
@Input() parentResource: ReadResource;

originalFilename: string;
temp: string;
kilchenmann marked this conversation as resolved.
Show resolved Hide resolved

constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
Expand Down
@@ -0,0 +1,10 @@
<div class="timeline-wrapper" #timeline>
<div class="progress-wrapper" #progress>
<div class="progress-background"></div>
<div class="progress-buffer"></div>
<div class="progress-fill"></div>
</div>
<div class="thumb" [class.dragging]="dragging" #thumb cdkDragLockAxis="x" cdkDrag
(cdkDragStarted)="toggleDragging()" (cdkDragEnded)="toggleDragging()" (cdkDragMoved)="dragAction($event)"
cdkDragBoundary=".timeline-wrapper"> </div>
</div>
@@ -0,0 +1,63 @@
// timeline / progress bar
.timeline-wrapper {
width: calc(100% - 32px);
height: 24px;
top: 0;
position: absolute;
cursor: pointer;

.progress-wrapper {
width: 100%;
height: 2px;
padding: 11px 0;
position: absolute;
overflow: hidden;
display: flex;

.progress-background,
// .progress-buffer,
.progress-fill {
height: 2px;
width: 100%;
}
.progress-background {
background-color: whitesmoke;
position: absolute;
transform-origin: 100% 100%;
transition: transform 20ms cubic-bezier(0.25, 0.8, 0.25, 1),
background-color 20ms cubic-bezier(0.25, 0.8, 0.25, 1);
}

.progress-fill {
background-color: red;
position: absolute;
transform: scaleX(0);
transform-origin: 0 0;
transition: transform 20ms cubic-bezier(0.25, 0.8, 0.25, 1),
background-color 20ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
}

.thumb {
cursor: grab;
position: absolute;
left: -7px;
bottom: 2px;
box-sizing: border-box;
width: 20px;
height: 20px;
border: 3px solid transparent;
border-radius: 50%;
background-color: red;
transform: scale(0.7);
transition: transform 20ms cubic-bezier(0.25, 0.8, 0.25, 1),
background-color 20ms cubic-bezier(0.25, 0.8, 0.25, 1),
border-color 20ms cubic-bezier(0.25, 0.8, 0.25, 1);
transition: none;

&.dragging {
cursor: grabbing;
border: 3px solid red;
}
}
}
@@ -0,0 +1,27 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { AvTimelineComponent } from './av-timeline.component';

describe('AvTimelineComponent', () => {
let component: AvTimelineComponent;
let fixture: ComponentFixture<AvTimelineComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [
AvTimelineComponent
]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(AvTimelineComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});