Add live info in watch page

pull/3284/head
Chocobozzz 2020-11-05 10:56:23 +01:00 committed by Chocobozzz
parent ba881f0e3f
commit f8c00564e7
22 changed files with 102 additions and 25 deletions

View File

@ -6,10 +6,10 @@ import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenServi
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
import { immutableAssign } from '@app/helpers' import { immutableAssign } from '@app/helpers'
import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
import { VideoSortField } from '@shared/models' import { VideoSortField } from '@shared/models'
import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component'
import { LiveStreamInformationComponent } from './modals/live-stream-information.component'
@Component({ @Component({
selector: 'my-account-videos', selector: 'my-account-videos',

View File

@ -6,6 +6,7 @@ import { NgModule } from '@angular/core'
import { SharedAbuseListModule } from '@app/shared/shared-abuse-list' import { SharedAbuseListModule } from '@app/shared/shared-abuse-list'
import { SharedFormModule } from '@app/shared/shared-forms' import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons' import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
import { SharedMainModule } from '@app/shared/shared-main' import { SharedMainModule } from '@app/shared/shared-main'
import { SharedModerationModule } from '@app/shared/shared-moderation' import { SharedModerationModule } from '@app/shared/shared-moderation'
import { SharedShareModal } from '@app/shared/shared-share-modal' import { SharedShareModal } from '@app/shared/shared-share-modal'
@ -33,9 +34,8 @@ import { MyAccountVideoPlaylistCreateComponent } from './my-account-video-playli
import { MyAccountVideoPlaylistElementsComponent } from './my-account-video-playlists/my-account-video-playlist-elements.component' import { MyAccountVideoPlaylistElementsComponent } from './my-account-video-playlists/my-account-video-playlist-elements.component'
import { MyAccountVideoPlaylistUpdateComponent } from './my-account-video-playlists/my-account-video-playlist-update.component' import { MyAccountVideoPlaylistUpdateComponent } from './my-account-video-playlists/my-account-video-playlist-update.component'
import { MyAccountVideoPlaylistsComponent } from './my-account-video-playlists/my-account-video-playlists.component' import { MyAccountVideoPlaylistsComponent } from './my-account-video-playlists/my-account-video-playlists.component'
import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
import { VideoChangeOwnershipComponent } from './my-account-videos/modals/video-change-ownership.component' import { VideoChangeOwnershipComponent } from './my-account-videos/modals/video-change-ownership.component'
import { LiveStreamInformationComponent } from './my-account-videos/modals/live-stream-information.component' import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
import { MyAccountComponent } from './my-account.component' import { MyAccountComponent } from './my-account.component'
@NgModule({ @NgModule({
@ -56,7 +56,8 @@ import { MyAccountComponent } from './my-account.component'
SharedUserInterfaceSettingsModule, SharedUserInterfaceSettingsModule,
SharedGlobalIconModule, SharedGlobalIconModule,
SharedAbuseListModule, SharedAbuseListModule,
SharedShareModal SharedShareModal,
SharedVideoLiveModule
], ],
declarations: [ declarations: [
@ -69,7 +70,6 @@ import { MyAccountComponent } from './my-account.component'
MyAccountVideosComponent, MyAccountVideosComponent,
VideoChangeOwnershipComponent, VideoChangeOwnershipComponent,
LiveStreamInformationComponent,
MyAccountOwnershipComponent, MyAccountOwnershipComponent,
MyAccountAcceptOwnershipComponent, MyAccountAcceptOwnershipComponent,

View File

@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'
import { SharedFormModule } from '@app/shared/shared-forms' import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons' import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main' import { SharedMainModule } from '@app/shared/shared-main'
import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
import { VideoEditComponent } from './video-edit.component' import { VideoEditComponent } from './video-edit.component'
@ -13,7 +14,8 @@ import { VideoEditComponent } from './video-edit.component'
SharedMainModule, SharedMainModule,
SharedFormModule, SharedFormModule,
SharedGlobalIconModule SharedGlobalIconModule,
SharedVideoLiveModule
], ],
declarations: [ declarations: [

View File

@ -35,7 +35,8 @@
<!-- Hidden because we want to load the component --> <!-- Hidden because we want to load the component -->
<form [hidden]="!isInUpdateForm" novalidate [formGroup]="form"> <form [hidden]="!isInUpdateForm" novalidate [formGroup]="form">
<my-video-edit <my-video-edit
[form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions" [schedulePublicationPossible]="false" [form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions"
[schedulePublicationPossible]="false" [waitTranscodingEnabled]="false"
[validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" [liveVideo]="liveVideo" [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" [liveVideo]="liveVideo"
type="go-live" type="go-live"
></my-video-edit> ></my-video-edit>

View File

@ -5,7 +5,8 @@ import { Router } from '@angular/router'
import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core'
import { scrollToTop } from '@app/helpers' import { scrollToTop } from '@app/helpers'
import { FormValidatorService } from '@app/shared/shared-forms' import { FormValidatorService } from '@app/shared/shared-forms'
import { LiveVideoService, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' import { VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LiveVideoService } from '@app/shared/shared-video-live'
import { LoadingBarService } from '@ngx-loading-bar/core' import { LoadingBarService } from '@ngx-loading-bar/core'
import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, ServerErrorCode, VideoPrivacy } from '@shared/models' import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, ServerErrorCode, VideoPrivacy } from '@shared/models'
import { VideoSend } from './video-send' import { VideoSend } from './video-send'
@ -64,8 +65,6 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon
channelId: this.firstStepChannelId channelId: this.firstStepChannelId
} }
this.firstStepDone.emit(name)
// Go live in private mode, but correctly fill the update form with the first user choice // Go live in private mode, but correctly fill the update form with the first user choice
const toPatch = Object.assign({}, video, { privacy: this.firstStepPrivacyId }) const toPatch = Object.assign({}, video, { privacy: this.firstStepPrivacyId })
this.form.patchValue(toPatch) this.form.patchValue(toPatch)
@ -76,6 +75,8 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon
this.videoUUID = res.video.uuid this.videoUUID = res.video.uuid
this.isInUpdateForm = true this.isInUpdateForm = true
this.firstStepDone.emit(name)
this.fetchVideoLive() this.fetchVideoLive()
}, },

View File

@ -1,13 +1,14 @@
import { of } from 'rxjs'
import { map, switchMap } from 'rxjs/operators' import { map, switchMap } from 'rxjs/operators'
import { Component, HostListener, OnInit } from '@angular/core' import { Component, HostListener, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
import { LiveVideoService, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
import { LiveVideoService } from '@app/shared/shared-video-live'
import { LoadingBarService } from '@ngx-loading-bar/core' import { LoadingBarService } from '@ngx-loading-bar/core'
import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models' import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
import { hydrateFormFromVideo } from './shared/video-edit-utils' import { hydrateFormFromVideo } from './shared/video-edit-utils'
import { of } from 'rxjs'
@Component({ @Component({
selector: 'my-videos-update', selector: 'my-videos-update',

View File

@ -2,7 +2,8 @@ import { forkJoin, of } from 'rxjs'
import { map, switchMap } from 'rxjs/operators' import { map, switchMap } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ActivatedRouteSnapshot, Resolve } from '@angular/router' import { ActivatedRouteSnapshot, Resolve } from '@angular/router'
import { VideoCaptionService, VideoChannelService, VideoDetails, LiveVideoService, VideoService } from '@app/shared/shared-main' import { VideoCaptionService, VideoChannelService, VideoDetails, VideoService } from '@app/shared/shared-main'
import { LiveVideoService } from '@app/shared/shared-video-live'
@Injectable() @Injectable()
export class VideoUpdateResolver implements Resolve<any> { export class VideoUpdateResolver implements Resolve<any> {

View File

@ -136,7 +136,7 @@
<ng-container *ngIf="isUserLoggedIn()"> <ng-container *ngIf="isUserLoggedIn()">
<my-video-actions-dropdown <my-video-actions-dropdown
placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions" placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions"
(videoRemoved)="onVideoRemoved()" (modalOpened)="onModalOpened()" [displayOptions]="videoActionsOptions" (videoRemoved)="onVideoRemoved()" (modalOpened)="onModalOpened()"
></my-video-actions-dropdown> ></my-video-actions-dropdown>
</ng-container> </ng-container>
</div> </div>

View File

@ -27,14 +27,14 @@ $video-info-margin-left: 44px;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
$height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
#videojs-wrapper { #videojs-wrapper {
width: 100%; width: 100%;
height: auto; height: $height;
} }
::ng-deep .video-js { ::ng-deep .video-js {
$height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
height: $height; height: $height;
width: 100%; width: 100%;
max-width: initial; max-width: initial;

View File

@ -21,7 +21,7 @@ import { isXPercentInViewport, scrollToTop } from '@app/helpers'
import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
import { VideoShareComponent } from '@app/shared/shared-share-modal' import { VideoShareComponent } from '@app/shared/shared-share-modal'
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
import { VideoDownloadComponent } from '@app/shared/shared-video-miniature' import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature'
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { MetaService } from '@ngx-meta/core' import { MetaService } from '@ngx-meta/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
@ -82,6 +82,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
tooltipSupport = '' tooltipSupport = ''
tooltipSaveToPlaylist = '' tooltipSaveToPlaylist = ''
videoActionsOptions: VideoActionsDisplayType = {
playlist: false,
download: true,
update: true,
blacklist: true,
delete: true,
report: true,
duplicate: true,
mute: true,
liveInfo: true
}
private nextVideoUuid = '' private nextVideoUuid = ''
private nextVideoTitle = '' private nextVideoTitle = ''
private currentTime: number private currentTime: number

View File

@ -30,7 +30,7 @@ import { FeedComponent } from './feeds'
import { LoaderComponent, SmallLoaderComponent } from './loaders' import { LoaderComponent, SmallLoaderComponent } from './loaders'
import { HelpComponent, ListOverflowComponent, TopMenuDropdownComponent } from './misc' import { HelpComponent, ListOverflowComponent, TopMenuDropdownComponent } from './misc'
import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users'
import { LiveVideoService, RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video'
import { VideoCaptionService } from './video-caption' import { VideoCaptionService } from './video-caption'
import { VideoChannelService } from './video-channel' import { VideoChannelService } from './video-channel'
@ -152,7 +152,6 @@ import { VideoChannelService } from './video-channel'
RedundancyService, RedundancyService,
VideoImportService, VideoImportService,
VideoOwnershipService, VideoOwnershipService,
LiveVideoService,
VideoService, VideoService,
VideoCaptionService, VideoCaptionService,

View File

@ -1,4 +1,3 @@
export * from './live-video.service'
export * from './redundancy.service' export * from './redundancy.service'
export * from './video-details.model' export * from './video-details.model'
export * from './video-edit.model' export * from './video-edit.model'

View File

@ -193,6 +193,11 @@ export class Video implements VideoServerModel {
return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO)) return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO))
} }
isLiveInfoAvailableBy (user: AuthUser) {
return this.isLive &&
user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.GET_ANY_LIVE))
}
canBeDuplicatedBy (user: AuthUser) { canBeDuplicatedBy (user: AuthUser) {
return user && this.isLocal === false && user.hasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES) return user && this.isLocal === false && user.hasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES)
} }

View File

@ -0,0 +1,4 @@
export * from './live-video.service'
export * from './live-stream-information.component'
export * from './shared-video-live.module'

View File

@ -1,6 +1,7 @@
import { Component, ElementRef, ViewChild } from '@angular/core' import { Component, ElementRef, ViewChild } from '@angular/core'
import { LiveVideoService, Video } from '@app/shared/shared-main' import { Video } from '@app/shared/shared-main'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { LiveVideoService } from './live-video.service'
@Component({ @Component({
selector: 'my-live-stream-information', selector: 'my-live-stream-information',

View File

@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { RestExtractor } from '@app/core' import { RestExtractor } from '@app/core'
import { LiveVideo, LiveVideoCreate, LiveVideoUpdate } from '@shared/models' import { LiveVideo, LiveVideoCreate, LiveVideoUpdate } from '@shared/models'
import { environment } from '../../../../environments/environment' import { environment } from '../../../environments/environment'
@Injectable() @Injectable()
export class LiveVideoService { export class LiveVideoService {

View File

@ -0,0 +1,28 @@
import { NgModule } from '@angular/core'
import { SharedFormModule } from '../shared-forms'
import { SharedGlobalIconModule } from '../shared-icons'
import { SharedMainModule } from '../shared-main/shared-main.module'
import { LiveStreamInformationComponent } from './live-stream-information.component'
import { LiveVideoService } from './live-video.service'
@NgModule({
imports: [
SharedMainModule,
SharedFormModule,
SharedGlobalIconModule
],
declarations: [
LiveStreamInformationComponent
],
exports: [
LiveStreamInformationComponent
],
providers: [
LiveVideoService
]
})
export class SharedVideoLiveModule { }

View File

@ -5,6 +5,7 @@ import { SharedGlobalIconModule } from '../shared-icons'
import { SharedMainModule } from '../shared-main/shared-main.module' import { SharedMainModule } from '../shared-main/shared-main.module'
import { SharedModerationModule } from '../shared-moderation' import { SharedModerationModule } from '../shared-moderation'
import { SharedThumbnailModule } from '../shared-thumbnail' import { SharedThumbnailModule } from '../shared-thumbnail'
import { SharedVideoLiveModule } from '../shared-video-live'
import { SharedVideoPlaylistModule } from '../shared-video-playlist/shared-video-playlist.module' import { SharedVideoPlaylistModule } from '../shared-video-playlist/shared-video-playlist.module'
import { VideoActionsDropdownComponent } from './video-actions-dropdown.component' import { VideoActionsDropdownComponent } from './video-actions-dropdown.component'
import { VideoDownloadComponent } from './video-download.component' import { VideoDownloadComponent } from './video-download.component'
@ -18,7 +19,8 @@ import { VideosSelectionComponent } from './videos-selection.component'
SharedModerationModule, SharedModerationModule,
SharedVideoPlaylistModule, SharedVideoPlaylistModule,
SharedThumbnailModule, SharedThumbnailModule,
SharedGlobalIconModule SharedGlobalIconModule,
SharedVideoLiveModule
], ],
declarations: [ declarations: [

View File

@ -18,4 +18,5 @@
<my-video-download #videoDownloadModal></my-video-download> <my-video-download #videoDownloadModal></my-video-download>
<my-video-report #videoReportModal [video]="video"></my-video-report> <my-video-report #videoReportModal [video]="video"></my-video-report>
<my-video-block #videoBlockModal [video]="video" (videoBlocked)="onVideoBlocked()"></my-video-block> <my-video-block #videoBlockModal [video]="video" (videoBlocked)="onVideoBlocked()"></my-video-block>
<my-live-stream-information #liveStreamInformationModal *ngIf="displayOptions.liveInfo"></my-live-stream-information>
</ng-container> </ng-container>

View File

@ -13,6 +13,7 @@ import {
VideoDetails, VideoDetails,
VideoService VideoService
} from '../shared-main' } from '../shared-main'
import { LiveStreamInformationComponent } from '../shared-video-live'
import { VideoAddToPlaylistComponent } from '../shared-video-playlist' import { VideoAddToPlaylistComponent } from '../shared-video-playlist'
import { VideoDownloadComponent } from './video-download.component' import { VideoDownloadComponent } from './video-download.component'
@ -25,6 +26,7 @@ export type VideoActionsDisplayType = {
report?: boolean report?: boolean
duplicate?: boolean duplicate?: boolean
mute?: boolean mute?: boolean
liveInfo?: boolean
} }
@Component({ @Component({
@ -39,6 +41,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
@ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent @ViewChild('videoReportModal') videoReportModal: VideoReportComponent
@ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent @ViewChild('videoBlockModal') videoBlockModal: VideoBlockComponent
@ViewChild('liveStreamInformationModal') liveStreamInformationModal: LiveStreamInformationComponent
@Input() video: Video | VideoDetails @Input() video: Video | VideoDetails
@Input() videoCaptions: VideoCaption[] = [] @Input() videoCaptions: VideoCaption[] = []
@ -51,7 +54,8 @@ export class VideoActionsDropdownComponent implements OnChanges {
delete: true, delete: true,
report: true, report: true,
duplicate: true, duplicate: true,
mute: true mute: true,
liveInfo: false
} }
@Input() placement = 'left' @Input() placement = 'left'
@ -127,6 +131,12 @@ export class VideoActionsDropdownComponent implements OnChanges {
this.videoBlockModal.show() this.videoBlockModal.show()
} }
showLiveInfoModal (video: Video) {
this.modalOpened.emit()
this.liveStreamInformationModal.show(video)
}
/* Actions checker */ /* Actions checker */
isVideoUpdatable () { isVideoUpdatable () {
@ -145,6 +155,10 @@ export class VideoActionsDropdownComponent implements OnChanges {
return this.video.isUnblockableBy(this.user) return this.video.isUnblockableBy(this.user)
} }
isVideoLiveInfoAvailable () {
return this.video.isLiveInfoAvailableBy(this.user)
}
isVideoDownloadable () { isVideoDownloadable () {
return this.video && return this.video &&
this.video.isLive !== true && this.video.isLive !== true &&
@ -260,6 +274,12 @@ export class VideoActionsDropdownComponent implements OnChanges {
isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(), isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(),
iconName: 'download' iconName: 'download'
}, },
{
label: $localize`Display live information`,
handler: ({ video }) => this.showLiveInfoModal(video),
isDisplayed: () => this.isVideoLiveInfoAvailable(),
iconName: 'live'
},
{ {
label: $localize`Update`, label: $localize`Update`,
linkBuilder: ({ video }) => [ '/videos/update', video.uuid ], linkBuilder: ({ video }) => [ '/videos/update', video.uuid ],