Skip to content

Commit

Permalink
Add transcription widget
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Aug 14, 2024
1 parent 3a2e457 commit be4bf80
Show file tree
Hide file tree
Showing 24 changed files with 566 additions and 90 deletions.
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,11 +754,13 @@

* [Olivier Massain](https://dribbble.com/omassain)
* [Marie-Cécile Godwin Paccard](https://mcgodwin.com/)
* [La Coopérative des Internets](https://www.lacooperativedesinternets.fr/)


# Icons

* [Feather Icons](https://feathericons.com) (MIT)
* [Lucide Icons](https://lucide.dev/) (ISC)
* `playlist add`, `history`, `subscriptions`, `miscellaneous-services.svg`, `tip` by Material UI (Apache 2.0)
* `support` by Chocobozzz (CC-BY)
* `language` by Aaron Jin (CC-BY)
Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@peertube/p2p-media-loader-core": "^1.0.20",
"@peertube/p2p-media-loader-hlsjs": "^1.0.20",
"@peertube/xliffmerge": "^2.0.3",
"@plussub/srt-vtt-parser": "^2.0.5",
"@popperjs/core": "^2.11.5",
"@types/chart.js": "^2.9.37",
"@types/core-js": "^2.5.2",
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/+search/search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<button
class="results-filter-button button-unstyle ms-auto" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed"
[attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="collapseBasic"
[attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="search-results-filter"
>
<span class="icon icon-filter"></span>
<ng-container i18n>
Expand All @@ -22,7 +22,7 @@
</button>
</div>

<div class="results-filter" [ngbCollapse]="isSearchFilterCollapsed" [animation]="true">
<div id="search-results-filter" class="results-filter" [ngbCollapse]="isSearchFilterCollapsed" [animation]="true">
<my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>

<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</div>
</div>

<ng-container *ngIf="!isUserLoggedIn && !video.isLive">
@if (!isUserLoggedIn && !video.isLive) {
<button
*ngIf="isVideoDownloadable()" class="action-button action-button-download"
(click)="showDownloadModal()" (keydown.enter)="showDownloadModal()"
Expand All @@ -45,15 +45,15 @@
</button>

<my-video-download #videoDownloadModal [videoPassword]="videoPassword"></my-video-download>
</ng-container>
}

<ng-container *ngIf="isUserLoggedIn">
<my-video-actions-dropdown
placement="bottom auto" buttonDirection="horizontal" buttonStyled="true" [video]="video" [videoCaptions]="videoCaptions"
actionAvailabilityHint="true"
[displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()"
></my-video-actions-dropdown>
</ng-container>
<my-video-actions-dropdown
[video]="video" [videoCaptions]="videoCaptions" [transcriptionWidgetOpened]="transcriptionWidgetOpened"
[displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()"
(showTranscriptionWidget)="showTranscriptionWidget.emit()" (hideTranscriptionWidget)="hideTranscriptionWidget.emit()"
placement="bottom auto" buttonDirection="horizontal" buttonStyled="true"
actionAvailabilityHint="true"
></my-video-actions-dropdown>
</div>

<div class="likes-dislikes-bar-outer-container">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'
import { NgClass, NgIf, NgStyle } from '@angular/common'
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
import { RedirectService, ScreenService } from '@app/core'
import { VideoDetails } from '@app/shared/shared-main/video/video-details.model'
import { VideoShareComponent } from '@app/shared/shared-share-modal/video-share.component'
import { SupportModalComponent } from '@app/shared/shared-support-modal/support-modal.component'
import { VideoDownloadComponent } from '@app/shared/shared-video-miniature/download/video-download.component'
import { VideoPlaylist } from '@app/shared/shared-video-playlist/video-playlist.model'
import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { UserVideoRateType, VideoCaption, VideoPrivacy } from '@peertube/peertube-models'
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
import {
VideoActionsDisplayType,
VideoActionsDropdownComponent
} from '../../../../shared/shared-video-miniature/video-actions-dropdown.component'
import { VideoAddToPlaylistComponent } from '../../../../shared/shared-video-playlist/video-add-to-playlist.component'
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
import { NgbTooltip, NgbDropdown, NgbDropdownToggle, NgbDropdownMenu } from '@ng-bootstrap/ng-bootstrap'
import { NgIf, NgClass, NgStyle } from '@angular/common'
import { VideoRateComponent } from './video-rate.component'
import { VideoDetails } from '@app/shared/shared-main/video/video-details.model'
import { VideoShareComponent } from '@app/shared/shared-share-modal/video-share.component'
import { SupportModalComponent } from '@app/shared/shared-support-modal/support-modal.component'
import { VideoDownloadComponent } from '@app/shared/shared-video-miniature/download/video-download.component'
import { VideoPlaylist } from '@app/shared/shared-video-playlist/video-playlist.model'

@Component({
selector: 'my-action-buttons',
Expand All @@ -38,7 +38,7 @@ import { VideoPlaylist } from '@app/shared/shared-video-playlist/video-playlist.
VideoShareComponent
]
})
export class ActionButtonsComponent implements OnInit, OnChanges {
export class ActionButtonsComponent implements OnChanges {
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
@ViewChild('supportModal') supportModal: SupportModalComponent
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
Expand All @@ -51,9 +51,14 @@ export class ActionButtonsComponent implements OnInit, OnChanges {
@Input() isUserLoggedIn: boolean
@Input() isUserOwner: boolean

@Input() transcriptionWidgetOpened: boolean

@Input() currentTime: number
@Input() currentPlaylistPosition: number

@Output() showTranscriptionWidget = new EventEmitter()
@Output() hideTranscriptionWidget = new EventEmitter()

likesBarTooltipText = ''

tooltipSupport = ''
Expand All @@ -70,7 +75,10 @@ export class ActionButtonsComponent implements OnInit, OnChanges {
duplicate: true,
mute: true,
liveInfo: true,
stats: true
stats: true,
generateTranscription: true,
transcriptionWidget: true,
transcoding: true
}

userRating: UserVideoRateType
Expand All @@ -80,16 +88,20 @@ export class ActionButtonsComponent implements OnInit, OnChanges {
private redirectService: RedirectService
) { }

ngOnInit () {
// Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover
if (this.isUserLoggedIn || !this.screenService.isInMobileView()) {
this.tooltipSupport = $localize`Open the modal to support the video uploader`
this.tooltipSaveToPlaylist = $localize`Save to playlist`
}
}

ngOnChanges () {
this.setVideoLikesBarTooltipText()

if (this.isUserLoggedIn) {
this.videoActionsOptions.download = true

// Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover
if (!this.screenService.isInMobileView()) {
this.tooltipSupport = $localize`Open the modal to support the video uploader`
this.tooltipSaveToPlaylist = $localize`Save to playlist`
}
} else {
this.videoActionsOptions.download = false
}
}

showDownloadModal () {
Expand Down
1 change: 0 additions & 1 deletion client/src/app/+videos/+video-watch/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ export * from './action-buttons'
export * from './comment'
export * from './information'
export * from './metadata'
export * from './playlist'
export * from './recommendations'
export * from './timestamp-route-transformer.directive'
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@use '_variables' as *;
@use '_mixins' as *;

.widget-root {
position: relative;
min-width: 200px;
width: 25vw;
max-width: 470px;
height: 66vh;
background-color: pvar(--mainBackgroundColor);
overflow-y: auto;
border-bottom: 1px solid $separator-border-color;

.widget-header {
background-color: pvar(--submenuBackgroundColor);
padding: 1rem 2rem;
}

.widget-content-padded {
padding: 0 2rem;
}

.widget-title {
font-size: 18px;
font-weight: $font-semibold;

.pt-badge {
@include margin-left(5px);
}
}

.widget-content {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<div class="widget-root">

<div class="widget-header d-flex justify-content-between">
<div class="widget-title" i18n>Transcription</div>

<div>
<button
class="border-0 p-0 me-3 settings-button" title="Settings" i18n-title
(click)="isSettingsPanelCollapsed = !isSettingsPanelCollapsed" [attr.aria-expanded]="!isSettingsPanelCollapsed" aria-controls="video-transcription-settings-panel"
>
<my-global-icon iconName="filter"></my-global-icon>
</button>

<button class="border-0 p-0" title="Close transcription widget" i18n-title (click)="closeTranscription.emit()">
<my-global-icon iconName="cross"></my-global-icon>
</button>
</div>
</div>

<div class="widget-content">
<div class="widget-content-padded">

<div
id="video-transcription-settings-panel" class="settings-panel"
#settingsPanel #settingsPanelCollapse="ngbCollapse" [ngbCollapse]="isSettingsPanelCollapsed"
(shown)="settingsPanelShown = true" (hidden)="settingsPanelShown = false"
>
<div class="card">
<div class="card-body">
<label i18n for="transcription-language">Language</label>

<my-select-options
labelForId="transcription-language" [items]="languagesOptions"
[(ngModel)]="currentLanguage" (ngModelChange)="updateCurrentCaption()" clearable="false"
></my-select-options>
</div>
</div>
</div>

<input
type="text" class="mb-3" name="search-transcript" i18n-placeholder placeholder="Search transcript"
(input)="onSearchChange($event)"
>

@if (search && segments.length === 0) {
<div i18n>No results for your search</div>
}
</div>

<div
role="button" tabindex="0" class="segment widget-content-padded pt-1 pb-1"
i18n-title title="Jump to this segment"
*ngFor="let segment of segments"
(keyup.enter)="onSegmentClick($event, segment)" (click)="onSegmentClick($event, segment)"
[ngClass]="getSegmentClasses(segment)"
>
<strong class="segment-start me-2">{{ segment.startFormatted }}</strong>
<span class="segment-text fs-7">{{ segment.text }}</span>
</div>
</div>

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@use '_variables' as *;
@use '_mixins' as *;

.segment {
&.active,
&:hover {
background: pvar(--mainBackgroundHoverColor);
}
}

input[type=text] {
@include peertube-input-text(100%);
}

.settings-button my-global-icon {
width: 18px;
height: 18px;
}

.settings-panel {
position: absolute;
width: 100%;
padding: 0 1.5rem;
left: 0;
right: 0;
}
Loading

0 comments on commit be4bf80

Please sign in to comment.