aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/search/advanced-search.model.ts14
-rw-r--r--client/src/app/search/search-filters.component.html23
-rw-r--r--client/src/app/search/search-filters.component.ts42
-rw-r--r--server/models/video/video.ts11
-rw-r--r--server/tests/api/check-params/search.ts6
-rw-r--r--server/tests/api/search/search-videos.ts66
-rw-r--r--shared/models/search/videos-search-query.model.ts3
7 files changed, 163 insertions, 2 deletions
diff --git a/client/src/app/search/advanced-search.model.ts b/client/src/app/search/advanced-search.model.ts
index 033fa9bba..5b713e145 100644
--- a/client/src/app/search/advanced-search.model.ts
+++ b/client/src/app/search/advanced-search.model.ts
@@ -4,6 +4,9 @@ export class AdvancedSearch {
4 startDate: string // ISO 8601 4 startDate: string // ISO 8601
5 endDate: string // ISO 8601 5 endDate: string // ISO 8601
6 6
7 originallyPublishedStartDate: string // ISO 8601
8 originallyPublishedEndDate: string // ISO 8601
9
7 nsfw: NSFWQuery 10 nsfw: NSFWQuery
8 11
9 categoryOneOf: string 12 categoryOneOf: string
@@ -23,6 +26,8 @@ export class AdvancedSearch {
23 constructor (options?: { 26 constructor (options?: {
24 startDate?: string 27 startDate?: string
25 endDate?: string 28 endDate?: string
29 originallyPublishedStartDate?: string
30 originallyPublishedEndDate?: string
26 nsfw?: NSFWQuery 31 nsfw?: NSFWQuery
27 categoryOneOf?: string 32 categoryOneOf?: string
28 licenceOneOf?: string 33 licenceOneOf?: string
@@ -37,6 +42,9 @@ export class AdvancedSearch {
37 42
38 this.startDate = options.startDate || undefined 43 this.startDate = options.startDate || undefined
39 this.endDate = options.endDate || undefined 44 this.endDate = options.endDate || undefined
45 this.originallyPublishedStartDate = options.originallyPublishedStartDate || undefined
46 this.originallyPublishedEndDate = options.originallyPublishedEndDate || undefined
47
40 this.nsfw = options.nsfw || undefined 48 this.nsfw = options.nsfw || undefined
41 this.categoryOneOf = options.categoryOneOf || undefined 49 this.categoryOneOf = options.categoryOneOf || undefined
42 this.licenceOneOf = options.licenceOneOf || undefined 50 this.licenceOneOf = options.licenceOneOf || undefined
@@ -66,6 +74,8 @@ export class AdvancedSearch {
66 reset () { 74 reset () {
67 this.startDate = undefined 75 this.startDate = undefined
68 this.endDate = undefined 76 this.endDate = undefined
77 this.originallyPublishedStartDate = undefined
78 this.originallyPublishedEndDate = undefined
69 this.nsfw = undefined 79 this.nsfw = undefined
70 this.categoryOneOf = undefined 80 this.categoryOneOf = undefined
71 this.licenceOneOf = undefined 81 this.licenceOneOf = undefined
@@ -82,6 +92,8 @@ export class AdvancedSearch {
82 return { 92 return {
83 startDate: this.startDate, 93 startDate: this.startDate,
84 endDate: this.endDate, 94 endDate: this.endDate,
95 originallyPublishedStartDate: this.originallyPublishedStartDate,
96 originallyPublishedEndDate: this.originallyPublishedEndDate,
85 nsfw: this.nsfw, 97 nsfw: this.nsfw,
86 categoryOneOf: this.categoryOneOf, 98 categoryOneOf: this.categoryOneOf,
87 licenceOneOf: this.licenceOneOf, 99 licenceOneOf: this.licenceOneOf,
@@ -98,6 +110,8 @@ export class AdvancedSearch {
98 return { 110 return {
99 startDate: this.startDate, 111 startDate: this.startDate,
100 endDate: this.endDate, 112 endDate: this.endDate,
113 originallyPublishedStartDate: this.originallyPublishedStartDate,
114 originallyPublishedEndDate: this.originallyPublishedEndDate,
101 nsfw: this.nsfw, 115 nsfw: this.nsfw,
102 categoryOneOf: this.intoArray(this.categoryOneOf), 116 categoryOneOf: this.intoArray(this.categoryOneOf),
103 licenceOneOf: this.intoArray(this.licenceOneOf), 117 licenceOneOf: this.intoArray(this.licenceOneOf),
diff --git a/client/src/app/search/search-filters.component.html b/client/src/app/search/search-filters.component.html
index 74bb781f4..8220a990b 100644
--- a/client/src/app/search/search-filters.component.html
+++ b/client/src/app/search/search-filters.component.html
@@ -21,6 +21,27 @@
21 </div> 21 </div>
22 22
23 <div class="form-group"> 23 <div class="form-group">
24 <label i18n for="original-publication-after">Original publication year</label>
25
26 <div class="row">
27 <div class="col-sm-6">
28 <input
29 type="text" id="original-publication-after" name="original-publication-after"
30 i18n-placeholder placeholder="After..."
31 [(ngModel)]="originallyPublishedStartYear"
32 >
33 </div>
34 <div class="col-sm-6">
35 <input
36 type="text" id="original-publication-before" name="original-publication-before"
37 i18n-placeholder placeholder="Before..."
38 [(ngModel)]="originallyPublishedEndYear"
39 >
40 </div>
41 </div>
42 </div>
43
44 <div class="form-group">
24 <div i18n class="radio-label">Duration</div> 45 <div i18n class="radio-label">Duration</div>
25 46
26 <div class="peertube-radio-container" *ngFor="let duration of durationRanges"> 47 <div class="peertube-radio-container" *ngFor="let duration of durationRanges">
@@ -93,4 +114,4 @@
93 <div class="submit-button"> 114 <div class="submit-button">
94 <input type="submit" i18n-value value="Filter"> 115 <input type="submit" i18n-value value="Filter">
95 </div> 116 </div>
96</form> \ No newline at end of file 117</form>
diff --git a/client/src/app/search/search-filters.component.ts b/client/src/app/search/search-filters.component.ts
index 3fdc6df35..762a6b7f2 100644
--- a/client/src/app/search/search-filters.component.ts
+++ b/client/src/app/search/search-filters.component.ts
@@ -25,6 +25,9 @@ export class SearchFiltersComponent implements OnInit {
25 publishedDateRange: string 25 publishedDateRange: string
26 durationRange: string 26 durationRange: string
27 27
28 originallyPublishedStartYear: string
29 originallyPublishedEndYear: string
30
28 constructor ( 31 constructor (
29 private i18n: I18n, 32 private i18n: I18n,
30 private serverService: ServerService 33 private serverService: ServerService
@@ -86,15 +89,27 @@ export class SearchFiltersComponent implements OnInit {
86 89
87 this.loadFromDurationRange() 90 this.loadFromDurationRange()
88 this.loadFromPublishedRange() 91 this.loadFromPublishedRange()
92 this.loadOriginallyPublishedAtYears()
89 } 93 }
90 94
91 formUpdated () { 95 formUpdated () {
92 this.updateModelFromDurationRange() 96 this.updateModelFromDurationRange()
93 this.updateModelFromPublishedRange() 97 this.updateModelFromPublishedRange()
98 this.updateModelFromOriginallyPublishedAtYears()
94 99
95 this.filtered.emit(this.advancedSearch) 100 this.filtered.emit(this.advancedSearch)
96 } 101 }
97 102
103 private loadOriginallyPublishedAtYears () {
104 this.originallyPublishedStartYear = this.advancedSearch.originallyPublishedStartDate
105 ? new Date(this.advancedSearch.originallyPublishedStartDate).getFullYear().toString()
106 : null
107
108 this.originallyPublishedEndYear = this.advancedSearch.originallyPublishedEndDate
109 ? new Date(this.advancedSearch.originallyPublishedEndDate).getFullYear().toString()
110 : null
111 }
112
98 private loadFromDurationRange () { 113 private loadFromDurationRange () {
99 if (this.advancedSearch.durationMin || this.advancedSearch.durationMax) { 114 if (this.advancedSearch.durationMin || this.advancedSearch.durationMax) {
100 const fourMinutes = 60 * 4 115 const fourMinutes = 60 * 4
@@ -127,6 +142,32 @@ export class SearchFiltersComponent implements OnInit {
127 } 142 }
128 } 143 }
129 144
145 private updateModelFromOriginallyPublishedAtYears () {
146 const baseDate = new Date()
147 baseDate.setHours(0, 0, 0, 0)
148 baseDate.setMonth(0, 1)
149
150 if (this.originallyPublishedStartYear) {
151 const year = parseInt(this.originallyPublishedStartYear, 10)
152 const start = new Date(baseDate)
153 start.setFullYear(year)
154
155 this.advancedSearch.originallyPublishedStartDate = start.toISOString()
156 } else {
157 this.advancedSearch.originallyPublishedStartDate = null
158 }
159
160 if (this.originallyPublishedEndYear) {
161 const year = parseInt(this.originallyPublishedEndYear, 10)
162 const end = new Date(baseDate)
163 end.setFullYear(year)
164
165 this.advancedSearch.originallyPublishedEndDate = end.toISOString()
166 } else {
167 this.advancedSearch.originallyPublishedEndDate = null
168 }
169 }
170
130 private updateModelFromDurationRange () { 171 private updateModelFromDurationRange () {
131 if (!this.durationRange) return 172 if (!this.durationRange) return
132 173
@@ -174,4 +215,5 @@ export class SearchFiltersComponent implements OnInit {
174 215
175 this.advancedSearch.startDate = date.toISOString() 216 this.advancedSearch.startDate = date.toISOString()
176 } 217 }
218
177} 219}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 215e26d7d..fe81fab1a 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1174,6 +1174,8 @@ export class VideoModel extends Model<VideoModel> {
1174 sort?: string 1174 sort?: string
1175 startDate?: string // ISO 8601 1175 startDate?: string // ISO 8601
1176 endDate?: string // ISO 8601 1176 endDate?: string // ISO 8601
1177 originallyPublishedStartDate?: string
1178 originallyPublishedEndDate?: string
1177 nsfw?: boolean 1179 nsfw?: boolean
1178 categoryOneOf?: number[] 1180 categoryOneOf?: number[]
1179 licenceOneOf?: number[] 1181 licenceOneOf?: number[]
@@ -1196,6 +1198,15 @@ export class VideoModel extends Model<VideoModel> {
1196 whereAnd.push({ publishedAt: publishedAtRange }) 1198 whereAnd.push({ publishedAt: publishedAtRange })
1197 } 1199 }
1198 1200
1201 if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) {
1202 const originallyPublishedAtRange = {}
1203
1204 if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Sequelize.Op.gte ] = options.originallyPublishedStartDate
1205 if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Sequelize.Op.lte ] = options.originallyPublishedEndDate
1206
1207 whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange })
1208 }
1209
1199 if (options.durationMin || options.durationMax) { 1210 if (options.durationMin || options.durationMax) {
1200 const durationRange = {} 1211 const durationRange = {}
1201 1212
diff --git a/server/tests/api/check-params/search.ts b/server/tests/api/check-params/search.ts
index aa81965f3..7b7e07784 100644
--- a/server/tests/api/check-params/search.ts
+++ b/server/tests/api/check-params/search.ts
@@ -113,6 +113,12 @@ describe('Test videos API validator', function () {
113 113
114 const customQuery2 = immutableAssign(query, { endDate: 'hello' }) 114 const customQuery2 = immutableAssign(query, { endDate: 'hello' })
115 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: 400 }) 115 await makeGetRequest({ url: server.url, path, query: customQuery2, statusCodeExpected: 400 })
116
117 const customQuery3 = immutableAssign(query, { originallyPublishedStartDate: 'hello' })
118 await makeGetRequest({ url: server.url, path, query: customQuery3, statusCodeExpected: 400 })
119
120 const customQuery4 = immutableAssign(query, { originallyPublishedEndDate: 'hello' })
121 await makeGetRequest({ url: server.url, path, query: customQuery4, statusCodeExpected: 400 })
116 }) 122 })
117 }) 123 })
118 124
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index 50da837da..fa4078b99 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -60,7 +60,10 @@ describe('Test a videos search', function () {
60 const attributes6 = immutableAssign(attributes1, { name: attributes1.name + ' - 6', tags: [ 't1', 't2 '] }) 60 const attributes6 = immutableAssign(attributes1, { name: attributes1.name + ' - 6', tags: [ 't1', 't2 '] })
61 await uploadVideo(server.url, server.accessToken, attributes6) 61 await uploadVideo(server.url, server.accessToken, attributes6)
62 62
63 const attributes7 = immutableAssign(attributes1, { name: attributes1.name + ' - 7' }) 63 const attributes7 = immutableAssign(attributes1, {
64 name: attributes1.name + ' - 7',
65 originallyPublishedAt: '2019-02-12T09:58:08.286Z'
66 })
64 await uploadVideo(server.url, server.accessToken, attributes7) 67 await uploadVideo(server.url, server.accessToken, attributes7)
65 68
66 const attributes8 = immutableAssign(attributes1, { name: attributes1.name + ' - 8', licence: 4 }) 69 const attributes8 = immutableAssign(attributes1, { name: attributes1.name + ' - 8', licence: 4 })
@@ -343,6 +346,67 @@ describe('Test a videos search', function () {
343 expect(videos[0].name).to.equal('1111 2222 3333') 346 expect(videos[0].name).to.equal('1111 2222 3333')
344 }) 347 })
345 348
349 it('Should search on originally published date', async function () {
350 const baseQuery = {
351 search: '1111 2222 3333',
352 languageOneOf: [ 'pl', 'fr' ],
353 durationMax: 4,
354 nsfw: 'false' as 'false',
355 licenceOneOf: [ 1, 4 ]
356 }
357
358 {
359 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-02-11T09:58:08.286Z' })
360 const res = await advancedVideosSearch(server.url, query)
361
362 expect(res.body.total).to.equal(1)
363 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7')
364 }
365
366 {
367 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-03-11T09:58:08.286Z' })
368 const res = await advancedVideosSearch(server.url, query)
369
370 expect(res.body.total).to.equal(1)
371 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7')
372 }
373
374 {
375 const query = immutableAssign(baseQuery, { originallyPublishedEndDate: '2019-01-11T09:58:08.286Z' })
376 const res = await advancedVideosSearch(server.url, query)
377
378 expect(res.body.total).to.equal(0)
379 }
380
381 {
382 const query = immutableAssign(baseQuery, { originallyPublishedStartDate: '2019-03-11T09:58:08.286Z' })
383 const res = await advancedVideosSearch(server.url, query)
384
385 expect(res.body.total).to.equal(0)
386 }
387
388 {
389 const query = immutableAssign(baseQuery, {
390 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
391 originallyPublishedEndDate: '2019-01-10T09:58:08.286Z'
392 })
393 const res = await advancedVideosSearch(server.url, query)
394
395 expect(res.body.total).to.equal(0)
396 }
397
398 {
399 const query = immutableAssign(baseQuery, {
400 originallyPublishedStartDate: '2019-01-11T09:58:08.286Z',
401 originallyPublishedEndDate: '2019-04-11T09:58:08.286Z'
402 })
403 const res = await advancedVideosSearch(server.url, query)
404
405 expect(res.body.total).to.equal(1)
406 expect(res.body.data[0].name).to.equal('1111 2222 3333 - 7')
407 }
408 })
409
346 after(async function () { 410 after(async function () {
347 killallServers([ server ]) 411 killallServers([ server ])
348 412
diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts
index 0db220758..838063095 100644
--- a/shared/models/search/videos-search-query.model.ts
+++ b/shared/models/search/videos-search-query.model.ts
@@ -11,6 +11,9 @@ export interface VideosSearchQuery {
11 startDate?: string // ISO 8601 11 startDate?: string // ISO 8601
12 endDate?: string // ISO 8601 12 endDate?: string // ISO 8601
13 13
14 originallyPublishedStartDate?: string // ISO 8601
15 originallyPublishedEndDate?: string // ISO 8601
16
14 nsfw?: NSFWQuery 17 nsfw?: NSFWQuery
15 18
16 categoryOneOf?: number[] 19 categoryOneOf?: number[]