dev@170
|
1 /**
|
dev@170
|
2 * Created by lucast on 21/03/2017.
|
dev@170
|
3 */
|
dev@231
|
4 import {
|
dev@231
|
5 ChangeDetectionStrategy,
|
dev@231
|
6 Component,
|
dev@231
|
7 Input,
|
dev@408
|
8 OnDestroy,
|
dev@456
|
9 OnInit,
|
dev@456
|
10 Output,
|
dev@456
|
11 EventEmitter
|
dev@236
|
12 } from '@angular/core';
|
dev@348
|
13 import {naivePagingMapper} from '../visualisations/WavesJunk';
|
dev@408
|
14 import {OnSeekHandler} from '../playhead/PlayHeadHelpers';
|
dev@361
|
15 import {
|
dev@381
|
16 defaultColourGenerator,
|
dev@460
|
17 HigherLevelFeatureShape
|
dev@361
|
18 } from '../visualisations/FeatureUtilities';
|
dev@408
|
19 import {
|
dev@408
|
20 RenderLoopService,
|
dev@408
|
21 TaskRemover
|
dev@408
|
22 } from '../services/render-loop/render-loop.service';
|
dev@456
|
23 import {DomSanitizer} from '@angular/platform-browser';
|
dev@460
|
24 import {
|
dev@460
|
25 isExtractedAnalysisItem,
|
dev@460
|
26 isLoadedRootAudioItem,
|
dev@460
|
27 isPendingAnalysisItem,
|
dev@460
|
28 isPendingRootAudioItem,
|
dev@460
|
29 Item,
|
dev@460
|
30 RootAudioItem
|
dev@460
|
31 } from './AnalysisItem';
|
dev@170
|
32
|
dev@170
|
33 @Component({
|
dev@170
|
34 selector: 'ugly-analysis-item',
|
dev@170
|
35 templateUrl: './analysis-item.component.html',
|
dev@231
|
36 styleUrls: ['./analysis-item.component.css'],
|
dev@231
|
37 changeDetection: ChangeDetectionStrategy.OnPush
|
dev@170
|
38 })
|
dev@408
|
39 export class AnalysisItemComponent implements OnInit, OnDestroy {
|
dev@224
|
40
|
dev@408
|
41 // TODO should be TimelineTimeContext?
|
dev@408
|
42 @Input() set timeline(timeline: Timeline) {
|
dev@408
|
43 this.mTimeline = timeline;
|
dev@408
|
44 this.resetRemoveAnimation();
|
dev@408
|
45 }
|
dev@408
|
46
|
dev@408
|
47 get timeline(): Timeline {
|
dev@408
|
48 return this.mTimeline;
|
dev@408
|
49 }
|
dev@408
|
50
|
dev@408
|
51 @Input() set isActive(isActive: boolean) {
|
dev@408
|
52 this.removeAnimation();
|
dev@408
|
53 this.mIsActive = isActive;
|
dev@408
|
54 if (isActive) {
|
dev@408
|
55 this.resetRemoveAnimation();
|
dev@408
|
56 }
|
dev@408
|
57 }
|
dev@408
|
58
|
dev@408
|
59 get isActive() {
|
dev@408
|
60 return this.mIsActive;
|
dev@408
|
61 }
|
dev@408
|
62
|
dev@350
|
63 @Input() item: Item;
|
dev@285
|
64 @Input() contentWidth: number;
|
dev@348
|
65 @Input() onSeek: OnSeekHandler;
|
dev@456
|
66 @Output() remove: EventEmitter<Item>;
|
dev@408
|
67 // TODO move / re-think - naivePagingMapper feels like a big ol' bodge
|
dev@408
|
68 private removeAnimation: TaskRemover;
|
dev@224
|
69 private hasProgressOnInit = false;
|
dev@408
|
70 private mIsActive: boolean;
|
dev@408
|
71 private mTimeline: Timeline;
|
dev@224
|
72
|
dev@456
|
73 constructor(private renderLoop: RenderLoopService,
|
dev@456
|
74 private sanitizer: DomSanitizer) {
|
dev@456
|
75 this.remove = new EventEmitter<Item>();
|
dev@456
|
76 }
|
dev@348
|
77
|
dev@224
|
78 ngOnInit(): void {
|
dev@408
|
79 this.resetRemoveAnimation();
|
dev@231
|
80 this.hasProgressOnInit = this.item.progress != null;
|
dev@224
|
81 }
|
dev@224
|
82
|
dev@224
|
83 isLoading(): boolean {
|
dev@231
|
84 return this.hasProgressOnInit && this.item.progress < 100;
|
dev@224
|
85 }
|
dev@348
|
86
|
dev@348
|
87 isAudioItem(): boolean {
|
dev@460
|
88 return this.item && isLoadedRootAudioItem(this.item);
|
dev@348
|
89 }
|
dev@361
|
90
|
dev@410
|
91 isPending(): boolean {
|
dev@410
|
92 return this.item &&
|
dev@460
|
93 !isLoadedRootAudioItem(this.item) && !isExtractedAnalysisItem(this.item) &&
|
dev@410
|
94 (isPendingAnalysisItem(this.item) || isPendingRootAudioItem(this.item));
|
dev@410
|
95 }
|
dev@410
|
96
|
dev@361
|
97 getFeatureShape(): HigherLevelFeatureShape | null {
|
dev@361
|
98 return !isPendingRootAudioItem(this.item) &&
|
dev@460
|
99 isExtractedAnalysisItem(this.item) ? this.item.shape : null;
|
dev@361
|
100 }
|
dev@381
|
101
|
dev@412
|
102 getDuration(): number | null {
|
dev@460
|
103 if (isLoadedRootAudioItem(this.item)) {
|
dev@412
|
104 return this.item.audioData.duration;
|
dev@412
|
105 }
|
dev@460
|
106 if (isExtractedAnalysisItem(this.item)) {
|
dev@412
|
107 return this.item.parent.audioData.duration;
|
dev@412
|
108 }
|
dev@412
|
109 }
|
dev@412
|
110
|
dev@381
|
111 getNextColour(): string {
|
dev@381
|
112 return defaultColourGenerator.next().value;
|
dev@381
|
113 }
|
dev@408
|
114
|
dev@408
|
115 ngOnDestroy(): void {
|
dev@408
|
116 this.removeAnimation();
|
dev@408
|
117 }
|
dev@408
|
118
|
dev@456
|
119 private sanitize(url: string) {
|
dev@456
|
120 return this.sanitizer.bypassSecurityTrustUrl(url);
|
dev@456
|
121 }
|
dev@456
|
122
|
dev@460
|
123 private generateFilename(item: RootAudioItem): string {
|
dev@456
|
124 // TODO this is too brittle, and will often produce the wrong result
|
dev@456
|
125 // i.e. audio/mpeg results in .mpeg, when .mp3 is likely desired
|
dev@456
|
126 const mimeParts = item.mimeType ? item.mimeType.split('/') : [];
|
dev@456
|
127 const extension = mimeParts.length === 2 ? mimeParts[1] : '';
|
dev@456
|
128 return `${item.title}.${extension}`;
|
dev@456
|
129 }
|
dev@456
|
130
|
dev@408
|
131 private resetRemoveAnimation(): void {
|
dev@408
|
132 if (this.removeAnimation) {
|
dev@408
|
133 this.removeAnimation();
|
dev@408
|
134 }
|
dev@408
|
135 const createPagingTask = () => {
|
dev@408
|
136 const pagingMapper = naivePagingMapper(this.timeline);
|
dev@408
|
137 return this.renderLoop.addPlayingTask(currentTime => {
|
dev@408
|
138 pagingMapper(currentTime);
|
dev@408
|
139 });
|
dev@408
|
140 };
|
dev@408
|
141 // only add a pager to audio items, it can drive the feature items
|
dev@408
|
142 const remover = this.timeline && this.isAudioItem() ?
|
dev@408
|
143 createPagingTask() : () => {};
|
dev@408
|
144 this.removeAnimation = () => {
|
dev@408
|
145 remover();
|
dev@408
|
146 this.removeAnimation = () => {};
|
dev@408
|
147 };
|
dev@408
|
148 }
|
dev@170
|
149 }
|