Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
3d84fe3
fixL rider ignore
CyAScott Oct 4, 2025
19923ae
refactor: project update to .net 10
CyAScott Oct 4, 2025
13d5bf9
feat: adding user controller
CyAScott Oct 4, 2025
085d89a
feat: adding swagger
CyAScott Oct 4, 2025
b5a1aa6
fix: model validation
CyAScott Oct 4, 2025
ea04638
feat: adding migration code
CyAScott Oct 4, 2025
cb71d8a
fix: default connection string
CyAScott Oct 4, 2025
5c870ce
feat: adding DB schema
CyAScott Oct 4, 2025
0c6d68d
fix: schema migration
CyAScott Oct 4, 2025
814e358
fix: adding sqLite files to the git ignore
CyAScott Oct 4, 2025
1c4f384
feat: media APIs
CyAScott Oct 4, 2025
7c74b48
feat: adding documentation
CyAScott Oct 4, 2025
e83e461
refactor: auth
CyAScott Oct 4, 2025
d5ac428
fix: login
CyAScott Oct 5, 2025
97405a2
refactor: media controller
CyAScott Oct 5, 2025
cfeb2ef
feat: adding media query
CyAScott Oct 5, 2025
37bcc02
refactor: media controller
CyAScott Oct 5, 2025
1f04c28
feat: adding thumbnail APIs
CyAScott Oct 5, 2025
f5b101d
feat: adding import support
CyAScott Oct 12, 2025
4779115
refactor: bootup code
CyAScott Oct 12, 2025
2478951
refactor: adding multiple SQL db support
CyAScott Oct 13, 2025
e28d7b0
feat: adding default user config
CyAScott Oct 14, 2025
4d53a0d
fix: import logic
CyAScott Oct 14, 2025
8d9598a
fix: issue with imports
CyAScott Oct 14, 2025
4b82664
refactor: code clean up
CyAScott Oct 14, 2025
c7c62ee
feat: adding frontend
CyAScott Oct 14, 2025
60cc439
refactor: rename username
CyAScott Oct 14, 2025
26d68d7
feat: adding update API
CyAScott Oct 14, 2025
3967e8c
refactor: using APIs
CyAScott Oct 14, 2025
4f04e14
fix: import
CyAScott Oct 14, 2025
a6f87da
fix: using index.html files
CyAScott Oct 14, 2025
c592e6f
fix: layout
CyAScott Oct 14, 2025
ba346ab
fix: blank names in the DB
CyAScott Oct 14, 2025
1a7c3c7
fix: name validation
CyAScott Oct 14, 2025
e7f8aca
fix: ffprobe issue
CyAScott Oct 14, 2025
2b01bdf
fix: nfo template
CyAScott Oct 14, 2025
55e1ae4
feat: adding system.text.json support
CyAScott Oct 14, 2025
301118c
fix: update
CyAScott Oct 14, 2025
9c320de
feat: adding log in page
CyAScott Oct 15, 2025
0a79902
fix: range issue for videos
CyAScott Oct 15, 2025
d28a891
fix: scroll issue
CyAScott Oct 15, 2025
6d2bd43
feat: adding link like cards
CyAScott Oct 15, 2025
e7fca1e
fix: navigation issue
CyAScott Oct 15, 2025
c4e0826
refactor: move css to styles
CyAScott Oct 15, 2025
68e8848
refactor: using query string parameters for state
CyAScott Oct 15, 2025
1ca6c60
feat: using session storage
CyAScott Oct 15, 2025
d1970e2
fix: navigation
CyAScott Oct 15, 2025
c9507f4
fix: scrolling issues for cast
CyAScott Oct 15, 2025
fe0afac
fix: scroll issues
CyAScott Oct 16, 2025
a819b95
fix: removing log lines
CyAScott Oct 16, 2025
6414179
feat: adding spinner for loading
CyAScott Oct 16, 2025
1dab313
refactor: spinner
CyAScott Oct 16, 2025
b298b26
refactor: login style
CyAScott Oct 16, 2025
c15ddad
refactor: style
CyAScott Oct 16, 2025
8a8dc81
fix: auth guard
CyAScott Oct 16, 2025
fce8b62
fix: login issue when running locally
CyAScott Oct 16, 2025
1e8e0f3
improved style
CyAScott Oct 16, 2025
00c373e
feat: adding fav icon
CyAScott Oct 16, 2025
3914101
fix: navigation issue
CyAScott Oct 16, 2025
ed13ecf
refactor: optimized for mobile
CyAScott Oct 16, 2025
457bf68
feat: mobile style
CyAScott Oct 17, 2025
962ac3b
feat: adding docker file
CyAScott Oct 17, 2025
5c7f8a4
feat: adding documentation
CyAScott Oct 17, 2025
ff1c868
fix: removing the ability to create more users without auth
CyAScott Oct 17, 2025
73da728
feat: adding import directory
CyAScott Oct 17, 2025
c8baed1
fix: issues with import
CyAScott Oct 17, 2025
d47e534
feat: thumbnail improvements
CyAScott Oct 17, 2025
cf0ec68
fix: location issue
CyAScott Oct 18, 2025
638cb66
fix: missing ffmpeg
CyAScott Oct 18, 2025
3c18b45
fix: issues after testing
CyAScott Oct 18, 2025
bd49cac
feat: user management
CyAScott Oct 18, 2025
58ea6ad
refactor: player to support other media types
CyAScott Oct 18, 2025
d8de098
feat: adding thumbnail APIs
CyAScott Oct 18, 2025
748a0e4
refactor: media editor
CyAScott Oct 18, 2025
be472f9
refactor: search
CyAScott Oct 18, 2025
ed9434b
refactor: search page
CyAScott Oct 18, 2025
1d0a5bd
adding support for imports for images
CyAScott Oct 18, 2025
3c6b1a1
feat: adding multi media support
CyAScott Oct 19, 2025
eb9bb37
feat: adding support for other meta filters
CyAScott Oct 20, 2025
fdaaad7
fix: editor
CyAScott Oct 20, 2025
c441ea5
fix: thumbnail URL
CyAScott Oct 20, 2025
4e09edd
fix: tooltip
CyAScott Oct 21, 2025
b4935e9
fix: copy paste issue
CyAScott Oct 21, 2025
e082610
refactor: search query
CyAScott Oct 25, 2025
6cc8ca6
fix: nav links
CyAScott Oct 25, 2025
bccbbd8
feat: better desktop browsing
CyAScott Oct 25, 2025
5726662
fix: volume controls
CyAScott Oct 25, 2025
0c4e00c
fix: play icon
CyAScott Oct 25, 2025
97eb05b
fix: sort modal button
CyAScott Oct 25, 2025
f6dcd28
refactor: state management
CyAScott Oct 25, 2025
8a8bcea
refactor: using route parameter
CyAScott Oct 25, 2025
9b0e63f
feat: adding thumbnail selector
CyAScott Oct 26, 2025
bb2bbad
refactor: icons
CyAScott Oct 26, 2025
3be01ad
feat: adding edit button on card
CyAScott Oct 26, 2025
3471535
feat: adding caching to improve performance
CyAScott Oct 26, 2025
bdc963b
fix: log
CyAScott Oct 26, 2025
c34bde1
fix: better thumbnail preview
CyAScott Oct 26, 2025
4b07697
fix: focus problem for thumbnails
CyAScott Oct 26, 2025
d896c4c
feat: adding typeahead
CyAScott Oct 26, 2025
51e78e0
feat: adding shortcut key to make thumbnail
CyAScott Oct 26, 2025
9bb97db
feat: adding background blur for picking thumbnails
CyAScott Oct 27, 2025
6d451a6
refactor: code clean up
CyAScott Mar 6, 2026
9bb0a1a
feat: adding integration tests
CyAScott Mar 6, 2026
84d8ac6
refactor: index test
CyAScott Mar 6, 2026
c40a25d
fix: root namespace
CyAScott Mar 6, 2026
bd04d85
feat: adding test to migrate schema on boot
CyAScott Mar 7, 2026
395608d
refactor: import logic
CyAScott Mar 7, 2026
02790bf
feat: adding test for import
CyAScott Mar 7, 2026
d5c6168
feat: added nfo tests
CyAScott Mar 7, 2026
f04a8bc
refactor: code clean up
CyAScott Mar 7, 2026
c8a232c
feat: adding test for imports
CyAScott Mar 7, 2026
069e0b6
refactor: import test
CyAScott Mar 8, 2026
7e2f6de
feat: adding meida controller tests
CyAScott Mar 9, 2026
29e2f9e
feat: adding unit tests for caching helper methods
CyAScott Mar 9, 2026
0d74a07
refactor: unit tests
CyAScott Mar 9, 2026
40cccad
feat: adding search unit tests
CyAScott Mar 9, 2026
1e0f430
feat: adding user controller tests
CyAScott Mar 9, 2026
e1442f4
refactor: code clean up
CyAScott Mar 9, 2026
b257138
feat: adding PR script
CyAScott Mar 9, 2026
f4a3864
fix: mysql tests
CyAScott Mar 9, 2026
1f2e23b
fix: sql server wait issue
CyAScott Mar 9, 2026
7fb9ffd
fix: sql server issue
CyAScott Mar 9, 2026
0cff183
fix: sql server issue
CyAScott Mar 9, 2026
21b09f8
fix: bad step for SQL server
CyAScott Mar 9, 2026
aed7b6e
fix: issues with tests
CyAScott Mar 9, 2026
e488754
fix: connection string issue
CyAScott Mar 9, 2026
a8a1e8d
fix: nullable path issue
CyAScott Mar 9, 2026
a275e38
fix: issues with test
CyAScott Mar 9, 2026
2afff88
fix: adding ffmpeg for tests
CyAScott Mar 9, 2026
8bfe599
refactor: simplified abstractions
CyAScott Mar 9, 2026
42db33c
feat: added deploy to docker hub
CyAScott Mar 10, 2026
2670030
refactor: readme
CyAScott Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
69 changes: 69 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Docker Build & Publish

