Merge pull request #42 from AnonymusRaccoon/readme

Updating the readme & adding a live server.
This commit is contained in:
Zoe Roux 2021-11-01 21:05:56 +01:00 committed by GitHub
commit 94bc5c840c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 284 additions and 2654 deletions

View File

@ -1,9 +1,8 @@
transcoder/build
Kyoo.WebApp/nodes_modules
Kyoo.WebApp/dist
Kyoo/bin
Kyoo/obj
Kyoo.Common/bin
Kyoo.Common/obj
Kyoo.CommonAPI/bin
Kyoo.CommonAPI/obj
.git
**/build
**/dist
src/Kyoo.WebApp/Front/nodes_modules
**/bin
**/obj
docs
tests

View File

@ -57,7 +57,8 @@ jobs:
-o:"anonymus-raccoon" \
-d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
-d:sonar.host.url="https://sonarcloud.io" \
-d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml"
-d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" \
-d:sonar.cs.vstest.reportsPaths="**/TestOutputResults.xml"
dotnet build --no-incremental '-p:SkipTranscoder=true;SkipWebApp=true'

View File

@ -8,16 +8,15 @@ on:
jobs:
Building:
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v1
- name: Install Doxygen
run: sudo apt-get install -y doxygen graphviz
- uses: nikeee/docfx-action@v1.0.0
name: Build Documentation
with:
args: docs/docfx.json
- name: Update the docs
run: |
rm -rf docs/*
doxygen Doxyfile
cd docs
cd _site
git config --global user.email "${GITHUB_ACTOR}@github.com";
git config --global user.name "${GITHUB_ACTOR}";
git init

View File

@ -26,7 +26,7 @@ jobs:
dotnet build --no-restore '-p:SkipWebApp=true;SkipTranscoder=true' -p:CopyLocalLockFileAssemblies=true
cp ./src/Kyoo.Abstractions/bin/Debug/net5.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net5.0/
- name: Test
run: dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover'
run: dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover' --logger "trx;LogFileName=TestOutputResults.xml"
env:
POSTGRES_HOST: postgres
POSTGRES_USERNAME: postgres
@ -34,6 +34,12 @@ jobs:
- name: Sanitize coverage output
if: ${{ always() }}
run: sed -i "s'$(pwd)'.'" tests/Kyoo.Tests/coverage.opencover.xml
- name: Upload tests results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: results.xml
path: "**/TestOutputResults.xml"
- name: Upload coverage report
if: ${{ always() }}
uses: actions/upload-artifact@v2

2
.gitignore vendored
View File

@ -10,8 +10,6 @@ libtranscoder.so
libtranscoder.dylib
transcoder.dll
docs/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##

View File

