diff options
author | Chocobozzz <me@florianbigard.com> | 2021-05-05 12:10:00 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-05-05 12:10:00 +0200 |
commit | 7a22a0a56aa75fbb1ba986a5d2c606e1343f30c2 (patch) | |
tree | 263bbbe0066710c3e2dfe0bcae9ac33c3f2949ea | |
parent | c9783c7b72b4b2af7730522693ba5d36b59783f0 (diff) | |
download | PeerTube-7a22a0a56aa75fbb1ba986a5d2c606e1343f30c2.tar.gz PeerTube-7a22a0a56aa75fbb1ba986a5d2c606e1343f30c2.tar.zst PeerTube-7a22a0a56aa75fbb1ba986a5d2c606e1343f30c2.zip |
Add ability to search live videos
4 files changed, 62 insertions, 22 deletions
diff --git a/client/src/app/+search/search-filters.component.html b/client/src/app/+search/search-filters.component.html index 1d1e7b868..421bc7f6f 100644 --- a/client/src/app/+search/search-filters.component.html +++ b/client/src/app/+search/search-filters.component.html | |||
@@ -18,6 +18,25 @@ | |||
18 | 18 | ||
19 | <div class="form-group"> | 19 | <div class="form-group"> |
20 | <div class="radio-label label-container"> | 20 | <div class="radio-label label-container"> |
21 | <label i18n>Display only</label> | ||
22 | <button i18n class="reset-button reset-button-small" (click)="resetField('isLive')" *ngIf="advancedSearch.isLive !== undefined"> | ||
23 | Reset | ||
24 | </button> | ||
25 | </div> | ||
26 | |||
27 | <div class="peertube-radio-container"> | ||
28 | <input type="radio" name="isLive" id="isLiveTrue" value="true" [(ngModel)]="advancedSearch.isLive"> | ||
29 | <label i18n for="isLiveTrue" class="radio">Live videos</label> | ||
30 | </div> | ||
31 | |||
32 | <div class="peertube-radio-container"> | ||
33 | <input type="radio" name="isLive" id="isLiveFalse" value="false" [(ngModel)]="advancedSearch.isLive"> | ||
34 | <label i18n for="isLiveFalse" class="radio">VOD videos</label> | ||
35 | </div> | ||
36 | </div> | ||
37 | |||
38 | <div class="form-group"> | ||
39 | <div class="radio-label label-container"> | ||
21 | <label i18n>Display sensitive content</label> | 40 | <label i18n>Display sensitive content</label> |
22 | <button i18n class="reset-button reset-button-small" (click)="resetField('nsfw')" *ngIf="advancedSearch.nsfw !== undefined"> | 41 | <button i18n class="reset-button reset-button-small" (click)="resetField('nsfw')" *ngIf="advancedSearch.nsfw !== undefined"> |
23 | Reset | 42 | Reset |
@@ -44,7 +63,7 @@ | |||
44 | </div> | 63 | </div> |
45 | 64 | ||
46 | <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges"> | 65 | <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges"> |
47 | <input type="radio" (change)="inputUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange"> | 66 | <input type="radio" (change)="onInputUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange"> |
48 | <label [for]="date.id" class="radio">{{ date.label }}</label> | 67 | <label [for]="date.id" class="radio">{{ date.label }}</label> |
49 | </div> | 68 | </div> |
50 | </div> | 69 | </div> |
@@ -60,7 +79,7 @@ | |||
60 | <div class="row"> | 79 | <div class="row"> |
61 | <div class="pl-0 col-sm-6"> | 80 | <div class="pl-0 col-sm-6"> |
62 | <input | 81 | <input |
63 | (change)="inputUpdated()" | 82 | (change)="onInputUpdated()" |
64 | (keydown.enter)="$event.preventDefault()" | 83 | (keydown.enter)="$event.preventDefault()" |
65 | type="text" id="original-publication-after" name="original-publication-after" | 84 | type="text" id="original-publication-after" name="original-publication-after" |
66 | i18n-placeholder placeholder="After..." | 85 | i18n-placeholder placeholder="After..." |
@@ -70,7 +89,7 @@ | |||
70 | </div> | 89 | </div> |
71 | <div class="pr-0 col-sm-6"> | 90 | <div class="pr-0 col-sm-6"> |
72 | <input | 91 | <input |
73 | (change)="inputUpdated()" | 92 | (change)="onInputUpdated()" |
74 | (keydown.enter)="$event.preventDefault()" | 93 | (keydown.enter)="$event.preventDefault()" |
75 | type="text" id="original-publication-before" name="original-publication-before" | 94 | type="text" id="original-publication-before" name="original-publication-before" |
76 | i18n-placeholder placeholder="Before..." | 95 | i18n-placeholder placeholder="Before..." |
@@ -93,7 +112,7 @@ | |||
93 | </div> | 112 | </div> |
94 | 113 | ||
95 | <div class="peertube-radio-container" *ngFor="let duration of durationRanges"> | 114 | <div class="peertube-radio-container" *ngFor="let duration of durationRanges"> |
96 | <input type="radio" (change)="inputUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange"> | 115 | <input type="radio" (change)="onInputUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange"> |
97 | <label [for]="duration.id" class="radio">{{ duration.label }}</label> | 116 | <label [for]="duration.id" class="radio">{{ duration.label }}</label> |
98 | </div> | 117 | </div> |
99 | </div> | 118 | </div> |
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts index a2af9a942..59aba22ff 100644 --- a/client/src/app/+search/search-filters.component.ts +++ b/client/src/app/+search/search-filters.component.ts | |||
@@ -3,6 +3,8 @@ import { ServerService } from '@app/core' | |||
3 | import { AdvancedSearch } from '@app/shared/shared-search' | 3 | import { AdvancedSearch } from '@app/shared/shared-search' |
4 | import { ServerConfig, VideoConstant } from '@shared/models' | 4 | import { ServerConfig, VideoConstant } from '@shared/models' |
5 | 5 | ||
6 | type FormOption = { id: string, label: string } | ||
7 | |||
6 | @Component({ | 8 | @Component({ |
7 | selector: 'my-search-filters', | 9 | selector: 'my-search-filters', |
8 | styleUrls: [ './search-filters.component.scss' ], | 10 | styleUrls: [ './search-filters.component.scss' ], |
@@ -17,9 +19,10 @@ export class SearchFiltersComponent implements OnInit { | |||
17 | videoLicences: VideoConstant<number>[] = [] | 19 | videoLicences: VideoConstant<number>[] = [] |
18 | videoLanguages: VideoConstant<string>[] = [] | 20 | videoLanguages: VideoConstant<string>[] = [] |
19 | 21 | ||
20 | publishedDateRanges: { id: string, label: string }[] = [] | 22 | publishedDateRanges: FormOption[] = [] |
21 | sorts: { id: string, label: string }[] = [] | 23 | sorts: FormOption[] = [] |
22 | durationRanges: { id: string, label: string }[] = [] | 24 | durationRanges: FormOption[] = [] |
25 | videoType: FormOption[] = [] | ||
23 | 26 | ||
24 | publishedDateRange: string | 27 | publishedDateRange: string |
25 | durationRange: string | 28 | durationRange: string |
@@ -34,10 +37,6 @@ export class SearchFiltersComponent implements OnInit { | |||
34 | ) { | 37 | ) { |
35 | this.publishedDateRanges = [ | 38 | this.publishedDateRanges = [ |
36 | { | 39 | { |
37 | id: 'any_published_date', | ||
38 | label: $localize`Any` | ||
39 | }, | ||
40 | { | ||
41 | id: 'today', | 40 | id: 'today', |
42 | label: $localize`Today` | 41 | label: $localize`Today` |
43 | }, | 42 | }, |
@@ -55,12 +54,19 @@ export class SearchFiltersComponent implements OnInit { | |||
55 | } | 54 | } |
56 | ] | 55 | ] |
57 | 56 | ||
58 | this.durationRanges = [ | 57 | this.videoType = [ |
59 | { | 58 | { |
60 | id: 'any_duration', | 59 | id: 'vod', |
61 | label: $localize`Any` | 60 | label: $localize`VOD videos` |
62 | }, | 61 | }, |
63 | { | 62 | { |
63 | id: 'live', | ||
64 | label: $localize`Live videos` | ||
65 | } | ||
66 | ] | ||
67 | |||
68 | this.durationRanges = [ | ||
69 | { | ||
64 | id: 'short', | 70 | id: 'short', |
65 | label: $localize`Short (< 4 min)` | 71 | label: $localize`Short (< 4 min)` |
66 | }, | 72 | }, |
@@ -104,24 +110,26 @@ export class SearchFiltersComponent implements OnInit { | |||
104 | this.loadOriginallyPublishedAtYears() | 110 | this.loadOriginallyPublishedAtYears() |
105 | } | 111 | } |
106 | 112 | ||
107 | inputUpdated () { | 113 | onInputUpdated () { |
108 | this.updateModelFromDurationRange() | 114 | this.updateModelFromDurationRange() |
109 | this.updateModelFromPublishedRange() | 115 | this.updateModelFromPublishedRange() |
110 | this.updateModelFromOriginallyPublishedAtYears() | 116 | this.updateModelFromOriginallyPublishedAtYears() |
111 | } | 117 | } |
112 | 118 | ||
113 | formUpdated () { | 119 | formUpdated () { |
114 | this.inputUpdated() | 120 | this.onInputUpdated() |
115 | this.filtered.emit(this.advancedSearch) | 121 | this.filtered.emit(this.advancedSearch) |
116 | } | 122 | } |
117 | 123 | ||
118 | reset () { | 124 | reset () { |
119 | this.advancedSearch.reset() | 125 | this.advancedSearch.reset() |
126 | |||
127 | this.resetOriginalPublicationYears() | ||
128 | |||
120 | this.durationRange = undefined | 129 | this.durationRange = undefined |
121 | this.publishedDateRange = undefined | 130 | this.publishedDateRange = undefined |
122 | this.originallyPublishedStartYear = undefined | 131 | |
123 | this.originallyPublishedEndYear = undefined | 132 | this.onInputUpdated() |
124 | this.inputUpdated() | ||
125 | } | 133 | } |
126 | 134 | ||
127 | resetField (fieldName: string, value?: any) { | 135 | resetField (fieldName: string, value?: any) { |
@@ -130,7 +138,7 @@ export class SearchFiltersComponent implements OnInit { | |||
130 | 138 | ||
131 | resetLocalField (fieldName: string, value?: any) { | 139 | resetLocalField (fieldName: string, value?: any) { |
132 | this[fieldName] = value | 140 | this[fieldName] = value |
133 | this.inputUpdated() | 141 | this.onInputUpdated() |
134 | } | 142 | } |
135 | 143 | ||
136 | resetOriginalPublicationYears () { | 144 | resetOriginalPublicationYears () { |
diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts index 0e3924841..2c83f53b6 100644 --- a/client/src/app/shared/shared-search/advanced-search.model.ts +++ b/client/src/app/shared/shared-search/advanced-search.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { BooleanBothQuery, SearchTargetType } from '@shared/models' | 1 | import { BooleanBothQuery, BooleanQuery, SearchTargetType, VideosSearchQuery } from '@shared/models' |
2 | 2 | ||
3 | export class AdvancedSearch { | 3 | export class AdvancedSearch { |
4 | startDate: string // ISO 8601 | 4 | startDate: string // ISO 8601 |
@@ -21,6 +21,8 @@ export class AdvancedSearch { | |||
21 | durationMin: number // seconds | 21 | durationMin: number // seconds |
22 | durationMax: number // seconds | 22 | durationMax: number // seconds |
23 | 23 | ||
24 | isLive: BooleanQuery | ||
25 | |||
24 | sort: string | 26 | sort: string |
25 | 27 | ||
26 | searchTarget: SearchTargetType | 28 | searchTarget: SearchTargetType |
@@ -41,6 +43,8 @@ export class AdvancedSearch { | |||
41 | tagsOneOf?: any | 43 | tagsOneOf?: any |
42 | tagsAllOf?: any | 44 | tagsAllOf?: any |
43 | 45 | ||
46 | isLive?: BooleanQuery | ||
47 | |||
44 | durationMin?: string | 48 | durationMin?: string |
45 | durationMax?: string | 49 | durationMax?: string |
46 | sort?: string | 50 | sort?: string |
@@ -54,6 +58,8 @@ export class AdvancedSearch { | |||
54 | this.originallyPublishedEndDate = options.originallyPublishedEndDate || undefined | 58 | this.originallyPublishedEndDate = options.originallyPublishedEndDate || undefined |
55 | 59 | ||
56 | this.nsfw = options.nsfw || undefined | 60 | this.nsfw = options.nsfw || undefined |
61 | this.isLive = options.isLive || undefined | ||
62 | |||
57 | this.categoryOneOf = options.categoryOneOf || undefined | 63 | this.categoryOneOf = options.categoryOneOf || undefined |
58 | this.licenceOneOf = options.licenceOneOf || undefined | 64 | this.licenceOneOf = options.licenceOneOf || undefined |
59 | this.languageOneOf = options.languageOneOf || undefined | 65 | this.languageOneOf = options.languageOneOf || undefined |
@@ -94,6 +100,7 @@ export class AdvancedSearch { | |||
94 | this.tagsAllOf = undefined | 100 | this.tagsAllOf = undefined |
95 | this.durationMin = undefined | 101 | this.durationMin = undefined |
96 | this.durationMax = undefined | 102 | this.durationMax = undefined |
103 | this.isLive = undefined | ||
97 | 104 | ||
98 | this.sort = '-match' | 105 | this.sort = '-match' |
99 | } | 106 | } |
@@ -112,12 +119,16 @@ export class AdvancedSearch { | |||
112 | tagsAllOf: this.tagsAllOf, | 119 | tagsAllOf: this.tagsAllOf, |
113 | durationMin: this.durationMin, | 120 | durationMin: this.durationMin, |
114 | durationMax: this.durationMax, | 121 | durationMax: this.durationMax, |
122 | isLive: this.isLive, | ||
115 | sort: this.sort, | 123 | sort: this.sort, |
116 | searchTarget: this.searchTarget | 124 | searchTarget: this.searchTarget |
117 | } | 125 | } |
118 | } | 126 | } |
119 | 127 | ||
120 | toAPIObject () { | 128 | toAPIObject (): VideosSearchQuery { |
129 | let isLive: boolean | ||
130 | if (this.isLive) isLive = this.isLive === 'true' | ||
131 | |||
121 | return { | 132 | return { |
122 | startDate: this.startDate, | 133 | startDate: this.startDate, |
123 | endDate: this.endDate, | 134 | endDate: this.endDate, |
@@ -131,6 +142,7 @@ export class AdvancedSearch { | |||
131 | tagsAllOf: this.tagsAllOf, | 142 | tagsAllOf: this.tagsAllOf, |
132 | durationMin: this.durationMin, | 143 | durationMin: this.durationMin, |
133 | durationMax: this.durationMax, | 144 | durationMax: this.durationMax, |
145 | isLive, | ||
134 | sort: this.sort, | 146 | sort: this.sort, |
135 | searchTarget: this.searchTarget | 147 | searchTarget: this.searchTarget |
136 | } | 148 | } |
diff --git a/shared/models/search/boolean-both-query.model.ts b/shared/models/search/boolean-both-query.model.ts index 57b0e8d44..d6a438249 100644 --- a/shared/models/search/boolean-both-query.model.ts +++ b/shared/models/search/boolean-both-query.model.ts | |||
@@ -1 +1,2 @@ | |||
1 | export type BooleanBothQuery = 'true' | 'false' | 'both' | 1 | export type BooleanBothQuery = 'true' | 'false' | 'both' |
2 | export type BooleanQuery = 'true' | 'false' | ||