on:
push:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write

steps:
- name: Checkout repository
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
with:
fetch-depth: 0

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@8fedf8a6f6cec22c9eaa66ee693f5b1f315dcbdd
with:
versionSpec: '6.0.5'
preferLatestVersion: true

- name: Determine version
id: gitversion
uses: gittools/actions/gitversion/execute@8fedf8a6f6cec22c9eaa66ee693f5b1f315dcbdd
with:
useConfigFile: true
configFilePath: GitVersion.yml

- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd

- name: Log into Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
with:
context: ./src
file: ./src/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/mediabrowser:latest
${{ secrets.DOCKERHUB_USERNAME }}/mediabrowser:${{ steps.gitversion.outputs.fullSemVer }}
build-args: |
ASSEMBLY_SEM_FILE_VER_ARG=${{ steps.gitversion.outputs.assemblySemFileVer }}
ASSEMBLY_SEM_VER_ARG=${{ steps.gitversion.outputs.assemblySemVer }}
FULL_SEM_VER_ARG=${{ steps.gitversion.outputs.fullSemVer }}
INFORMATIONAL_VERSION_ARG=${{ steps.gitversion.outputs.informationalVersion }}

- name: Tag repository with version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "${{ steps.gitversion.outputs.semVer }}" -m "Release ${{ steps.gitversion.outputs.semVer }}"
git push origin "${{ steps.gitversion.outputs.semVer }}"
gh release create "${{ steps.gitversion.outputs.semVer }}" --generate-notes
80 changes: 80 additions & 0 deletions .github/workflows/pull-requests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Pull Request