@ -4,22 +4,22 @@ WORKDIR /transcoder
COPY src/Kyoo.Transcoder .
RUN cmake . && make -j
FROM node:alpine as webapp
FROM node:14-alpine as webapp
WORKDIR /webapp
COPY src/Kyoo.WebApp .
WORKDIR /webapp/Front
RUN npm install
RUN npm run build -- --prod
COPY src/Kyoo.WebApp/Front .
RUN npm install -g @angular/cli
RUN yarn install --frozen-lockfile
RUN yarn run build --configuration production
FROM mcr.microsoft.com/dotnet/sdk:5.0 as builder
COPY . .
RUN dotnet publish -c Release -o /opt/kyoo '-p:SkipWebApp=true;SkipTranscoder=true'
RUN dotnet publish -c Release -o /opt/kyoo '-p:SkipWebApp=true;SkipTranscoder=true;CheckCodingStyle=false' src/Kyoo.Host.Console
FROM mcr.microsoft.com/dotnet/aspnet:5.0
RUN apt-get update && apt-get install -y libavutil-dev libavcodec-dev libavformat-dev
EXPOSE 5000
COPY --from=builder /opt/kyoo /usr/lib/kyoo
COPY --from=transcoder /transcoder/libtranscoder.so /usr/lib/kyoo
COPY --from=webapp /webapp/Front/dist/* /usr/lib/kyoo/wwwroot/
CMD ["/usr/lib/kyoo/Kyoo", "/var/lib/kyoo"]
COPY --from=webapp /webapp/dist/* /usr/lib/kyoo/wwwroot/
CMD ["/usr/lib/kyoo/Kyoo.Host.Console"]

2556
Doxyfile

File diff suppressed because it is too large Load Diff

119
README.md
View File

@ -1,20 +1,85 @@
# Kyoo
# <img width="24px" src="./icons/icon-256x256.png" alt="Kyoo"> Kyoo
<p>
<a href="https://github.com/AnonymusRaccoon/Kyoo/actions/workflows/build.yml"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Build?style=flat-square" alt="Build status"></a>
<a href="https://github.com/AnonymusRaccoon/Kyoo/actions/workflows/tests.yml"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Testing?label=tests&style=flat-square" alt="Tests status"></a>
<a href="https://github.com/users/AnonymusRaccoon/packages/container/package/kyoo"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Docker?label=docker&style=flat-square"/></a>
<a href="https://sonarcloud.io/dashboard?id=AnonymusRaccoon_Kyoo"><img src="https://img.shields.io/sonar/violations/AnonymusRaccoon_Kyoo?format=long&server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Analysis report"></a>
<a href="https://github.com/users/AnonymusRaccoon/packages/container/package/kyoo"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Docker?label=docker&style=flat-square" alt="Docker status"/></a>
<a href="https://sonarcloud.io/dashboard?id=AnonymusRaccoon_Kyoo"><img src="https://img.shields.io/sonar/tests/AnonymusRaccoon_Kyoo?compact_message&server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Test report"></a>
<a href="https://sonarcloud.io/dashboard?id=AnonymusRaccoon_Kyoo"><img src="https://img.shields.io/sonar/coverage/AnonymusRaccoon_Kyoo?server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Coverage"></a>
<a href="./LICENSE"><img src="https://img.shields.io/github/license/AnonymusRaccoon/Kyoo?style=flat-square" alt="License"></a>
</p>
Kyoo is an open-souce media browser which allow you to stream your movies, tv-shows or animes.
Kyoo is an open-source media browser which allow you to stream your movies, tv-shows or anime.
It is an alternative to Plex, Emby or Jellyfin.
Kyoo has been created from scratch, it is not a fork. Everything is and always will be free and open-source.
Feel free to open issues or pull requests, all contribution are welcomed.
## Getting started
- [Installation](https://docs.kyoo.moe/installation)
- [Api Documentation](https://demo.kyoo.moe/redoc)
- [Documentation (work in progress)](https://docs.kyoo.moe)
- [Contributing](./CONTRIBUTING.md)
## Features
- Manage your movies, tv-series & anime
- Download metadata automatically
- Transmux files to make them available on every platform (Transcode coming soon)
- Account system with a permission system
- Handle subtitles natively with embedded fonts (ass, subrip or vtt)
- Entirely free and works without internet (when metadata have already been downloaded)
- Works on Linux, Windows, Docker and probably Mac
- A powerful plugin system
## Live Demo
You can see a live demo with copyright-free movies here: [demo.kyoo.moe](https://demo.kyoo.moe).
Thanks to the [blender studio](https://www.blender.org/about/studio/) for providing open-source movies available for all.
The files available on the demo server can be downloaded [here](https://demo.kyoo.moe/files.zip). The demo server is simply created using the following docker compose:
```yml
version: "3.8"
services:
kyoo:
image: ghcr.io/anonymusraccoon/kyoo:master
restart: on-failure
environment:
- KYOO_DATADIR=/var/lib/kyoo
- BASICS__PUBLICURL=https://demo.kyoo.moe
- BASICS__MetadataInShow=false
- DATABASE__ENABLED=postgres
- DATABASE__CONFIGURATIONS__POSTGRES__SERVER=postgres
- DATABASE__CONFIGURATIONS__POSTGRES__USER ID=kyoo
- DATABASE__CONFIGURATIONS__POSTGRES__PASSWORD=kyooPassword
- TVDB__APIKEY=TheTvDbApiKey
- THE-MOVIEDB__APIKEY=TheMovieDbApiKey
ports:
- "80:5000"
depends_on:
- postgres
volumes:
- kyoo:/var/lib/kyoo
- video:/video
postgres:
image: "postgres"
restart: on-failure
environment:
- POSTGRES_USER=kyoo
- POSTGRES_PASSWORD=kyooPassword
volumes:
- db:/var/lib/postgresql/data
volumes:
kyoo:
video:
db:
```
## Screens
![Show](../screens/show.png?raw=true)
- - -
![Show Dropdown](../screens/show_dropdown.png?raw=true)
@ -28,49 +93,3 @@ Feel free to open issues or pull requests, all contribution are welcomed.
![Player](../screens/player.png?raw=true)
- - -
![Search](../screens/search.png?raw=true)
## Installation
On any system, you will need a running postgres server where Kyoo can connect. The connection's informations can be specified on the appsettings.json file, the default connection settings
use the user `kyoo` with the password `kyooPassword` on the server at `127.0.0.1:5432` (the default postgres url).
You can find nightly prebuild zipped version here:
- [Windows](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_windows.zip)
- [MacOS](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_macos.zip)
- [Linux](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_linux.zip)
For arch based, debian based or rpm compatible distributions, a package is automatically created and can be downloaded:
- [Arch](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_arch.zip)
- [Debian](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_debian.zip)
- [RPM](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_rpm.zip)
A docker file is also available and an up-to-date docker image is available at: `ghcr.io/anonymusraccoon/kyoo:master`. An example docker-compose image is available at the root of the repository. You can customise it to feet your needs and use a prebuild image or you can build it from source. To do that, clone the repository with the `--recurse` flag and run `docker-compose up `.
## Repositories
This is the main repository for Kyoo. Here, you will find all the server's code, the build process & the login page.
In the [Kyoo.WebApp](https://github.com/AnonymusRaccoon/Kyoo.WebApp) repository, you will find the code of the web app (created usint angular).
In the [Kyoo.Transcoder](https://github.com/AnonymusRaccoon/Kyoo.Transcoder) repository, you will find the C code that handle transcoding, transmuxing & subtitles/codecs extractions from media files.
Both of theses repository are needed to fully build Kyoo, when you clone this repository you should use the --recurse argument of git like so: ```git clone https://github.com/AnonymusRaccoon/Kyoo --recurse```.
## Development & Build
To develop for Kyoo, you will need the .NET 5.0 SDK and node & npm for the webapp. If you want to build the transcoder, you will also need a cmake compatible environement.
To run the development server, simply open the .sln file with your favorite C# IDE (like Jetbrain's Rider or Visual Studio) and press run or you can use the CLI and use the ```dotnet run -p Kyoo``` command.
To pack the application, run the ```dotnet publish -c Release -o <build_path> Kyoo``` command. This will build the server, the webapp and the transcoder and output files in the <build_path> directory.
If you want, you can build kyoo without it's transcoder by running ```dotnet build '-p:SkipTranscoder=true'```. You are now responsible of bringing a transcoder dynamic library at the build location. If you don't bring one, the transcoder won't be available.
You can also disable the webapp build by running ```dotnet build '-p:SkipWebApp=true'```. Those two options can be combined by running ```dotnet build '-p:SkipTranscoder=true;SkipWebApp=true'```
## Plugins
You can create plugins for Kyoo. To do that, create a C# Library project targetting the .Net Core 5 and install the [Kyoo.Common](https://www.nuget.org/packages/Kyoo.Common) package and implement the IPlugin interface.
You can create Tasks which can be started manually or automatically at startup or every X hours. You can also create metadata providers that will be used to get informations about shows, seasons, episodes & people.
You can find an exemple of metadata provider [here](https://github.com/AnonymusRaccoon/Kyoo.TheMovieDB).

2
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
_site/
obj

View File

@ -0,0 +1,20 @@
---
uid: build
title: Build
---
# Build
## Dependencies
To develop for Kyoo, you will need the **.NET 5.0 SDK**, **node** and **npm** for the webapp. If you want to build the transcoder, you will also need a cmake compatible environment.
## Building
To run the development server, simply open the .sln file with your favorite C# IDE (like Jetbrain's Rider or Visual Studio) and press run or you can use the CLI and use the ```dotnet run dotnet run -p src/Kyoo.Host.Console --launch-profile "Console"``` command.
To pack the application, run the ```dotnet publish -c Release -o <build_path> Kyoo.Host.Console``` command. This will build the server, the webapp and the transcoder and output files in the <build_path> directory.
## Skipping parts
If you want, you can build kyoo without it's transcoder by running ```dotnet build '-p:SkipTranscoder=true'```. You are now responsible of bringing a transcoder dynamic library at the build location. If you don't bring one, the transcoder won't be available.
You can also disable the webapp build by running ```dotnet build '-p:SkipWebApp=true'```. Those two options can be combined by running ```dotnet build '-p:SkipTranscoder=true;SkipWebApp=true'```

View File

@ -0,0 +1,8 @@
---
uid: plugin
title: Plugin
---
# Plugins
You can create plugins for Kyoo. To do that, create a C# Library project targeting the .Net Core 5 and install the [Kyoo.Abstraction](https://www.nuget.org/packages/Kyoo.Abstraction) package and implement the IPlugin interface.

View File

@ -0,0 +1,2 @@
- name: Build
href: build.md

65
docs/docfx.json Normal file
View File

@ -0,0 +1,65 @@
{
"metadata": [
{
"src": [
{
"files": [ "**.csproj" ],
"exclude": [ "**/bin/**", "**/obj/**" ],
"src": "../src"
}
],
"dest": "obj/api",
"disableGitFeatures": false,
"disableDefaultFilter": false
}
],
"build": {
"content": [
{
"files": [
"**/*.md",
"**/toc.yml"
]
},
{
"files": "CONTRIBUTING.md",
"src": "../",
"dest": "contributing"
},
{
"files": [ "**/*.yml" ],
"src": "obj/api",
"dest": "api"
}
],
"resource": [
{
"files": ["icons/**"],
"src": "../"
}
],
"dest": "_site",
"globalMetadata": {
"_appTitle": "Documentation - Kyoo",
"_appLogoPath": "icons/icon-32x32.png",
"_appFaviconPath": "icons/icon-256x256.ico",
"_gitContribute": {
"repo": "https://github.com/AnonymusRaccoon/Kyoo",
"branch": "master",
"apiSpecFolder": "docs"
},
"_enableSearch": true
},
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"template": [
"default"
],
"postProcessors": [],
"markdownEngineName": "markdig",
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false,
"disableGitFeatures": false
}
}

