diff options
-rw-r--r-- | client/src/app/search/advanced-search.model.ts | 14 | ||||
-rw-r--r-- | client/src/app/search/search-filters.component.html | 23 | ||||
-rw-r--r-- | client/src/app/search/search-filters.component.ts | 42 | ||||
-rw-r--r-- | server/models/video/video.ts | 11 | ||||
-rw-r--r-- | server/tests/api/check-params/search.ts | 6 | ||||
-rw-r--r-- | server/tests/api/search/search-videos.ts | 66 | ||||
-rw-r--r-- | shared/models/search/videos-search-query.model.ts | 3 |
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[] |