on:
pull_request:
branches: [ "main" ]

jobs:
test:
runs-on: ubuntu-latest

services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: mediabrowser
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: mediabrowser
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
env:
SA_PASSWORD: "Password123!"
ACCEPT_EULA: "Y"
MSSQL_PID: "Express"
ports:
- 1433:1433
options: >-
--health-cmd="exit 0"
--health-interval 10s
--health-timeout 5s
--health-retries 10

steps:
- name: Checkout repository
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602
with:
dotnet-version: 10.x

- name: Restore dependencies
run: dotnet restore src/MediaBrowser.Tests/MediaBrowser.Tests.csproj

- name: Build
run: dotnet build src/MediaBrowser.Tests/MediaBrowser.Tests.csproj --no-restore

- name: Install FFmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg

- name: Create index.html for tests
run: |
mkdir -p src/MediaBrowser/wwwroot
echo '<!DOCTYPE html><html lang="en"><body></body></html>' > src/MediaBrowser/wwwroot/index.html

- name: Test
run: dotnet test src/MediaBrowser.Tests/MediaBrowser.Tests.csproj --no-build --verbosity normal
env:
LOGGING__LOGLEVEL__DEFAULT: "Error"
DB__MYSQLCONNECTIONSTRING: "Server=127.0.0.1;Port=3306;Database=mediabrowser;User=root;Password=password;"
DB__POSTGRESCONNECTIONSTRING: "Host=127.0.0.1;Port=5432;Database=mediabrowser;Username=postgres;Password=password;"
DB__SQLSERVERCONNECTIONSTRING: "Server=127.0.0.1,1433;Database=mediabrowser;User Id=sa;Password=Password123!;TrustServerCertificate=True;"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -388,5 +388,8 @@ testem.log
/typings

