{"version":3,"file":"6845.e702eeccff39072a.js","mappings":"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","names":["PAGE","ClipsService","_ClipsService","constructor","store","sentryService","clipsApi","youtubeService","analyticsService","this","getPoster","clip","res","poster","DEFAULT_POSTER","youtube_id","getThumbnail","url","createClipFromFilestack","filestackResult","projectId","userId","_this","_asyncToGenerator","uploadTags","lastModifiedDate","duration","environment","production","console","log","warn","Error","newVideo","filename","type","mimetype","size","Date","toISOString","loading","originalFile","lastModified","convertVideoFileToClip","source","src","filestackConfig","storeTo","path","orig","key","name","file","id","Utils","removeFileExt","filmingDate","getDateTimeString","createClip","then","newClip","subClipsUpdated","dispatch","mystackActions","toFixed","seconds","durationInSeconds","clipUpload","error","DEBUG_LOGS","hlsMeta","HLS_META_TRANSCODE","Clip","clipActions","catch","err","Array","isArray","errors","length","errorType","info","message","params","sub","subscribeClipsUpdated","subscribe","next","updates","JSON","parse","hlsSrc","sources","Object","keys","isHlsComplete","unsubscribe","captureError","errorMessage","complete","getPosterTimeFromPoster","sNum","substring","lastIndexOf","parsed","parseInt","updatePosterToPosterTime","posterTime","newPoster","slice","startIndex","endIndex","handleSubClips","i0","i1","i2","i3","i4","i5","factory","ɵfac","providedIn","ClipEditPage_ng_template_7_span_7_Template","ctx_r1","returnUrl","defaultReturnUrl","selectedSegment","clip$","ClipEditPage","_ClipEditPage","clipsService","route","router","location","title","segmentNames","ClipperSegments","Settings","onDestroy$","Subject","ngOnInit","queryParams","pipe","takeUntil","_b","_a","getCurrentNavigation","extras","state","snapshot","paramMap","get","clipId","getClip","urlSegment","toUpperCase","Poster","Split","Trim","ENABLE_FEATURE_TRIM_CLIPS","onSelectSegment","event","createUrlTree","toLowerCase","toString","replaceState","ngOnDestroy","selectors","standalone","features","decls","vars","consts","template","rf","ctx","$event","_r1","ClipEditPage_ng_container_6_Template","ClipEditPage_ng_template_7_Template","ClipEditPage_ng_template_9_Template","tmp_2_0","hasClipId_r4","noClipId_r3","IonHeader","TopNavbarComponent","ClipperSegmentBarComponent","IonContent","NgIf","RouterLink","ClipperComponent","AsyncPipe","TranslatePipe"],"ignoreList":[],"sourceRoot":"webpack:///","sources":["./src/app/clips/shared/services/clips.service.ts","./src/app/pages/clip-edit/clip-edit.page.html","./src/app/pages/clip-edit/clip-edit.page.ts"],"sourcesContent":["/** @format */\n\nimport { Injectable } from '@angular/core';\nimport { Store } from '@ngrx/store';\nimport { State } from '@store/reducers';\nimport * as clipActions from '@store/actions/clips.actions';\nimport * as mystackActions from '@store/actions/mystack.actions';\nimport { ClipsApiService } from '@app/core/api/clips-api.service';\nimport { AnalyticsService } from '@app/core/services/analytics/analytics.service';\nimport { SentryService } from '@app/core/services/sentry.service';\nimport { YoutubeService } from '@app/core/services/youtube.service';\nimport { Utils } from '@app/shared/utils';\nimport { ClipVideoFile, Clip } from '@app/shared/models/clip.model';\nimport { convertVideoFileToClip, isHlsComplete } from '@app/shared/models/clip.model';\nimport { HLS_META_TRANSCODE, DEFAULT_POSTER } from '@app/shared/models/clip.model';\nimport { environment, filestackConfig } from 'src/environments/environment';\n\nconst DEBUG_LOGS = false;\nconst PAGE = '[ClipsService]';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class ClipsService {\n constructor(\n private store: Store<State>,\n private sentryService: SentryService,\n private clipsApi: ClipsApiService,\n private youtubeService: YoutubeService,\n private analyticsService: AnalyticsService\n ) {}\n\n getPoster(clip: Clip, res: string = 'default') {\n let poster = DEFAULT_POSTER;\n if (clip) {\n if (clip.poster) {\n poster = clip.poster;\n } else if (clip.youtube_id) {\n poster = this.youtubeService.getThumbnail(clip.youtube_id, res).url;\n }\n }\n return poster;\n }\n\n /**\n * Create a new Clip from the FileStack Upload\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async createClipFromFilestack(filestackResult: any, projectId: string, userId: string) {\n DEBUG_LOGS && console.log(`createClipFromFilestack filestackResult =`, filestackResult);\n const {\n uploadTags: {\n lastModifiedDate = null, // = (new Date()).toISOString(),\n // filesize = filestackResult.size, // don't use the stringified from uploadTags\n duration = 0,\n } = {},\n } = filestackResult;\n // MVP-1169 file selected metadata\n !environment.production &&\n console.log(`get size & updatedDate from filestackResult:`, { lastModifiedDate, duration, filestackResult });\n\n if (!filestackResult || !projectId) {\n console.warn(`${PAGE} createClipFromFilestack missing required params`, { filestackResult, projectId });\n throw new Error('Missing required parameters - unable to create clip.');\n }\n\n const newVideo: ClipVideoFile = {\n filename: filestackResult.filename,\n type: filestackResult.mimetype,\n size: filestackResult.size,\n lastModifiedDate: new Date().toISOString(),\n loading: true,\n };\n\n // get additional metadata if it exists.\n if (lastModifiedDate) {\n newVideo.lastModifiedDate = lastModifiedDate;\n } else if (filestackResult.originalFile) {\n if (\n filestackResult.originalFile.lastModifiedDate &&\n typeof filestackResult.originalFile.lastModifiedDate.toISOString === 'function'\n ) {\n newVideo.lastModifiedDate = filestackResult.originalFile.lastModifiedDate.toISOString();\n } else if (filestackResult.originalFile.lastModified) {\n newVideo.lastModifiedDate = new Date(filestackResult.originalFile.lastModified).toISOString();\n }\n }\n\n // create our clip...\n const clip = convertVideoFileToClip(newVideo);\n clip.source = clip.source || {};\n // it is important that this startsWith(filestackConfig.storeTo.path) in the clipApi logic\n clip.source.src = filestackConfig.storeTo.clip.path + newVideo.filename;\n clip.source.orig = filestackResult.key;\n clip.source.filename = newVideo.filename;\n clip.source.type = newVideo.type;\n clip.source.loading = newVideo.loading;\n clip.source.lastModifiedDate = newVideo.lastModifiedDate;\n\n if (filestackResult.originalFile && filestackResult.originalFile.name) {\n clip.source.file = filestackResult.originalFile;\n }\n // this must match for api to do its magic...\n clip.id = Utils.removeFileExt(newVideo.filename);\n clip.projectId = projectId;\n\n if (typeof clip.filmingDate !== 'string') {\n clip.filmingDate = Utils.getDateTimeString(clip.filmingDate);\n }\n\n DEBUG_LOGS && console.log(`${PAGE} createClipFromFilestack -> createClip:`, clip);\n\n // kick off the createClip flow, subscribe to updates, add to mystack\n // as soon as we return, the Capture Page will nav to the Stack Editor\n return this.createClip(clip).then((newClip) => {\n DEBUG_LOGS &&\n console.log(`${PAGE} createClipFromFilestack -> now watch updates & addToStack (createClip result:`, newClip);\n // watch changes and push to store\n this.subClipsUpdated(newClip.projectId, newClip.id, userId);\n // add the clip to the Editor\n this.store.dispatch(mystackActions.addClip({ clip: newClip }));\n\n try {\n // analytics\n const size =\n newVideo && newVideo.size && typeof newVideo.size.toFixed === 'function' ? newVideo.size.toFixed(0) : false;\n const seconds =\n clip.source && clip.source.durationInSeconds && typeof clip.source.durationInSeconds.toFixed === 'function'\n ? clip.source.durationInSeconds.toFixed(3)\n : false;\n this.analyticsService.clipUpload({\n projectId: newClip.projectId,\n id: newClip.id,\n userId: newClip.userId,\n ...(seconds && { seconds }),\n ...(size && { size }),\n });\n } catch (error) {\n console.warn(`Analytics error:`, error);\n }\n });\n }\n\n /**\n * upload clip and add to store\n * @param clip\n * 1. Create the clip in DB\n * 2.\n */\n createClip(clip: Clip): Promise<Clip> {\n DEBUG_LOGS && console.log(`${PAGE} createClip`, clip);\n\n // add the new HLS transcoding instruction\n clip.hlsMeta = {\n ...(clip.hlsMeta || {}),\n ...HLS_META_TRANSCODE,\n };\n\n return this.clipsApi\n .createClip(clip)\n .then((res) => {\n DEBUG_LOGS && console.log(`${PAGE} createClip res:`, res);\n // add the clip to the Store\n const newClip = new Clip(res);\n this.store.dispatch(clipActions.addClip({ clip: newClip }));\n return newClip;\n })\n .catch((err) => {\n if (\n err &&\n Array.isArray(err.errors) &&\n err.errors.length > 0 &&\n err.errors[0] &&\n err.errors[0].errorType &&\n err.errors[0].errorType === 'DynamoDB:ConditionalCheckFailedException'\n ) {\n console.info(`${PAGE} createClip already Exists:`, err);\n return new Clip(clip);\n }\n console.error(`${PAGE} createClip err:`, err);\n if (err && Array.isArray(err.errors) && err.errors.length > 0 && err.errors[0].message) {\n throw err.errors[0].message;\n }\n throw err;\n });\n }\n\n /**\n * clipUpdate GraphQL Subscription\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n subClipsUpdated(projectId: string, id = '', userId = '') {\n const params: { projectId: string; id?: string } = {\n projectId,\n };\n if (id) {\n params.id = id;\n }\n // not currently using userId on this GQL subscription in the API\n // if (userId) { params.userId = userId; }\n\n // keeping the sub variable local so there can be multiple open subs\n const sub = this.clipsApi.subscribeClipsUpdated(params).subscribe({\n next: (res) => {\n // todo: convert to handleSubClips(res) when this not needed by editor\n DEBUG_LOGS && console.log(`${PAGE} clipsUpdatedSubscription next:`, res);\n if (!res || !res.id || !res.projectId) {\n throw new Error('Missing clip id in subscription?');\n }\n const updates: Partial<Clip> = {};\n let hlsMeta;\n if (res.hlsMeta) {\n try {\n hlsMeta = typeof res.hlsMeta === 'string' ? JSON.parse(res.hlsMeta) : res.hlsMeta;\n updates.hlsMeta = hlsMeta;\n } catch (error) {\n console.log(`Caught error during JSON.parse(res.hlsMeta):`, {\n hlsMeta: res.hlsMeta,\n error: error.message || error,\n });\n }\n }\n if (res.hlsSrc) {\n updates.hlsSrc = res.hlsSrc;\n }\n if (Array.isArray(res.sources) && res.sources.length > 0) {\n updates.sources = res.sources;\n }\n if (res.poster) {\n updates.poster = res.poster;\n }\n if (res.duration) {\n updates.duration = res.duration;\n }\n\n if (Object.keys(updates).length > 0) {\n // take the updates and flow to store...\n this.store.dispatch(\n clipActions.updateClipTranscoding({\n projectId: res.projectId,\n id: res.id,\n updates,\n })\n );\n DEBUG_LOGS &&\n console.log(`${PAGE} clipsUpdatedSubscription updateClipTranscoding:`, {\n projectId: res.projectId,\n id: res.id,\n updates,\n });\n }\n\n if (isHlsComplete(new Clip(updates))) {\n // when complete, this.unsubClipsUpdated()\n sub.unsubscribe();\n }\n },\n error: (err) => {\n console.warn(`${PAGE} clipsUpdatedSubscription ERROR:`, err);\n this.sentryService.captureError(err);\n\n if (projectId && id) {\n this.store.dispatch(\n clipActions.updateClipTranscoding({\n projectId,\n id,\n updates: {\n hlsMeta: {\n errorMessage: err.message || err.errorMessage || err,\n },\n },\n })\n );\n }\n sub.unsubscribe();\n },\n complete: () => {\n DEBUG_LOGS && console.log(`${PAGE} clipsUpdatedSubscription => Complete`);\n },\n });\n }\n //end GraphQL Subscription\n\n /**\n * Take the poster string and return poster time from the transcoded name\n * example poster path:\n * https://videos.filmstacker.com/public/filmstacker-dev/jd_-_jd-note_202203021348/jd_-_jd-note_202203021348_thumb.0000000.jpg\n */\n getPosterTimeFromPoster(clip: Clip) {\n if (clip && clip.poster) {\n const sNum = clip.poster.substring(clip.poster.lastIndexOf('_thumb.'), clip.poster.lastIndexOf('.'));\n // there shouldn't be any dashes anymore\n // const sTime = sNum.replace(/-/g, '').replace(/0/gi, '');\n try {\n const parsed = parseInt(sNum, 10);\n return parsed > 0 ? parsed : 0;\n } catch (error) {\n return 0;\n }\n }\n return 0;\n }\n\n /**\n * Update the poster image to match the new posterTime\n *\n * @todo verify it exists in s3\n * potential ref: https://stackoverflow.com/questions/9815762/detect-when-an-image-fails-to-load-in-javascript\n *\n * example poster path:\n * https://videos.filmstacker.com/public/filmstacker-dev/jd_-_jd-note_202203021348/jd_-_jd-note_202203021348_thumb.0000000.jpg\n */\n updatePosterToPosterTime(clip: Clip, posterTime: number) {\n if (clip && clip.poster) {\n let newPoster = clip.poster;\n const sNum = ('0000000' + posterTime).slice(-7);\n const startIndex = newPoster.lastIndexOf('_thumb.') + '_thumb.'.length;\n const endIndex = newPoster.lastIndexOf('.');\n newPoster = newPoster.slice(0, startIndex) + sNum + newPoster.slice(endIndex);\n console.log(`${PAGE} change clip.poster (TODO: verify it exists in s3) posterTime: ${posterTime} to:`, {\n sNum,\n newPoster,\n });\n return newPoster;\n } else {\n console.warn(`${PAGE} missing clip.poster - unable to change`, clip);\n return null;\n }\n }\n\n /**\n * @deprecated unused - use subClipsUpdated\n * Do what we need with the clip we just received\n */\n private handleSubClips(res) {\n DEBUG_LOGS && console.log(`${PAGE} handleSubClips next:`, res);\n if (!res || !res.id || !res.projectId) {\n throw new Error('Missing clip id in subscription?');\n }\n // if any values exist not null, add them as changes?\n const updates: Partial<Clip> = {};\n let hlsMeta;\n if (res.hlsMeta) {\n try {\n hlsMeta = typeof res.hlsMeta === 'string' ? JSON.parse(res.hlsMeta) : res.hlsMeta;\n updates.hlsMeta = hlsMeta;\n } catch (error) {\n console.log(`Caught error during JSON.parse(res.hlsMeta):`, {\n hlsMeta: res.hlsMeta,\n error: error.message || error,\n });\n }\n }\n if (res.hlsSrc) {\n updates.hlsSrc = res.hlsSrc;\n }\n if (Array.isArray(res.sources) && res.sources.length > 0) {\n updates.sources = res.sources;\n }\n if (res.poster) {\n updates.poster = res.poster;\n }\n if (res.duration) {\n updates.duration = res.duration;\n }\n\n // take the updates and flow to store...\n // this.store.dispatch(clipActions.updateClipTranscoding({\n // projectId: res.projectId,\n // id: res.id,\n // updates,\n // }));\n }\n}\n","<ion-header>\n <app-top-navbar \n [title]=\"((clip$ | async)?.title) ? title + ': ' + (clip$ | async).title : title\" \n [backButtonReturnUrl]=\"returnUrl\">\n <app-clipper-segment-bar\n [selectedSegment]=\"selectedSegment\"\n (onSelectSegment)=\"onSelectSegment($event)\"\n ></app-clipper-segment-bar>\n \n </app-top-navbar>\n</ion-header>\n\n<ion-content>\n\n <ng-container *ngIf=\"clipId && projectId; then hasClipId else noClipId\"></ng-container>\n \n <ng-template #noClipId>\n <div class=\"ion-text-center ion-padding\">\n <div class=\"ion-margin-vertical ion-padding-vertical\">{{ 'ERRORS.NO_CLIP_ID' | translate }}</div>\n\n <ion-button [routerLink]=\"returnUrl ? returnUrl : defaultReturnUrl\">\n {{ 'COMMON.BACK' | translate }} <span *ngIf=\"!returnUrl\"> {{ 'COMMON.TO_STUDIO' | translate }}</span>\n </ion-button>\n </div>\n </ng-template>\n \n <ng-template #hasClipId>\n \n <app-clipper\n [selectedSegment]=\"selectedSegment\"\n [clip]=\"clip$ | async\"\n [returnUrl]=\"returnUrl\"\n ></app-clipper>\n \n </ng-template>\n\n</ion-content>\n\n\n","/** @format */\n\nimport { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Location, NgIf, AsyncPipe } from '@angular/common';\nimport { Router, ActivatedRoute, RouterLink } from '@angular/router';\nimport { IonHeader, IonContent } from '@ionic/angular/standalone';\nimport { TranslatePipe } from '@ngx-translate/core';\nimport { Observable, Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { ENABLE_FEATURE_TRIM_CLIPS } from '@app/app.config';\nimport { ClipsCoreService } from '@app/core/services/clips.service';\nimport { Clip } from '@app/shared/models/clip.model';\nimport { ClipperComponent, ClipperSegments } from '@app/modules/clipper/clipper.component';\nimport { ClipperSegmentBarComponent } from '@app/modules/clipper/clipper-segment-bar/clipper-segment-bar.component';\nimport { TopNavbarComponent } from '@app/shared/components/top-navbar/top-navbar.component';\n\nconst PAGE = '[ClipEdit]';\n\n@Component({\n selector: 'app-clip-edit',\n templateUrl: './clip-edit.page.html',\n styleUrls: ['./clip-edit.page.scss'],\n standalone: true,\n imports: [\n IonHeader,\n TopNavbarComponent,\n ClipperSegmentBarComponent,\n IonContent,\n NgIf,\n RouterLink,\n ClipperComponent,\n AsyncPipe,\n TranslatePipe,\n ],\n})\nexport class ClipEditPage implements OnInit, OnDestroy {\n clipId: string;\n projectId: string;\n title = 'Edit Clip';\n clip$: Observable<Clip>;\n returnUrl: string;\n defaultReturnUrl = '/stack/studio';\n\n public segmentNames = ClipperSegments;\n public selectedSegment: ClipperSegments = ClipperSegments.Settings;\n private onDestroy$ = new Subject<void>();\n\n constructor(\n private clipsService: ClipsCoreService,\n private route: ActivatedRoute,\n private router: Router,\n private location: Location\n ) {}\n\n ngOnInit() {\n this.route.queryParams.pipe(takeUntil(this.onDestroy$)).subscribe((params) => {\n if (params?.returnUrl) {\n console.log(`${PAGE} queryParams returnUrl:`, params.returnUrl);\n this.returnUrl = params.returnUrl; //JSON.parse(params.returnUrl);\n } else if (this.router.getCurrentNavigation()?.extras?.state) {\n this.returnUrl = this.router.getCurrentNavigation().extras.state.returnUrl || this.defaultReturnUrl;\n console.log(`${PAGE} navState:`, { returnUrl: this.returnUrl });\n }\n });\n this.projectId = this.route.snapshot.paramMap.get('projectId');\n this.clipId = this.route.snapshot.paramMap.get('id');\n\n if (this.projectId && this.clipId) {\n // get the clip\n this.clip$ = this.clipsService.getClip(this.projectId, this.clipId);\n\n // check for a segment in url\n const urlSegment = this.route.snapshot.paramMap.get('segment');\n if (urlSegment) {\n switch (urlSegment.toUpperCase()) {\n case ClipperSegments.Poster:\n this.selectedSegment = ClipperSegments.Poster;\n break;\n case ClipperSegments.Split:\n this.selectedSegment = ClipperSegments.Split;\n break;\n case ClipperSegments.Trim:\n if (ENABLE_FEATURE_TRIM_CLIPS) {\n this.selectedSegment = ClipperSegments.Trim;\n break;\n }\n // eslint-disable-next-line no-fallthrough\n case ClipperSegments.Settings:\n default:\n this.selectedSegment = ClipperSegments.Settings;\n }\n }\n }\n }\n\n onSelectSegment(event) {\n console.log(`onSelectSegment`, { returnUrl: this.router.url });\n if (event) {\n this.selectedSegment = event;\n\n if (this.clipId && this.projectId) {\n // update Url so it's linkable\n const url = this.router\n .createUrlTree(['/stack/clip/', this.projectId, this.clipId, (event as string).toLowerCase()])\n .toString();\n this.location.replaceState(url, '', {\n state: {\n returnUrl: this.router.url,\n },\n });\n }\n } else {\n console.warn(`${PAGE} segmentChanged NO value?:`, event);\n }\n }\n\n ngOnDestroy() {\n this.onDestroy$.next();\n this.onDestroy$.complete();\n }\n}\n"],"x_google_ignoreList":[]}