8
docs/index.md Normal file
View File

@ -0,0 +1,8 @@
---
uid: home
title: Home
---
# Welcome to Kyoo's Documentation
This is a work in progress, for now this documentation is mostly empty. The [Rest API](https://demo.kyoo.org/redoc) is the only part that is well documented.

21
docs/start/install.md Normal file
View File

@ -0,0 +1,21 @@
---
uid: install
title: Install
---
# Installing
To install kyoo, you can use one of the following installer depending on your platform:
- [Arch](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_arch.zip)
- [Debian](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_debian.zip)
- [RPM](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_rpm.zip)
- [Windows](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_windows_installer.zip)
If your system is not on the previous list, you can use one of the prebuild version:
- [Windows](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_windows.zip)
- [MacOS](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_macos.zip)
- [Linux](https://nightly.link/AnonymusRaccoon/Kyoo/workflows/build/master/kyoo_linux.zip)
# Docker
A docker file is also available and an up-to-date docker image is available at: `ghcr.io/anonymusraccoon/kyoo:master`. An example docker-compose image is available at the root of the repository.

2
docs/start/toc.yml Normal file
View File

@ -0,0 +1,2 @@
- name: Install
href: install.md

12
docs/toc.yml Normal file
View File

@ -0,0 +1,12 @@
- name: Home
href: index.md
- name: Getting Started
href: start/
- name: Contributing
href: contributing/
- name: References
href: obj/api/
- name: GitHub
href: https://github.com/AnonymusRaccoon/Kyoo
- name: REST API
href: https://demo.kyoo.org/redoc

View File

@ -99,8 +99,7 @@ namespace Kyoo.Abstractions
/// <returns>The public URl of kyoo (without a slash at the end)</returns>
public static Uri GetPublicUrl(this IConfiguration configuration)
{
string uri = configuration["basics:publicUrl"]?.TrimEnd('/') ?? "http://localhost:5000";
return new Uri(uri);
return new Uri(configuration["basics:publicUrl"] ?? "http://localhost:5000");
}
}
}

