diff --git a/server/openapi.json b/server/openapi.json index e6c9234b..8c05c3d4 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -1167,12 +1167,11 @@ "in": "query", "required": false, "schema": { - "enum": [ - "paused", - "playing", - null + "allOf": [ + { + "$ref": "#/components/schemas/ProgressAction" + } ], - "type": "string", "nullable": true } }, @@ -1196,6 +1195,62 @@ } } } + }, + "get": { + "description": "Get all progress", + "tags": [ + "Progress" + ], + "operationId": "get", + "parameters": [ + { + "name": "action", + "in": "query", + "required": false, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ProgressAction" + } + ], + "nullable": true + } + }, + { + "name": "extended", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "#/components/schemas/Progress" + } + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProgressExtended" + } + } + ] + } + } + } + } + } } }, "/api/progress/by-external-id": { @@ -4216,6 +4271,90 @@ "url" ] }, + "ProgressAction": { + "enum": [ + "paused", + "playing" + ], + "type": "string" + }, + "Progress": { + "type": "object", + "properties": { + "id": { + "type": "number", + "nullable": true + }, + "date": { + "type": "number" + }, + "mediaItemId": { + "type": "number" + }, + "episodeId": { + "type": "number", + "nullable": true + }, + "userId": { + "type": "number" + }, + "progress": { + "type": "number", + "nullable": true + }, + "duration": { + "type": "number", + "nullable": true + }, + "action": { + "allOf": [ + { + "$ref": "#/components/schemas/ProgressAction" + } + ], + "nullable": true + }, + "device": { + "type": "string", + "nullable": true + } + }, + "required": [ + "date", + "mediaItemId", + "userId" + ] + }, + "ProgressExtended": { + "allOf": [ + { + "$ref": "#/components/schemas/Progress" + }, + { + "type": "object", + "properties": { + "mediaType": { + "$ref": "#/components/schemas/MediaType" + }, + "tmdbId": { + "type": "number", + "nullable": true + }, + "seasonNumber": { + "type": "number", + "nullable": true + }, + "episodeNumber": { + "type": "number", + "nullable": true + } + }, + "required": [ + "mediaType" + ] + } + ] + }, "LastSeenAt": { "enum": [ "custom_date", diff --git a/server/src/controllers/progress.ts b/server/src/controllers/progress.ts index 0b6e6319..d8a319ed 100644 --- a/server/src/controllers/progress.ts +++ b/server/src/controllers/progress.ts @@ -5,10 +5,11 @@ import { tvEpisodeRepository } from 'src/repository/episode'; import { mediaItemRepository } from 'src/repository/mediaItem'; import { seenRepository } from 'src/repository/seen'; import { listItemRepository } from 'src/repository/listItemRepository'; +import { progressRepository } from 'src/repository/progress'; import { MediaType } from 'src/entity/mediaItem'; import { findMediaItemOrEpisodeByExternalId } from 'src/metadata/findByExternalId'; import { logger } from 'src/logger'; -import { Progress } from 'src/entity/progress'; +import { Progress, ProgressExtended, ProgressAction } from 'src/entity/progress'; import { Database } from 'src/dbconfig'; import { Seen } from 'src/entity/seen'; /** @@ -164,6 +165,60 @@ export class ProgressController { res.send(); }); + + /** + * @description Get all progress + * @openapi_operationId get + */ + get = createExpressRoute<{ + method: 'get'; + path: '/api/progress'; + /** + * @description List of progress + */ + requestQuery: { + action?: ProgressAction; + extended?: boolean; + }; + responseBody: Progress[] | ProgressExtended[]; + }>(async (req, res) => { + const userId = Number(req.user); + + const action = req.query.action; + const extended = req.query.extended; + + const list = await Database.knex('progress') + .modify((qb) => { + if (extended) { + qb + .select( + 'progress.id', + 'progress.date', + 'progress.mediaItemId', + 'progress.episodeId', + 'progress.userId', + 'progress.progress', + 'progress.duration', + 'progress.action', + 'progress.device', + 'mediaItem.mediaType', + 'mediaItem.tmdbId', + 'episode.seasonNumber', + 'episode.episodeNumber', + ) + .leftJoin('mediaItem', 'mediaItem.id', 'progress.mediaItemId') + .leftJoin('episode', 'episode.id', 'progress.episodeId') + } + }) + .where((qb) => { + qb.where('userId', userId) + if (action) { + qb.where('action', action); + } + }) + + res.send(list); + }); } const addItem = async (args: Progress) => { diff --git a/server/src/entity/progress.ts b/server/src/entity/progress.ts index 9f6d8733..19ff9c20 100644 --- a/server/src/entity/progress.ts +++ b/server/src/entity/progress.ts @@ -1,3 +1,7 @@ +import { MediaType } from 'src/entity/mediaItem'; + +export type ProgressAction = 'playing' | 'paused'; + export type Progress = { id?: number; date: number; @@ -6,10 +10,17 @@ export type Progress = { userId: number; progress?: number; duration?: number; - action?: 'playing' | 'paused'; + action?: ProgressAction; device?: string; }; +export type ProgressExtended = Progress & { + mediaType: MediaType; + tmdbId?: number; + seasonNumber?: number; + episodeNumber?: number; +}; + export const progressColumns = [ 'id', 'date', diff --git a/server/src/generated/routes/routes.ts b/server/src/generated/routes/routes.ts index 9c761a98..e3321e3a 100644 --- a/server/src/generated/routes/routes.ts +++ b/server/src/generated/routes/routes.ts @@ -685,6 +685,9 @@ router.put( validatorHandler({ requestQuerySchema: { $schema: 'http://json-schema.org/draft-07/schema#', + definitions: { + ProgressAction: { enum: ['paused', 'playing'], type: 'string' }, + }, type: 'object', properties: { mediaItemId: { type: 'number' }, @@ -692,7 +695,9 @@ router.put( progress: { type: ['number', 'null'] }, date: { type: 'number' }, duration: { type: ['number', 'null'] }, - action: { enum: ['paused', 'playing', null], type: 'string' }, + action: { + oneOf: [{ $ref: '#/definitions/ProgressAction' }, { type: 'null' }], + }, device: { type: ['string', 'null'] }, }, required: ['date', 'mediaItemId'], @@ -747,6 +752,25 @@ router.delete( }), _ProgressController.deleteById ); +router.get( + '/api/progress', + validatorHandler({ + requestQuerySchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + definitions: { + ProgressAction: { enum: ['paused', 'playing'], type: 'string' }, + }, + type: 'object', + properties: { + action: { + oneOf: [{ $ref: '#/definitions/ProgressAction' }, { type: 'null' }], + }, + extended: { type: ['boolean', 'null'] }, + }, + }, + }), + _ProgressController.get +); router.put( '/api/rating', validatorHandler({