diff --git a/ErsatzTV.Application/Channels/Mapper.cs b/ErsatzTV.Application/Channels/Mapper.cs index b48406ec0..4b0fdd6b8 100644 --- a/ErsatzTV.Application/Channels/Mapper.cs +++ b/ErsatzTV.Application/Channels/Mapper.cs @@ -1,4 +1,5 @@ -using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Api.Channels; +using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.Channels; @@ -19,11 +20,26 @@ internal static class Mapper channel.FallbackFillerId, channel.Playouts?.Count ?? 0); + internal static ChannelResponseModel ProjectToResponseModel(Channel channel) => + new( + channel.Id, + channel.Number, + channel.Name, + channel.FFmpegProfile.Name, + channel.PreferredLanguageCode, + GetStreamingMode(channel)); + private static string GetLogo(Channel channel) => Optional(channel.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo)) .Match(a => a.Path, string.Empty); - private static string GetWatermark(Channel channel) => - Optional(channel.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Watermark)) - .Match(a => a.Path, string.Empty); + private static string GetStreamingMode(Channel channel) => + channel.StreamingMode switch + { + StreamingMode.TransportStream => "MPEG-TS (Legacy)", + StreamingMode.TransportStreamHybrid => "MPEG-TS", + StreamingMode.HttpLiveStreamingDirect => "HLS Direct", + StreamingMode.HttpLiveStreamingSegmenter => "HLS Segmenter", + _ => throw new ArgumentOutOfRangeException() + }; } \ No newline at end of file diff --git a/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApi.cs b/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApi.cs new file mode 100644 index 000000000..58a1a9ea0 --- /dev/null +++ b/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApi.cs @@ -0,0 +1,5 @@ +using ErsatzTV.Core.Api.Channels; + +namespace ErsatzTV.Application.Channels; + +public record GetAllChannelsForApi : IRequest>; diff --git a/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApiHandler.cs b/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApiHandler.cs new file mode 100644 index 000000000..c85b30dc4 --- /dev/null +++ b/ErsatzTV.Application/Channels/Queries/GetAllChannelsForApiHandler.cs @@ -0,0 +1,21 @@ +using ErsatzTV.Core.Api.Channels; +using ErsatzTV.Core.Domain; +using ErsatzTV.Core.Interfaces.Repositories; +using static ErsatzTV.Application.Channels.Mapper; + +namespace ErsatzTV.Application.Channels; + +public class GetAllChannelsForApiHandler : IRequestHandler> +{ + private readonly IChannelRepository _channelRepository; + + public GetAllChannelsForApiHandler(IChannelRepository channelRepository) => _channelRepository = channelRepository; + + public async Task> Handle( + GetAllChannelsForApi request, + CancellationToken cancellationToken) + { + IEnumerable channels = Optional(await _channelRepository.GetAll()).Flatten(); + return channels.Map(ProjectToResponseModel).ToList(); + } +} \ No newline at end of file diff --git a/ErsatzTV.Core/Api/Channels/ChannelResponseModel.cs b/ErsatzTV.Core/Api/Channels/ChannelResponseModel.cs new file mode 100644 index 000000000..129aca437 --- /dev/null +++ b/ErsatzTV.Core/Api/Channels/ChannelResponseModel.cs @@ -0,0 +1,9 @@ +namespace ErsatzTV.Core.Api.Channels; + +public record ChannelResponseModel( + int Id, + string Number, + string Name, + string FFmpegProfile, + string Language, + string StreamingMode); diff --git a/ErsatzTV/Controllers/Api/ChannelController.cs b/ErsatzTV/Controllers/Api/ChannelController.cs new file mode 100644 index 000000000..ba76f20d7 --- /dev/null +++ b/ErsatzTV/Controllers/Api/ChannelController.cs @@ -0,0 +1,20 @@ +using ErsatzTV.Application.Channels; +using ErsatzTV.Core.Api.Channels; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace ErsatzTV.Controllers.Api; + +[ApiController] +public class ChannelController +{ + private readonly IMediator _mediator; + + public ChannelController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpGet("/api/channels")] + public async Task> GetAll() => await _mediator.Send(new GetAllChannelsForApi()); +} diff --git a/ErsatzTV/client-app/package-lock.json b/ErsatzTV/client-app/package-lock.json index d7789e37b..f3eae8e88 100644 --- a/ErsatzTV/client-app/package-lock.json +++ b/ErsatzTV/client-app/package-lock.json @@ -14,6 +14,7 @@ "pinia": "^2.0.11", "roboto-fontface": "*", "vue": "^2.6.14", + "vue-property-decorator": "^9.1.2", "vue-router": "^3.2.0", "vuetify": "^2.6.0" }, @@ -3025,7 +3026,7 @@ "version": "1.4.9", "resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.4.9.tgz", "integrity": "sha512-l6YOeg5LEXmfPqyxAnBaCv1FMRw0OGKJ4m6nOWRm6ngt5TuHcj5ZoBRN+LXh3J0u6Ur3C4VA+RiKT+M0eItr/g==", - "dev": true, + "devOptional": true, "peerDependencies": { "vue": ">= 2.5 < 3" } @@ -9444,7 +9445,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "dev": true, - "optional": true, "bin": { "prettier": "bin-prettier.js" }, @@ -11144,7 +11144,7 @@ "version": "4.5.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11280,6 +11280,15 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" }, + "node_modules/vue-class-component": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz", + "integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==", + "peer": true, + "peerDependencies": { + "vue": "^2.0.0" + } + }, "node_modules/vue-cli-plugin-vuetify": { "version": "2.4.7", "resolved": "https://registry.npmmirror.com/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.4.7.tgz", @@ -11527,6 +11536,15 @@ "node": ">=8" } }, + "node_modules/vue-property-decorator": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz", + "integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==", + "peerDependencies": { + "vue": "*", + "vue-class-component": "*" + } + }, "node_modules/vue-router": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz", @@ -14344,7 +14362,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.1.tgz", "integrity": "sha512-5J/n+Ht4r2eVuncwCXcZPHzYCz/2haktle4WcggWiKeg3jSQVUJbjviPBs6sOo3y/LG3CEfZMP9bPJjVDbexpQ==", - "dev": true + "dev": true, + "requires": {} }, "@vue/cli-service": { "version": "5.0.1", @@ -14556,7 +14575,8 @@ "version": "1.4.9", "resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.4.9.tgz", "integrity": "sha512-l6YOeg5LEXmfPqyxAnBaCv1FMRw0OGKJ4m6nOWRm6ngt5TuHcj5ZoBRN+LXh3J0u6Ur3C4VA+RiKT+M0eItr/g==", - "dev": true + "devOptional": true, + "requires": {} }, "@vue/devtools-api": { "version": "6.0.12", @@ -14777,13 +14797,15 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "8.2.0", @@ -14852,7 +14874,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "ansi-colors": { "version": "4.1.1", @@ -15869,7 +15892,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "dev": true + "dev": true, + "requires": {} }, "csso": { "version": "4.2.0", @@ -16417,7 +16441,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true + "dev": true, + "requires": {} }, "eslint-plugin-prettier": { "version": "4.0.0", @@ -17454,7 +17479,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -18975,25 +19001,29 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.0.tgz", "integrity": "sha512-L0IKF4jAshRyn03SkEO6ar/Ipz2oLywVbg2THf2EqqdNkBwmVMxuTR/RoAltOw4piiaLt3gCAdrbAqmTBInmhg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-empty": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.0.tgz", "integrity": "sha512-782T/buGgb3HOuHOJAHpdyKzAAKsv/BxWqsutnZ+QsiHEcDkY7v+6WWdturuBiSal6XMOO1p1aJvwXdqLD5vhA==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-loader": { "version": "6.2.1", @@ -19083,7 +19113,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -19118,7 +19149,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -19268,8 +19300,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true, - "optional": true + "dev": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -20546,7 +20577,7 @@ "version": "4.5.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "dev": true + "devOptional": true }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -20648,6 +20679,13 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" }, + "vue-class-component": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz", + "integrity": "sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==", + "peer": true, + "requires": {} + }, "vue-cli-plugin-vuetify": { "version": "2.4.7", "resolved": "https://registry.npmmirror.com/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.4.7.tgz", @@ -20673,7 +20711,8 @@ "vue-demi": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz", - "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==" + "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==", + "requires": {} }, "vue-eslint-parser": { "version": "8.3.0", @@ -20813,6 +20852,12 @@ } } }, + "vue-property-decorator": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz", + "integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==", + "requires": {} + }, "vue-router": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz", @@ -20855,7 +20900,8 @@ "vuetify": { "version": "2.6.4", "resolved": "https://registry.npmmirror.com/vuetify/-/vuetify-2.6.4.tgz", - "integrity": "sha512-2wEzU/Gz39gQCxK93xoiWPKCHQUnyUKWd81wB7Q7hfYJWu5QOWQXYlF0X/BgUZzf8IOyHWKiSNEAfEe9OE3b4w==" + "integrity": "sha512-2wEzU/Gz39gQCxK93xoiWPKCHQUnyUKWd81wB7Q7hfYJWu5QOWQXYlF0X/BgUZzf8IOyHWKiSNEAfEe9OE3b4w==", + "requires": {} }, "vuetify-loader": { "version": "1.7.3", @@ -21194,7 +21240,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -21321,7 +21368,8 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true + "dev": true, + "requires": {} }, "y18n": { "version": "5.0.8", diff --git a/ErsatzTV/client-app/package.json b/ErsatzTV/client-app/package.json index c035c0c4f..625a2c517 100644 --- a/ErsatzTV/client-app/package.json +++ b/ErsatzTV/client-app/package.json @@ -14,6 +14,7 @@ "pinia": "^2.0.11", "roboto-fontface": "*", "vue": "^2.6.14", + "vue-property-decorator": "^9.1.2", "vue-router": "^3.2.0", "vuetify": "^2.6.0" }, diff --git a/ErsatzTV/client-app/src/App.vue b/ErsatzTV/client-app/src/App.vue index 4e86c8265..734b16e15 100644 --- a/ErsatzTV/client-app/src/App.vue +++ b/ErsatzTV/client-app/src/App.vue @@ -25,7 +25,6 @@ export default Vue.extend({