View File

@ -130,16 +130,16 @@ namespace Kyoo.Authentication
foreach (Client client in clients)
{
client.RedirectUris = client.RedirectUris
.Select(x => x.StartsWith("/") ? publicUrl + x : x)
.Select(x => x.StartsWith("/") ? publicUrl.ToString().TrimEnd('/') + x : x)
.ToArray();
}
services.AddIdentityServer(options =>
{
options.IssuerUri = publicUrl.ToString();
options.UserInteraction.LoginUrl = $"{publicUrl}/login";
options.UserInteraction.ErrorUrl = $"{publicUrl}/error";
options.UserInteraction.LogoutUrl = $"{publicUrl}/logout";
options.UserInteraction.LoginUrl = $"{publicUrl}login";
options.UserInteraction.ErrorUrl = $"{publicUrl}error";
options.UserInteraction.LogoutUrl = $"{publicUrl}logout";
})
.AddInMemoryIdentityResources(IdentityContext.GetIdentityResources())
.AddInMemoryApiScopes(IdentityContext.GetScopes())

View File

@ -205,11 +205,11 @@ namespace Kyoo.Core.Controllers
string path = await fs.GetExtraDirectory(resource)
?? resource switch
{
Season season => await GetExtraDirectory(season.Show),
Episode episode => await GetExtraDirectory(episode.Show),
Track track => await GetExtraDirectory(track.Episode),
IResource res => Combine(_options.CurrentValue.MetadataPath,
typeof(T).Name.ToLowerInvariant(), res.Slug),
IResource res => Combine(
_options.CurrentValue.MetadataPath,
typeof(T).Name.ToLowerInvariant(),
res.Slug
),
_ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLowerInvariant())
};
return await CreateDirectory(path);