# System Files
temp/*
.DS_Store
Thumbs.db
src/.idea/**
*.db
8 changes: 8 additions & 0 deletions GitVersion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mode: ContinuousDeployment
increment: Patch
next-version: 0.1.0
commit-message-incrementing: Enabled
branches: {}
ignore:
sha: []
minor-version-bump-message: '(^feat|\+semver:\s?(feature|minor))'
78 changes: 77 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,77 @@
# MediaBrowser
# MediaBrowser

# Schema Updates

After making changes, run these commands from the root of the repo:
```sh
export NAME=InitialCreate
cd src/MediaBrowser

export db__type=sqlite
dotnet ef migrations add $NAME --project ../Migrations/MediaBrowser.Sqlite/MediaBrowser.Sqlite.csproj
dotnet ef migrations script --output ../Migrations/MediaBrowser.Sqlite/schema.sql

export db__type=mySql
dotnet ef migrations add $NAME --project ../Migrations/MediaBrowser.MySql/MediaBrowser.MySql.csproj
dotnet ef migrations script --output ../Migrations/MediaBrowser.MySql/schema.sql --idempotent

export db__type=postgres
dotnet ef migrations add $NAME --project ../Migrations/MediaBrowser.Postgres/MediaBrowser.Postgres.csproj
dotnet ef migrations script --output ../Migrations/MediaBrowser.Postgres/schema.sql --idempotent

export db__type=sqlServer
dotnet ef migrations add $NAME --project ../Migrations/MediaBrowser.SqlServer/MediaBrowser.SqlServer.csproj
dotnet ef migrations script --output ../Migrations/MediaBrowser.SqlServer/schema.sql --idempotent
```

# Docker

To build the docker image run the following from the root of the repository:
```sh
export FULL_SEM_VER=1.0.0
cd src
docker build -t mediabrowser:$FULL_SEM_VER \
--build-arg FULL_SEM_VER_ARG=$FULL_SEM_VER \
--build-arg ASSEMBLY_SEM_VER_ARG=$FULL_SEM_VER.0 \
--build-arg ASSEMBLY_SEM_FILE_VER_ARG=$FULL_SEM_VER.0 \
--build-arg INFORMATIONAL_VERSION_ARG=$FULL_SEM_VER.0 \
.
```

To run the docker image run the following command:
```sh
docker run -d -p 5050:5050 --name mediabrowser-test mediabrowser
```

# Config

The best way to configure the service is through environment variables. Here are the variables:

* `cookies__secure` (boolean) - set to true when the cookie should only be set to "secure" (meaning https only cookie). The default is true.
* `db__migrateOnBoot` (boolean) - set to true to automatically update the DB schema on boot. The default is true.
* `db__type` (enum / string) - the type of DB: `mySql`, `postgres`, `sqlite`, and `sqlServer`. The default is `sqlite`.
* `db__mySqlConnectionString` (string) - required if `db__type` is `mySql`.
* `db__postgresConnectionString` (string) - required if `db__type` is `postgres`.
* `db__sqliteConnectionString` (string) - required if `db__type` is `sqlite`. The default is to use an in memory SQLite DB file which means if the process restarts then the DB contents are gone.
* `db__sqlServerConnectionString` (string) - required if `db__type` is `sqlServer`.
* `jwt__audience` (string) - this is the audience value for the authentication JWT. The default is `mediaBrowserAudience`.
* `jwt__expiryMinutes` (integer) - this is the number of minutes that the authentication JWT is set to expire and is also used to set the expiration for the cookie that contains the JWT. The default is `60`.
* `jwt__issuer` (string) - this is the issuer value for the authentication JWT. The default is `mediaBrowserIssuer`.
* `jwt__secretKey` (string) - it is important to set this value to a randomized long string (like a password). It is used for the JWT signing algorithm `HS256`. The default is `mediaBrowserSecretKey`.
* `initial__username` (string) - when the system boots it will create a user with this name (if the user doesn't already exists). The default is `admin`.
* `initial__password` (string) - the password to give for `initial__username`. The default is `admin`.
* `media__castDirectory` (string) - the directory path where cast member JPG files are stored. The file name should match name in the DB (i.e. `John Doe.jpg` should match `John Doe`). The default is `/cast`.
* `media__directorsDirectory` (string) - the directory path where director JPG files are stored. The file name should match name in the DB (i.e. `John Doe.jpg` should match `John Doe`). The default is `/directors`.
* `media__genresDirectory` (string) - the directory path where genre JPG files are stored. The file name should match name in the DB (i.e. `Horror.jpg` should match `Horror`). The default is `/genres`.
* `media__producersDirectory` (string) - the directory path where producer JPG files are stored. The file name should match name in the DB (i.e. `John Doe.jpg` should match `John Doe`). The default is `/producers`.
* `media__writersDirectory` (string) - the directory path where writer JPG files are stored. The file name should match name in the DB (i.e. `John Doe.jpg` should match `John Doe`). The default is `/writers`.
* `media__mediaDirectory` (string) - the directory path where media files are stored with their thumbnails and NFO files. The file name should be the MD5 hash (lowercase) of the media file followed by the file extension (i.e. `1a79a4d60de6718e8e5b326e338ae533.mp4`). The thumbnail file should be `1a79a4d60de6718e8e5b326e338ae533.jpg` and optionally the fanart thumbnail should be `1a79a4d60de6718e8e5b326e338ae533-fanart.jpg`. The [NFO file](https://en.wikipedia.org/wiki/.nfo) should be `1a79a4d60de6718e8e5b326e338ae533.nfo`. The default is `/media`.
* `media__syncOnBoot` (boolean) - when true this will scan the media directory for NFO files and import the data from those files into the DB. After the import the service will terminate.
* `stopAfterSync` (boolean) - when true and `media__syncOnBoot` is true then the application will shutdown after importing the files.
* `media__importExtensions` (dictionary) - a dictionary of file extensions and `order`, `mime`, and `ext`. The `order` is used to sort the dictionary so that using the mine for a file can be used to find a corresponding file extension (since it is possible to find multiple file extensions). The `mime` is the corresponding mime type for the file extension. The `ext` is the normalized file extension (i.e. `wave` is normalized to `wav`).
* `media__importDirectory` (string) - a directory where files can be dumped so that they can be imported into the `media__mediaDirectory`.

# TODO - Features

* Cast, director, genre, producer, and writer management.
* M3u playlist support.
88 changes: 88 additions & 0 deletions src/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Multi-stage Dockerfile for MediaBrowser
# Stage 1: Build the Angular frontend
FROM node:20-alpine AS frontend-build

WORKDIR /app/frontend

# Copy package.json and package-lock.json (if available)
COPY MediaBrowser.Frontend/package*.json ./

# Install dependencies
RUN npm ci

# Copy the frontend source code
COPY MediaBrowser.Frontend/ ./

# Create the wwwroot directory in the MediaBrowser project
RUN mkdir -p ../MediaBrowser/wwwroot

# Build the frontend
RUN npm run build

# Stage 2: Build the .NET application
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS dotnet-build

WORKDIR /app

# Copy solution file and project files
COPY MediaBrowser.slnx ./
COPY MediaBrowser/MediaBrowser.csproj ./MediaBrowser/
COPY MediaBrowser.Common/MediaBrowser.Common.csproj ./MediaBrowser.Common/
COPY Migrations/MediaBrowser.MySql/MediaBrowser.MySql.csproj ./Migrations/MediaBrowser.MySql/
COPY Migrations/MediaBrowser.Postgres/MediaBrowser.Postgres.csproj ./Migrations/MediaBrowser.Postgres/
COPY Migrations/MediaBrowser.Sqlite/MediaBrowser.Sqlite.csproj ./Migrations/MediaBrowser.Sqlite/
COPY Migrations/MediaBrowser.SqlServer/MediaBrowser.SqlServer.csproj ./Migrations/MediaBrowser.SqlServer/

# Restore dependencies
RUN dotnet restore MediaBrowser/MediaBrowser.csproj

# Copy the rest of the source code
COPY . ./

# Copy the built frontend from the previous stage
COPY --from=frontend-build /app/MediaBrowser/wwwroot ./MediaBrowser/wwwroot/

ARG FULL_SEM_VER_ARG
ARG ASSEMBLY_SEM_VER_ARG
ARG ASSEMBLY_SEM_FILE_VER_ARG
ARG INFORMATIONAL_VERSION_ARG

# Build the application
RUN dotnet publish MediaBrowser/MediaBrowser.csproj -c Release -o /app/publish \
/p:Version=$FULL_SEM_VER_ARG \
/p:AssemblyVersion=$ASSEMBLY_SEM_VER_ARG \
/p:FileVersion=$ASSEMBLY_SEM_FILE_VER_ARG \
/p:InformationalVersion=$INFORMATIONAL_VERSION_ARG

# Stage 3: Runtime image
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime

WORKDIR /app

# Install ffmpeg and ffprobe
RUN apt-get update && \
apt-get install -y ffmpeg && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Create a non-root user
RUN useradd -r -s /bin/false appuser

# Copy the published application
COPY --from=dotnet-build /app/publish ./

# Change ownership to the non-root user
RUN chown -R appuser:appuser /app

# Switch to the non-root user
USER appuser

# Expose the port the app runs on
EXPOSE 5050

# Set environment variables
ENV ASPNETCORE_URLS=http://+:5050
ENV ASPNETCORE_ENVIRONMENT=Production

# Start the application
ENTRYPOINT ["dotnet", "MediaBrowser.dll"]
20 changes: 20 additions & 0 deletions src/MediaBrowser.Common/DbConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace MediaBrowser;

[ExcludeFromCodeCoverage(Justification = "POCO")]
public class DbConfig(IConfiguration configuration)
{
public bool MigrateOnBoot { get; } = bool.Parse(configuration["db:migrateOnBoot"]!);
public string MySqlConnectionString { get; } = configuration["db:mySqlConnectionString"]!;
public string PostgresConnectionString { get; } = configuration["db:postgresConnectionString"]!;
public string SqliteConnectionString { get; } = configuration["db:sqliteConnectionString"]!;
public string SqlServerConnectionString { get; } = configuration["db:sqlServerConnectionString"]!;
public DbType DbType { get; } = Enum.Parse<DbType>(configuration["db:type"] ?? nameof(DbType.Sqlite), true);
}

public enum DbType
{
MySql,
Postgres,
Sqlite,
SqlServer
}
27 changes: 27 additions & 0 deletions src/MediaBrowser.Common/Media/Cast/Cast.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace MediaBrowser.Media.Cast;

[Table("media_cast"), Equatable, ExcludeFromCodeCoverage(Justification = "POCO")]
public partial class CastEntity
{
[Column("id"), IgnoreEquality, JsonPropertyName("id"), Key, Required,
DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public required int Id { get; set; }
[Column("media_id"), JsonPropertyName("mediaId"), MaxLength(36), Required]
public required Guid MediaId { get; init; }
[Column("cast_member"), JsonPropertyName("name"), Required, MaxLength(50)]
public required string Name { get; init; }
[JsonIgnore, IgnoreEquality]
public MediaEntity Media { get; init; } = null!;
}

public class CastEntityConfiguration : IEntityTypeConfiguration<CastEntity>
{
public void Configure(EntityTypeBuilder<CastEntity> builder)
{
builder.HasIndex(e => new { e.MediaId, e.Name }).IsUnique();

builder.HasOne(e => e.Media)
.WithMany(m => m.Cast)
.HasForeignKey(e => e.MediaId);
}
}
Loading
Loading