mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Merge branch 'master' into feature/helmchart
This commit is contained in:
commit
79c7dc0ad0
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@ -100,7 +100,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
if: steps.filter.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.event.ref, 'refs/tags/v')
|
if: steps.filter.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.event.ref, 'refs/tags/v')
|
||||||
with:
|
with:
|
||||||
context: ${{matrix.context}}
|
context: ${{matrix.context}}
|
||||||
|
304
DIAGRAMS.md
Normal file
304
DIAGRAMS.md
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
# Diagrams
|
||||||
|
|
||||||
|
# Project Structure
|
||||||
|
Kyoo is a monorepo that consists of several projects each in their own directory. Diagram below shows an outline of kyoo, projects, and artifacts.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 1
|
||||||
|
block:proj1:1
|
||||||
|
proj_name["Kyoo"]:1
|
||||||
|
end
|
||||||
|
block:proj2:1
|
||||||
|
dir_1["autosync/"]
|
||||||
|
dir_2["back/"]
|
||||||
|
dir_3["front/"]
|
||||||
|
dir_4["transcoder/"]
|
||||||
|
dir_5["scanner/"]
|
||||||
|
end
|
||||||
|
block:proj3:1
|
||||||
|
%% columns auto (default)
|
||||||
|
block:autosync_b:1
|
||||||
|
autosync_i1("kyoo_autosync")
|
||||||
|
end
|
||||||
|
block:back_b:1
|
||||||
|
columns 1
|
||||||
|
back_i1("kyoo_back")
|
||||||
|
back_i2("kyoo_migrations")
|
||||||
|
end
|
||||||
|
block:front_b:1
|
||||||
|
front_i1("kyoo_front")
|
||||||
|
end
|
||||||
|
block:transcoder_b:1
|
||||||
|
transcoder_i1("kyoo_transcoder")
|
||||||
|
end
|
||||||
|
block:scanner_b:1
|
||||||
|
columns 1
|
||||||
|
scanner_i1("kyoo_scanner")
|
||||||
|
scanner_i2("kyoo_scanner*")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
style proj_name fill:transparent,stroke-width:0px
|
||||||
|
style proj1 fill:#1168bd,stroke-width:0px
|
||||||
|
style proj2 fill:#1168bd,stroke-width:0px
|
||||||
|
style proj3 fill:#1168bd,stroke-width:0px
|
||||||
|
|
||||||
|
style dir_1 fill:#438dd5,stroke-width:0px
|
||||||
|
style dir_2 fill:#438dd5,stroke-width:0px
|
||||||
|
style dir_3 fill:#438dd5,stroke-width:0px
|
||||||
|
style dir_4 fill:#438dd5,stroke-width:0px
|
||||||
|
style dir_5 fill:#438dd5,stroke-width:0px
|
||||||
|
|
||||||
|
style autosync_b fill:#438dd5,stroke-width:0px
|
||||||
|
style back_b fill:#438dd5,stroke-width:0px
|
||||||
|
style front_b fill:#438dd5,stroke-width:0px
|
||||||
|
style transcoder_b fill:#438dd5,stroke-width:0px
|
||||||
|
style scanner_b fill:#438dd5,stroke-width:0px
|
||||||
|
|
||||||
|
style autosync_i1 fill:#85bbf0,stroke-width:0px
|
||||||
|
style back_i1 fill:#85bbf0,stroke-width:0px
|
||||||
|
style back_i2 fill:#85bbf0,stroke-width:0px
|
||||||
|
style front_i1 fill:#85bbf0,stroke-width:0px
|
||||||
|
style transcoder_i1 fill:#85bbf0,stroke-width:0px
|
||||||
|
style scanner_i1 fill:#85bbf0,stroke-width:0px
|
||||||
|
style scanner_i2 fill:#85bbf0,stroke-width:0px
|
||||||
|
```
|
||||||
|
|
||||||
|
# C4 Diagrams
|
||||||
|
Diagrams that focus on capturing project from a high level point of view. Context, Container, Component, Code
|
||||||
|
|
||||||
|
## Context
|
||||||
|
```mermaid
|
||||||
|
C4Context
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="2")
|
||||||
|
|
||||||
|
title Context Diagram for Kyoo
|
||||||
|
|
||||||
|
Person(user, "User")
|
||||||
|
System(kyoo, "Kyoo", "")
|
||||||
|
System_Ext(media, "MediaLibrary", "")
|
||||||
|
System_Ext(content, "ContentDatabase", "")
|
||||||
|
System_Ext(tracker, "ActivityTracker", "")
|
||||||
|
|
||||||
|
Rel(user, kyoo, "")
|
||||||
|
Rel(kyoo, content, "")
|
||||||
|
Rel(kyoo, media, "")
|
||||||
|
Rel(kyoo, tracker, "")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Container
|
||||||
|
Messaging is middleware. EnterpriseMessageBus is for any messaging handled between different projects.
|
||||||
|
```mermaid
|
||||||
|
C4Container
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3")
|
||||||
|
|
||||||
|
title Container diagram for Kyoo System
|
||||||
|
|
||||||
|
Person(user, "User")
|
||||||
|
System_Boundary(internal, "Kyoo") {
|
||||||
|
Container(frontend, "front/")
|
||||||
|
Container(backend, "back/")
|
||||||
|
Container(transcoder, "transcoder/")
|
||||||
|
Container(scanner, "scanner/")
|
||||||
|
ContainerQueue(emb, "emb", "", "EnterpriseMessageBus")
|
||||||
|
Container(autosync, "autosync/")
|
||||||
|
}
|
||||||
|
System_Boundary(external, "") {
|
||||||
|
System_Ext(content, "ContentDatabase", "")
|
||||||
|
}
|
||||||
|
System_Boundary(external2, "") {
|
||||||
|
System_Ext(tracker, "ActivityTracker", "")
|
||||||
|
}
|
||||||
|
System_Boundary(external3, "") {
|
||||||
|
System_Ext(media, "MediaLibrary", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(user, frontend, "")
|
||||||
|
Rel(user, backend, "")
|
||||||
|
Rel(frontend, backend, "")
|
||||||
|
Rel(backend, emb, "")
|
||||||
|
Rel(backend, media, "")
|
||||||
|
Rel(backend, transcoder, "")
|
||||||
|
Rel_Back(autosync, emb, "")
|
||||||
|
Rel(autosync, tracker, "")
|
||||||
|
Rel_Back(scanner, emb, "")
|
||||||
|
Rel(scanner, backend, "")
|
||||||
|
Rel(scanner, media, "")
|
||||||
|
Rel(scanner, content, "")
|
||||||
|
Rel(transcoder, media, "")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Component
|
||||||
|
### Autosync
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="2")
|
||||||
|
|
||||||
|
title Component Diagram for Autosync
|
||||||
|
|
||||||
|
Container_Boundary(autosync, "autosync") {
|
||||||
|
Component(autosync_c1, "kyoo_autosync", "python, python3.12", "")
|
||||||
|
}
|
||||||
|
Container_Boundary(emb, "emb") {
|
||||||
|
ComponentQueue(emb_q1, "autosync", "RabbitMQ, Queue", "")
|
||||||
|
ComponentQueue(emb_e1, "events.watched", "RabbitMQ, Exchange", "")
|
||||||
|
|
||||||
|
}
|
||||||
|
Container_Boundary(tracker, "ActivityTracker") {
|
||||||
|
Component_Ext(tracker_c1, "TrackerProvider", "API", "simkl")
|
||||||
|
}
|
||||||
|
Container_Boundary(backend, "back") {
|
||||||
|
Component(backend_c2, "kyoo_back", "C#, .NET 8.0", "API Backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(emb_e1, emb_q1, "bound")
|
||||||
|
Rel_Back(autosync_c1, emb_q1, "consumes")
|
||||||
|
Rel(backend_c2, emb_e1, "produces")
|
||||||
|
Rel(autosync_c1, tracker_c1, "updates")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Back
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="3")
|
||||||
|
|
||||||
|
title Component Diagram for Back
|
||||||
|
|
||||||
|
Person(user, "User")
|
||||||
|
|
||||||
|
Container_Boundary(frontend, "front") {
|
||||||
|
Component(frontend_c1, "kyoo_front", "typescript, node.js", "Static Content")
|
||||||
|
}
|
||||||
|
Container_Boundary(backend, "back") {
|
||||||
|
Component(backend_c1, "kyoo_migrations", "C#, .NET 8.0", "Postgres Migration")
|
||||||
|
ComponentDb(backend_db1, "backend", "Postgres", "user data and session state")
|
||||||
|
Component(backend_c3, "BackendMetadata", "Volume", "Persistent. Distributed Metadata")
|
||||||
|
ComponentDb(backend_db2, "search", "Meilisearch", "search resource")
|
||||||
|
Component(backend_c2, "kyoo_back", "C#, .NET 8.0", "API Backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(media, "MediaLibrary") {
|
||||||
|
Component_Ext(media_c1, "MediaShare", "Volume", "Read Only")
|
||||||
|
}
|
||||||
|
Container_Boundary(transcoder, "transcoder") {
|
||||||
|
Component(transcoder_c1, "kyoo_transcoder", "go, go", "Video Transcoder")
|
||||||
|
}
|
||||||
|
Container_Boundary(emb, "emb") {
|
||||||
|
ComponentQueue(emb_e1, "events.watched", "RabbitMQ, Exchange", "")
|
||||||
|
ComponentQueue(emb_q2, "scanner.rescan", "RabbitMQ, Queue", "")
|
||||||
|
ComponentQueue(emb_q1, "autosync", "RabbitMQ, Queue", "")
|
||||||
|
ComponentQueue(emb_e2, "events.resource", "RabbitMQ, Exchange", "unused")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(scanner, "scanner") {
|
||||||
|
Component(scanner_c1, "kyoo_scanner", "python, python3.12", "scanner")
|
||||||
|
Component(scanner_c2, "kyoo_scanner", "python, python3.12", "matcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(autosync, "autosync") {
|
||||||
|
Component(autosync_c1, "kyoo_autosync", "python, python3.12", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rel(user, backend_c2, "")
|
||||||
|
Rel(backend_c1, backend_db1, "")
|
||||||
|
Rel(backend_c2, backend_db1, "")
|
||||||
|
Rel(backend_c2, backend_db2, "")
|
||||||
|
Rel(backend_c2, media_c1, "")
|
||||||
|
Rel(backend_c2, transcoder_c1, "")
|
||||||
|
Rel(backend_c2, backend_c3, "")
|
||||||
|
Rel(backend_c2, emb_q2, "produces")
|
||||||
|
Rel(backend_c2, emb_e1, "produces")
|
||||||
|
Rel(backend_c2, emb_e2, "produces")
|
||||||
|
Rel(emb_e1, emb_q1, "bound")
|
||||||
|
Rel_Back(autosync_c1, emb_q1, "consumes")
|
||||||
|
Rel_Back(scanner_c1, emb_q2, "consumes")
|
||||||
|
Rel(scanner_c1, backend_c2, "")
|
||||||
|
Rel(scanner_c2, backend_c2, "")
|
||||||
|
Rel(frontend_c1, backend_c2, "")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Front
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="2")
|
||||||
|
|
||||||
|
title Component Diagram for Front
|
||||||
|
|
||||||
|
Person(user, "User")
|
||||||
|
Container_Boundary(frontend, "front") {
|
||||||
|
Component(frontend_c1, "kyoo_front", "typescript, node.js", "Static Content")
|
||||||
|
}
|
||||||
|
Container_Boundary(backend, "back") {
|
||||||
|
Component(backend_c2, "kyoo_back", "C#, .NET 8.0", "API Backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(frontend_c1, backend_c2, "ssr")
|
||||||
|
Rel(user, frontend_c1, "")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scanner
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="5", $c4BoundaryInRow="3")
|
||||||
|
|
||||||
|
title Component Diagram for Scanner
|
||||||
|
|
||||||
|
Container_Boundary(media, "MediaLibrary") {
|
||||||
|
Component_Ext(media_c1, "MediaShare", "Volume", "Read Only")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(content, "ContentDatabase") {
|
||||||
|
Component_Ext(content_c1, "ContentProvider", "API", "tmdb or tvdb")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(scanner, "scanner") {
|
||||||
|
Component(scanner_c2, "kyoo_scanner", "python, python3.12", "matcher")
|
||||||
|
ComponentQueue(scanner_q1, "scanner", "RabbitMQ, Queue", "")
|
||||||
|
Component(scanner_c1, "kyoo_scanner", "python, python3.12", "scanner")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(emb, "emb") {
|
||||||
|
ComponentQueue(emb_q2, "scanner.rescan", "RabbitMQ, Queue", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Container_Boundary(backend, "back") {
|
||||||
|
Component(backend_c2, "kyoo_back", "C#, .NET 8.0", "API Backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(scanner_c1, scanner_q1, "produces")
|
||||||
|
Rel(scanner_c1, media_c1, "watches")
|
||||||
|
Rel(scanner_c1, backend_c2, "Fetch existing scans")
|
||||||
|
Rel(scanner_c2, content_c1, "Fetch media data")
|
||||||
|
Rel(scanner_c2, backend_c2, "Pushes media data")
|
||||||
|
Rel_Back(scanner_c2, scanner_q1, "consumes")
|
||||||
|
Rel_Back(scanner_c1, emb_q2, "consumes")
|
||||||
|
Rel(backend_c2, emb_q2, "produces")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transcoder
|
||||||
|
```mermaid
|
||||||
|
C4Component
|
||||||
|
UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="2")
|
||||||
|
|
||||||
|
title Component Diagram for Transcoder
|
||||||
|
|
||||||
|
Container_Boundary(transcoder, "transcoder") {
|
||||||
|
Component(transcoder_c2, "TranscodeMetadata", "Volume", "Persistent. Distributed Metadata")
|
||||||
|
Component(transcoder_c1, "kyoo_transcoder", "go, go", "Video Transcoder")
|
||||||
|
Component(transcoder_c3, "TranscodeCache", "Volume", "Volatile. Local cache")
|
||||||
|
}
|
||||||
|
Container_Boundary(media, "MediaLibrary") {
|
||||||
|
Component_Ext(media_c1, "MediaShare", "Volume", "Read Only")
|
||||||
|
}
|
||||||
|
Container_Boundary(backend, "back") {
|
||||||
|
Component(backend_c2, "kyoo_back", "C#, .NET 8.0", "API Backend")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(transcoder_c1, media_c1, "mounts")
|
||||||
|
Rel(transcoder_c1, transcoder_c2, "")
|
||||||
|
Rel(transcoder_c1, transcoder_c3, "")
|
||||||
|
Rel(backend_c2, transcoder_c1, "")
|
||||||
|
```
|
@ -27,6 +27,7 @@ using Kyoo.Abstractions.Models.Permissions;
|
|||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api;
|
namespace Kyoo.Core.Api;
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ public abstract class TranscoderApi<T>(IRepository<T> repository) : CrudThumbsAp
|
|||||||
private async Task<string> _GetPath64(Identifier identifier)
|
private async Task<string> _GetPath64(Identifier identifier)
|
||||||
{
|
{
|
||||||
string path = await GetPath(identifier);
|
string path = await GetPath(identifier);
|
||||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(path));
|
return WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -131,7 +131,7 @@ services:
|
|||||||
profiles: ['qsv']
|
profiles: ['qsv']
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.0
|
image: traefik:v3.1
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
command:
|
command:
|
||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
|
@ -155,7 +155,7 @@ services:
|
|||||||
profiles: ['qsv']
|
profiles: ['qsv']
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.0
|
image: traefik:v3.1
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
command:
|
command:
|
||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
|
@ -130,7 +130,7 @@ services:
|
|||||||
profiles: ['qsv']
|
profiles: ['qsv']
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.0
|
image: traefik:v3.1
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
|
@ -259,7 +259,7 @@ export const EpisodeLine = ({
|
|||||||
width: percent(18),
|
width: percent(18),
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
}}
|
}}
|
||||||
{...css({ flexShrink: 0, m: ts(1) })}
|
{...css({ flexShrink: 0, m: ts(1), borderRadius: imageBorderRadius })}
|
||||||
>
|
>
|
||||||
{(watchedPercent || watchedStatus === WatchStatusV.Completed) && (
|
{(watchedPercent || watchedStatus === WatchStatusV.Completed) && (
|
||||||
<>
|
<>
|
||||||
|
@ -59,7 +59,15 @@
|
|||||||
"desc": "azalan"
|
"desc": "azalan"
|
||||||
},
|
},
|
||||||
"switchToGrid": "Izgara görünümüne geç",
|
"switchToGrid": "Izgara görünümüne geç",
|
||||||
"switchToList": "Liste görünümüne geç"
|
"switchToList": "Liste görünümüne geç",
|
||||||
|
"mediatypekey": {
|
||||||
|
"collection": "Koleksiyonlar",
|
||||||
|
"show": "Diziler",
|
||||||
|
"all": "Hepsi",
|
||||||
|
"movie": "Filmler"
|
||||||
|
},
|
||||||
|
"mediatype-tt": "İçeriğin Türü",
|
||||||
|
"mediatypelabel": "İçeriğin Türü"
|
||||||
},
|
},
|
||||||
"misc": {
|
"misc": {
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["config:recommended", ":disableRateLimiting", "regexManagers:biomeVersions"],
|
"extends": ["config:recommended", ":disableRateLimiting", "customManagers:biomeVersions"],
|
||||||
"minimumReleaseAge": "5 days",
|
"minimumReleaseAge": "5 days",
|
||||||
"ignorePaths": ["**/front/**"],
|
"ignorePaths": ["**/front/**"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
|
@ -24,9 +24,9 @@ func GetPath(c echo.Context) (string, string, error) {
|
|||||||
if key == "" {
|
if key == "" {
|
||||||
return "", "", echo.NewHTTPError(http.StatusBadRequest, "Missing resouce path.")
|
return "", "", echo.NewHTTPError(http.StatusBadRequest, "Missing resouce path.")
|
||||||
}
|
}
|
||||||
pathb, err := base64.StdEncoding.DecodeString(key)
|
pathb, err := base64.RawURLEncoding.DecodeString(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", echo.NewHTTPError(http.StatusBadRequest, "Invalid path. Should be base64 encoded.")
|
return "", "", echo.NewHTTPError(http.StatusBadRequest, "Invalid path. Should be base64url (without padding) encoded.")
|
||||||
}
|
}
|
||||||
path := filepath.Clean(string(pathb))
|
path := filepath.Clean(string(pathb))
|
||||||
if !filepath.IsAbs(path) {
|
if !filepath.IsAbs(path) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user