View File

@ -20,6 +20,7 @@ using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Microsoft.AspNetCore.StaticFiles;
@ -114,7 +115,7 @@ namespace Kyoo.Core.Controllers
/// <param name="imageID">The ID of the image. See <see cref="Images"/> for values.</param>
/// <typeparam name="T">The type of the item</typeparam>
/// <returns>The path of the image for the given resource, <b>even if it does not exists</b></returns>
private async Task<string> _GetPrivateImagePath<T>(T item, int imageID)
private async Task<string> _GetPrivateImagePath<T>([NotNull] T item, int imageID)
{
if (item == null)
throw new ArgumentNullException(nameof(item));

View File

@ -1,6 +1,6 @@
{
"profiles": {
"Console Host": {
"Console": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
@ -8,7 +8,7 @@
},
"applicationUrl": "http://localhost:5000"
},
"Console Host (Browser)": {
"Console-Browser": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {

View File

@ -22,6 +22,9 @@ using Kyoo.Core;
namespace Kyoo.Host.WindowsTrait
{
/// <summary>
/// Windows's program entrypoint.
/// </summary>
public static class Program
{
/// <summary>

View File

@ -19,11 +19,13 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Abstractions.Models.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NJsonSchema;
using NJsonSchema.Generation.TypeMappers;
@ -50,6 +52,20 @@ namespace Kyoo.Swagger
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <summary>
/// The configuration instance used to retrieve the server's public url.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// Create a new <see cref="SwaggerModule"/>.
/// </summary>
/// <param name="configuration">The configuration instance used to retrieve the server's public url.</param>
public SwaggerModule(IConfiguration configuration)
{
_configuration = configuration;
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
@ -75,6 +91,11 @@ namespace Kyoo.Swagger
Name = "GPL-3.0-or-later",
Url = "https://github.com/AnonymusRaccoon/Kyoo/blob/master/LICENSE"
};
options.Servers.Add(new OpenApiServer
{
Url = _configuration.GetPublicUrl().ToString(),
Description = "The currently running kyoo's instance."
});
options.Info.ExtensionData ??= new Dictionary<string, object>();
options.Info.ExtensionData["x-logo"] = new

@ -1 +1 @@
Subproject commit f9741049100f90d09be88826b02c079b00b8b91b
Subproject commit 326c9aa9d35a265fbada2f6b268a2740a5beab8e

View File

@ -47,15 +47,15 @@
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Message Importance="high" Text="Restoring dependencies using 'yarn'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="yarn install" />
<Touch Files="$(NpmStamp)" AlwaysCreate="true" />
</Target>
<Target Name="RunWebpack" Condition="'$(SkipWebApp)' != 'true' And '$(Configuration)' == 'Release'">
<Message Importance="high" Text="Building the web app. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<Exec WorkingDirectory="$(SpaRoot)" Command="yarn run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="yarn run build:ssr -- --prod" Condition="'$(BuildServerSideRenderer)' == 'true'" />
</Target>
<Target Name="CopyFrontEndFiles" AfterTargets="RunWebpack" BeforeTargets="GetCopyToOutputDirectoryItems" Condition="'$(Configuration)' != 'Debug'">