From 7bba4fc0d8981ab58b97a3e0a306d13986e5db3a Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Fri, 16 Aug 2024 14:37:03 +0200 Subject: [PATCH 01/20] Translated using Weblate (Turkish) (#598) Currently translated at 100.0% (207 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/tr/ Co-authored-by: PUFF1N --- front/translations/tr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/translations/tr.json b/front/translations/tr.json index 1b2631ff..035737e5 100644 --- a/front/translations/tr.json +++ b/front/translations/tr.json @@ -237,7 +237,8 @@ "duration": "Uzunluk", "size": "Boyut", "novideo": "Video yok", - "nocontainer": "Geçersiz kapsayıcı" + "nocontainer": "Geçersiz kapsayıcı", + "external": "Harici" }, "admin": { "users": { From da21984290ff792ed9f0ee44b46fae50a00a44e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 18 Aug 2024 17:22:57 +0200 Subject: [PATCH 02/20] Update dotnet monorepo to v8.0.8 (#599) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- back/src/Kyoo.Authentication/Kyoo.Authentication.csproj | 2 +- back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj b/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj index dd497675..dbcbdef6 100644 --- a/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj +++ b/back/src/Kyoo.Authentication/Kyoo.Authentication.csproj @@ -1,7 +1,7 @@ - + diff --git a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj index 5c193da2..07b43a64 100644 --- a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ b/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj @@ -13,7 +13,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 809bfc49cdb80dc1cabd45dfc662cf55bf054b38 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 21 Aug 2024 22:46:42 +0200 Subject: [PATCH 03/20] Fix android app not starting due to icon --- front/packages/ui/src/navbar/icon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/packages/ui/src/navbar/icon.tsx b/front/packages/ui/src/navbar/icon.tsx index cb14ff5d..e0382322 100644 --- a/front/packages/ui/src/navbar/icon.tsx +++ b/front/packages/ui/src/navbar/icon.tsx @@ -19,7 +19,7 @@ */ import Svg, { type SvgProps, Path } from "react-native-svg"; -import { useYoshiki } from "yoshiki"; +import { useYoshiki } from "yoshiki/native"; /* export const KyooLogo = (props: SvgProps) => ( */ /* */ From 22dd3c65d22b4b918e690913594928ec646107f2 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 21 Aug 2024 22:46:59 +0200 Subject: [PATCH 04/20] Fix snackbar key warning --- front/packages/primitives/src/snackbar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/packages/primitives/src/snackbar.tsx b/front/packages/primitives/src/snackbar.tsx index b8680895..ad3075f1 100644 --- a/front/packages/primitives/src/snackbar.tsx +++ b/front/packages/primitives/src/snackbar.tsx @@ -61,7 +61,8 @@ export const SnackbarProvider = ({ children }: { children: ReactElement | ReactE return; } - addPortal("snackbar", ); + const { key, ...props } = top; + addPortal("snackbar", ); timeout.current = setTimeout(() => { removePortal("snackbar"); updatePortal(); From b49eb3bffa85907d7693efcc00e4f5366cbb58df Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 21 Aug 2024 22:48:44 +0200 Subject: [PATCH 05/20] Add N/A note on packets_nbr > frames_nbr files --- transcoder/src/keyframes.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transcoder/src/keyframes.go b/transcoder/src/keyframes.go index 2b3ade5a..6e16d6a6 100644 --- a/transcoder/src/keyframes.go +++ b/transcoder/src/keyframes.go @@ -191,6 +191,8 @@ func getVideoKeyframes(path string, video_idx uint32, kf *Keyframe) error { pts, flags := x[0], x[1] // true if there is no keyframes (e.g. in a file w/o video track) + // can also happen if a video has more packets than frames (so the last packet + // is emtpy and has a N/A pts) if pts == "N/A" { break } From e7c9eca524f0cb8b9467281f292acd235e74c30a Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 21 Aug 2024 23:28:06 +0200 Subject: [PATCH 06/20] Add schema handling via env var --- transcoder/.env.example | 5 ++++- transcoder/src/metadata.go | 15 +++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/transcoder/.env.example b/transcoder/.env.example index cfe34e02..b8772e54 100644 --- a/transcoder/.env.example +++ b/transcoder/.env.example @@ -26,4 +26,7 @@ POSTGRES_PASSWORD= POSTGRES_DB= POSTGRES_SERVER= POSTGRES_PORT=5432 -# (the schema "gocoder" will be used) +# Default is gocoder, you can specify "disabled" to use the default search_path of the user. +# If this is not "disabled", the schema will be created (if it does not exists) and +# the search_path of the user will be ignored (only the schema specified will be used). +POSTGRES_SCHEMA=gocoder diff --git a/transcoder/src/metadata.go b/transcoder/src/metadata.go index c1fd2edf..a382d8d3 100644 --- a/transcoder/src/metadata.go +++ b/transcoder/src/metadata.go @@ -24,21 +24,28 @@ type MetadataService struct { func NewMetadataService() (*MetadataService, error) { con := fmt.Sprintf( - "postgresql://%v:%v@%v:%v/%v?application_name=gocoder&search_path=gocoder&sslmode=disable", + "postgresql://%v:%v@%v:%v/%v?application_name=gocoder&sslmode=disable", url.QueryEscape(os.Getenv("POSTGRES_USER")), url.QueryEscape(os.Getenv("POSTGRES_PASSWORD")), url.QueryEscape(os.Getenv("POSTGRES_SERVER")), url.QueryEscape(os.Getenv("POSTGRES_PORT")), url.QueryEscape(os.Getenv("POSTGRES_DB")), ) + schema := GetEnvOr("POSTGRES_SCHEMA", "gocoder") + if schema != "disabled" { + con = fmt.Sprintf("%s&search_path=%s", con, url.QueryEscape(schema)) + } db, err := sql.Open("postgres", con) if err != nil { + fmt.Printf("Could not connect to database, check your env variables!") return nil, err } - _, err = db.Exec("create schema if not exists gocoder") - if err != nil { - return nil, err + if schema != "disabled" { + _, err = db.Exec(fmt.Sprintf("create schema if not exists %s", schema)) + if err != nil { + return nil, err + } } driver, err := postgres.WithInstance(db, &postgres.Config{}) From 6d702e3786cf4d12e7f35906547ab213c228f6ce Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 22 Aug 2024 00:03:08 +0200 Subject: [PATCH 07/20] Update transcoder dockerfiles --- transcoder/Dockerfile | 6 +++--- transcoder/Dockerfile.dev | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/transcoder/Dockerfile b/transcoder/Dockerfile index 11cb9653..78419df8 100644 --- a/transcoder/Dockerfile +++ b/transcoder/Dockerfile @@ -1,9 +1,9 @@ # FROM golang:1.22 as build -FROM debian:trixie-slim as build +FROM debian:trixie-slim AS build # those were copied from https://github.com/docker-library/golang/blob/master/Dockerfile-linux.template ENV GOTOOLCHAIN=local -ENV GOPATH /go -ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH +ENV GOPATH=/go +ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ diff --git a/transcoder/Dockerfile.dev b/transcoder/Dockerfile.dev index 70671cc9..16dd9d1d 100644 --- a/transcoder/Dockerfile.dev +++ b/transcoder/Dockerfile.dev @@ -5,8 +5,8 @@ FROM debian:trixie-slim # those were copied from https://github.com/docker-library/golang/blob/master/Dockerfile-linux.template ENV GOTOOLCHAIN=local -ENV GOPATH /go -ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH +ENV GOPATH=/go +ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ From ae2cdab9f1c28937e5e197fb7ae9c67af5974327 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 22 Aug 2024 00:03:19 +0200 Subject: [PATCH 08/20] Use genpts --- transcoder/src/keyframes.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transcoder/src/keyframes.go b/transcoder/src/keyframes.go index 6e16d6a6..0f859482 100644 --- a/transcoder/src/keyframes.go +++ b/transcoder/src/keyframes.go @@ -157,6 +157,8 @@ func getVideoKeyframes(path string, video_idx uint32, kf *Keyframe) error { "-loglevel", "error", "-select_streams", fmt.Sprintf("V:%d", video_idx), "-show_entries", "packet=pts_time,flags", + // some avi files don't have pts, we use this to ask ffmpeg to generate them (it uses the dts under the hood) + "-fflags", "+genpts", "-of", "csv=print_section=0", path, ) From da29d9753d408e2c6852c49a4071bcf5dca8fcd7 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 22 Aug 2024 00:37:05 +0200 Subject: [PATCH 09/20] Use genpts as an input param for transcodes --- transcoder/src/stream.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/transcoder/src/stream.go b/transcoder/src/stream.go index 3692e3ec..027180e1 100644 --- a/transcoder/src/stream.go +++ b/transcoder/src/stream.go @@ -238,6 +238,11 @@ func (ts *Stream) run(start int32) error { ) } args = append(args, + // some avi files are missing pts, using this flag makes ffmpeg use dts as pts and prevents an error with + // -c:v copy. Only issue: pts is sometime wrong (+1fps than expected) and this leads to some clients refusing + // to play the file (they just switch back to the previous quality). + // since this is better than errorring or not supporting transmux at all, i'll keep it here for now. + "-fflags", "+genpts", "-i", ts.file.Info.Path, // this makes behaviors consistent between soft and hardware decodes. // this also means that after a -ss 50, the output video will start at 50s From 5c8260bcbdade085d88577a4e8974cad13badfe9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:47:36 +0000 Subject: [PATCH 10/20] Update dotnet monorepo to v8.0.8 (#600) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- back/.config/dotnet-tools.json | 2 +- back/src/Kyoo.Core/Kyoo.Core.csproj | 2 +- back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/back/.config/dotnet-tools.json b/back/.config/dotnet-tools.json index 7f93f7a0..b3b46e0e 100644 --- a/back/.config/dotnet-tools.json +++ b/back/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.7", + "version": "8.0.8", "commands": [ "dotnet-ef" ] diff --git a/back/src/Kyoo.Core/Kyoo.Core.csproj b/back/src/Kyoo.Core/Kyoo.Core.csproj index ebaaccef..364a67ef 100644 --- a/back/src/Kyoo.Core/Kyoo.Core.csproj +++ b/back/src/Kyoo.Core/Kyoo.Core.csproj @@ -23,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj index 07b43a64..9ccead3d 100644 --- a/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ b/back/src/Kyoo.Postgresql/Kyoo.Postgresql.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 16f01257e35cb5c81b5004a95bc9eea2f18fb6d2 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 27 Aug 2024 02:41:57 +0200 Subject: [PATCH 11/20] Use last audio pts time for duration --- transcoder/src/keyframes.go | 55 +++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/transcoder/src/keyframes.go b/transcoder/src/keyframes.go index 0f859482..563b9ad9 100644 --- a/transcoder/src/keyframes.go +++ b/transcoder/src/keyframes.go @@ -2,6 +2,7 @@ package src import ( "bufio" + "errors" "fmt" "log" "os/exec" @@ -12,7 +13,7 @@ import ( "github.com/lib/pq" ) -const KeyframeVersion = 1 +const KeyframeVersion = 2 type Keyframe struct { Keyframes []float64 @@ -237,8 +238,58 @@ func getVideoKeyframes(path string, video_idx uint32, kf *Keyframe) error { // we can pretty much cut audio at any point so no need to get specific frames, just cut every 4s func getAudioKeyframes(info *MediaInfo, audio_idx uint32, kf *Keyframe) error { + defer printExecTime("ffprobe keyframe analysis for %s audio n%d", info.Path, audio_idx)() + // Format's duration CAN be different than audio's duration. To make sure we do not + // miss a segment or make one more, we need to check the audio's duration. + // + // Some formats DO NOT contain this metadata, we need to manually fetch it from the packets. + // + // We could use the same command to retrieve all packets and know when we can cut PRECISELY + // but since packets always contain only a few ms we don't need this precision. + cmd := exec.Command( + "ffprobe", + "-select_streams", fmt.Sprintf("a:%d", audio_idx), + "-show_entries", "packet=pts_time", + // some avi files don't have pts, we use this to ask ffmpeg to generate them (it uses the dts under the hood) + "-fflags", "+genpts", + // We use a read_interval LARGER than the file (at least we estimate) + // This allows us to only decode the LAST packets + "-read_intervals", fmt.Sprintf("%f", info.Duration+10_000), + "-of", "csv=print_section=0", + info.Path, + ) + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + err = cmd.Start() + if err != nil { + return err + } + + scanner := bufio.NewScanner(stdout) + var duration float64 + for scanner.Scan() { + pts := scanner.Text() + if pts == "" || pts == "N/A" { + continue + } + + duration, err = strconv.ParseFloat(pts, 64) + if err != nil { + return err + } + + } + if err := scanner.Err(); err != nil { + return err + } + if duration <= 0 { + return errors.New("could not find audio's duration") + } + dummyKeyframeDuration := float64(4) - segmentCount := int((float64(info.Duration) / dummyKeyframeDuration) + 1) + segmentCount := int((duration / dummyKeyframeDuration) + 1) kf.Keyframes = make([]float64, segmentCount) for segmentIndex := 0; segmentIndex < segmentCount; segmentIndex += 1 { kf.Keyframes[segmentIndex] = float64(segmentIndex) * dummyKeyframeDuration From 45d17fdd2c037d519652e67dd9186254687efe02 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 27 Aug 2024 15:36:11 +0200 Subject: [PATCH 12/20] Eagerly generate audio keyframes --- transcoder/src/info.go | 4 ++-- transcoder/src/keyframes.go | 41 +++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/transcoder/src/info.go b/transcoder/src/info.go index 2a648cd4..2caf1536 100644 --- a/transcoder/src/info.go +++ b/transcoder/src/info.go @@ -36,7 +36,7 @@ type MediaInfo struct { /// The file size of the video file. Size int64 `json:"size"` /// The length of the media in seconds. - Duration float32 `json:"duration"` + Duration float64 `json:"duration"` /// The container of the video file of this episode. Container *string `json:"container"` /// Version of the metadata. This can be used to invalidate older metadata from db if the extraction code has changed. @@ -238,7 +238,7 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) { // Remove leading . Extension: filepath.Ext(path)[1:], Size: ParseInt64(mi.Format.Size), - Duration: float32(mi.Format.DurationSeconds), + Duration: mi.Format.DurationSeconds, Container: OrNull(mi.Format.FormatName), Versions: Versions{ Info: InfoVersion, diff --git a/transcoder/src/keyframes.go b/transcoder/src/keyframes.go index 563b9ad9..65c3910a 100644 --- a/transcoder/src/keyframes.go +++ b/transcoder/src/keyframes.go @@ -13,7 +13,7 @@ import ( "github.com/lib/pq" ) -const KeyframeVersion = 2 +const KeyframeVersion = 1 type Keyframe struct { Keyframes []float64 @@ -123,7 +123,7 @@ func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx uint32 } if err != nil { - log.Printf("Couldn't retrive keyframes for %s %s %d: %v", info.Path, table, idx, err) + log.Printf("Couldn't retrieve keyframes for %s %s %d: %v", info.Path, table, idx, err) return } @@ -236,13 +236,32 @@ func getVideoKeyframes(path string, video_idx uint32, kf *Keyframe) error { return nil } +const DummyKeyframeDuration = float64(4) + // we can pretty much cut audio at any point so no need to get specific frames, just cut every 4s func getAudioKeyframes(info *MediaInfo, audio_idx uint32, kf *Keyframe) error { defer printExecTime("ffprobe keyframe analysis for %s audio n%d", info.Path, audio_idx)() // Format's duration CAN be different than audio's duration. To make sure we do not // miss a segment or make one more, we need to check the audio's duration. // - // Some formats DO NOT contain this metadata, we need to manually fetch it from the packets. + // Since fetching the duration requires reading packets and is SLOW, we start by generating + // keyframes until a reasonably safe point of the file (if the format has a 20min duration, audio + // probably has a close duration). + // You can read why duration retrieval is slow on the comment below. + safe_duration := info.Duration - 20 + segment_count := int((safe_duration / DummyKeyframeDuration) + 1) + if segment_count > 0 { + kf.Keyframes = make([]float64, segment_count) + for i := 0; i < segment_count; i += 1 { + kf.Keyframes[i] = float64(i) * DummyKeyframeDuration + } + kf.info.ready.Done() + } else { + segment_count = 0 + } + + // Some formats DO NOT contain a duration metadata, we need to manually fetch it + // from the packets. // // We could use the same command to retrieve all packets and know when we can cut PRECISELY // but since packets always contain only a few ms we don't need this precision. @@ -288,14 +307,18 @@ func getAudioKeyframes(info *MediaInfo, audio_idx uint32, kf *Keyframe) error { return errors.New("could not find audio's duration") } - dummyKeyframeDuration := float64(4) - segmentCount := int((duration / dummyKeyframeDuration) + 1) - kf.Keyframes = make([]float64, segmentCount) - for segmentIndex := 0; segmentIndex < segmentCount; segmentIndex += 1 { - kf.Keyframes[segmentIndex] = float64(segmentIndex) * dummyKeyframeDuration + new_seg_count := int((duration / DummyKeyframeDuration) + 1) + if new_seg_count > segment_count { + new_segments := make([]float64, new_seg_count-segment_count) + for i := segment_count; i < new_seg_count; i += 1 { + new_segments[i-segment_count] = float64(i) * DummyKeyframeDuration + } + kf.add(new_segments) + if segment_count == 0 { + kf.info.ready.Done() + } } kf.IsDone = true - kf.info.ready.Done() return nil } From b50501907ce51beff28f915e633c8b353d564e6f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 27 Aug 2024 16:03:43 +0200 Subject: [PATCH 13/20] Fix dual keyframe extractions --- transcoder/src/info.go | 4 ++++ transcoder/src/keyframes.go | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/transcoder/src/info.go b/transcoder/src/info.go index 2caf1536..a1e167fb 100644 --- a/transcoder/src/info.go +++ b/transcoder/src/info.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" "golang.org/x/text/language" @@ -55,6 +56,9 @@ type MediaInfo struct { Fonts []string `json:"fonts"` /// The list of chapters. See Chapter for more information. Chapters []Chapter `json:"chapters"` + + /// lock used to read/set keyframes of video/audio + lock sync.Mutex } type Video struct { diff --git a/transcoder/src/keyframes.go b/transcoder/src/keyframes.go index 65c3910a..c13e1c15 100644 --- a/transcoder/src/keyframes.go +++ b/transcoder/src/keyframes.go @@ -89,11 +89,17 @@ type KeyframeKey struct { } func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx uint32) (*Keyframe, error) { + info.lock.Lock() + var ret *Keyframe if isVideo && info.Videos[idx].Keyframes != nil { - return info.Videos[idx].Keyframes, nil + ret = info.Videos[idx].Keyframes } if !isVideo && info.Audios[idx].Keyframes != nil { - return info.Audios[idx].Keyframes, nil + ret = info.Audios[idx].Keyframes + } + info.lock.Unlock() + if ret != nil { + return ret, nil } get_running, set := s.keyframeLock.Start(KeyframeKey{ @@ -111,6 +117,14 @@ func (s *MetadataService) GetKeyframes(info *MediaInfo, isVideo bool, idx uint32 } kf.info.ready.Add(1) + info.lock.Lock() + if isVideo { + info.Videos[idx].Keyframes = kf + } else { + info.Audios[idx].Keyframes = kf + } + info.lock.Unlock() + go func() { var table string var err error From a62cc600dc70ea93c03e83bb5528774866d31101 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 29 Aug 2024 12:59:39 +0200 Subject: [PATCH 14/20] Add new API DB schema --- api/README.md | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 api/README.md diff --git a/api/README.md b/api/README.md new file mode 100644 index 00000000..256600c5 --- /dev/null +++ b/api/README.md @@ -0,0 +1,177 @@ +# Kyoo API + +## Database schema + +```mermaid +erDiagram + shows { + guid id PK + kind kind "serie|movie" + string(128) slug UK + genre[] genres + int rating "From 0 to 100" + status status "NN" + datetime added_date + date start_air + date end_air "null for movies" + datetime next_refresh + jsonb external_id + guid studio_id FK + string original_language + } + show_translations { + guid id PK, FK + string language PK + string name "NN" + string tagline + string[] aliases + string description + string[] tags + string trailerUrl + jsonb poster + jsonb banner + jsonb logo + jsonb thumbnail + } + shows ||--|{ show_translations : has + shows |o--|| entries : has + + entries { + guid id PK + string(256) slug UK + guid show_id FK, UK + %% Order is absolute number. + uint order "NN" + uint season_number UK + uint episode_number UK "NN" + type type "episode|movie|special|extra" + date air_date + uint runtime + jsonb thumbnail + datetime next_refresh + jsonb external_id + } + entry_translations { + guid id PK, FK + string language PK + string name + string description + } + entries ||--|{ entry_translations : has + + video { + guid id PK + string path "NN" + uint rendering "dedup for duplicates part1/2" + uint part + uint version "max version is preferred rendering" + } + video }|--|{ entries : for + + collections { + guid id PK + string(256) slug UK + datetime added_date + datetime next_refresh + } + + collection_translations { + guid id PK, FK + string language PK + string name "NN" + jsonb poster + jsonb thumbnail + } + collections ||--|{ collection_translations : has + collections |o--|{ shows : has + + seasons { + guid id PK + string(256) slug UK + guid show_id FK + uint season_number "NN" + datetime added_date + date start_air + date end_air + datetime next_refresh + jsonb external_id + } + + season_translations { + guid id PK,FK + string language PK + string name + string description + jsonb poster + jsonb banner + jsonb logo + jsonb thumbnail + } + seasons ||--|{ season_translations : has + seasons ||--o{ entries : has + shows ||--|{ seasons : has + + watched_shows { + guid show_id PK, FK + guid user_id PK, FK + status status "completed|watching|droped|planned" + uint seen_entry_count "NN" + } + shows ||--|{ watched_shows : has + + watched_entries { + guid entry_id PK, FK + guid user_id PK, FK + uint time "in seconds, null of finished" + uint progress "NN, from 0 to 100" + datetime played_date + } + entries ||--|{ watched_entries : has + + roles { + guid show_id PK, FK + guid staff_id PK, FK + uint order + type type "actor|director|writer|producer|music|other" + jsonb character_image + } + + role_translations { + string language PK + string character_name + } + roles||--o{ role_translations : has + shows ||--|{ roles : has + + staff { + guid id PK + string(256) slug UK + jsonb image + datetime next_refresh + jsonb external_id + } + + staff_translations { + guid id PK,FK + string language PK + string name "NN" + } + staff ||--|{ staff_translations : has + staff ||--|{ roles : has + + studios { + guid id PK + string(128) slug UK + jsonb logo + datetime next_refresh + jsonb external_id + } + + studio_translations { + guid id PK,FK + string language PK + string name + } + studios ||--|{ studio_translations : has + shows ||--|{ studios : has +``` From 4b06a46356bb93690c554d224480639ef9acf70c Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 29 Aug 2024 15:49:45 +0200 Subject: [PATCH 15/20] Add entries/video many to many note --- api/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/README.md b/api/README.md index 256600c5..942cba07 100644 --- a/api/README.md +++ b/api/README.md @@ -2,6 +2,8 @@ ## Database schema +The many-to-many relation between entries (episodes/movies) & videos is NOT a mistake. Some video files can contain multiples episodes (like `MyShow 2&3.mvk`). One video file can also contain only a portion of an episode (like `MyShow 2 Part 1.mkv`) + ```mermaid erDiagram shows { From c15a849bc63679ce9dbd291b4ad5d0fb916b8c8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:12:14 +0000 Subject: [PATCH 16/20] Update module golang.org/x/text to v0.18.0 --- transcoder/go.mod | 2 +- transcoder/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/transcoder/go.mod b/transcoder/go.mod index b3fa9ccf..f9ef619c 100644 --- a/transcoder/go.mod +++ b/transcoder/go.mod @@ -25,6 +25,6 @@ require ( golang.org/x/image v0.19.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.17.0 + golang.org/x/text v0.18.0 golang.org/x/time v0.6.0 // indirect ) diff --git a/transcoder/go.sum b/transcoder/go.sum index b960f1c8..68791155 100644 --- a/transcoder/go.sum +++ b/transcoder/go.sum @@ -76,8 +76,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= From e260414daba881f7b6c6df2362f7b1302e1b1780 Mon Sep 17 00:00:00 2001 From: MattiaPell Date: Fri, 6 Sep 2024 12:03:51 +0000 Subject: [PATCH 17/20] Translated using Weblate (Italian) Currently translated at 30.4% (63 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/it/ --- front/translations/it.json | 113 ++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/front/translations/it.json b/front/translations/it.json index fb3df3e4..ec65986c 100644 --- a/front/translations/it.json +++ b/front/translations/it.json @@ -1,54 +1,54 @@ { "home": { - "recommended": "", + "recommended": "Raccomandati", "news": "", - "watchlist": "", + "watchlist": "Continua a guardare", "info": "", - "none": "", + "none": "Nessun episodio", "watchlistLogin": "", - "refreshMetadata": "", + "refreshMetadata": "Aggiorna metadata", "episodeMore": { - "goToShow": "", + "goToShow": "Vai allo show", "download": "", "mediainfo": "" } }, "show": { - "play": "", - "trailer": "", + "play": "Riproduci", + "trailer": "Riproduci Trailer", "studio": "", - "genre": "", - "genre-none": "", + "genre": "Generi", + "genre-none": "Nessun genere", "staff": "", "staff-none": "", "noOverview": "", - "episode-none": "", + "episode-none": "Non ci sono episodi in questa stagione", "episodeNoMetadata": "", "tags": "", - "links": "", + "links": "Link", "jumpToSeason": "", "partOf": "", "watchlistAdd": "", "watchlistEdit": "", - "watchlistRemove": "", + "watchlistRemove": "Segna come non visto", "watchlistLogin": "", "watchlistMark": { - "completed": "", + "completed": "Segna come completato", "planned": "", "watching": "", "droped": "", - "null": "" + "null": "Segna come non visto" }, - "nextUp": "", - "season": "" + "nextUp": "Prossimo", + "season": "Stagione {{number}}" }, "browse": { - "sortby": "", - "sortby-tt": "", + "sortby": "Ordina per {{key}}", + "sortby-tt": "Ordina per", "sortkey": { "relevance": "", - "name": "", - "airDate": "", + "name": "Nome", + "airDate": "Data di trasmissione", "startAir": "", "endAir": "", "addedDate": "", @@ -59,46 +59,52 @@ "desc": "" }, "switchToGrid": "", - "switchToList": "" + "switchToList": "", + "mediatypekey": { + "all": "Tutti", + "movie": "Film", + "show": "Serie TV", + "collection": "Collezione" + } }, "misc": { - "settings": "", - "prev-page": "", - "next-page": "", - "delete": "", - "cancel": "", - "more": "", - "expand": "", + "settings": "Impostazioni", + "prev-page": "Pagina precedente", + "next-page": "Pagina successiva", + "delete": "Elimina", + "cancel": "Annulla", + "more": "Di più", + "expand": "Espandi", "collapse": "", - "edit": "", + "edit": "Modifica", "or": "", - "loading": "" + "loading": "Caricamento" }, "navbar": { - "home": "", - "browse": "", - "search": "", - "login": "", - "admin": "" + "home": "Home", + "browse": "Cerca", + "search": "Ricerca", + "login": "Login", + "admin": "Pannello amministratore" }, "settings": { "general": { - "label": "", + "label": "Generale", "theme": { - "label": "", - "description": "", - "auto": "", - "light": "", - "dark": "" + "label": "Tema", + "description": "Imposta il tema della tua applicazione", + "auto": "Sistema", + "light": "Chiaro", + "dark": "Scuro" }, "language": { - "label": "", - "description": "", - "system": "" + "label": "Lingua", + "description": "Imposta la lingua della tua applicazione", + "system": "Sistema" } }, "playback": { - "label": "", + "label": "Riproduzione", "playmode": { "label": "", "description": "" @@ -241,5 +247,22 @@ "scan": "", "empty": "" } + }, + "genres": { + "Mystery": "Mistero", + "Kids": "Bambini", + "Western": "Western", + "History": "Storico", + "Romance": "Romantico", + "ScienceFiction": "Fantascienza", + "Thriller": "Thriller", + "War": "Guerra", + "Animation": "Animazione", + "Action": "Azione", + "Adventure": "Avventura", + "Comedy": "Commedia", + "Crime": "Criminale", + "Documentary": "Documentario", + "Drama": "Drammatico" } } From c065aa8572d6bea8765913974c022492bbf9b9a8 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Tue, 10 Sep 2024 08:18:10 +0200 Subject: [PATCH 18/20] Translations update from Hosted Weblate (#613) * Added translation using Weblate (Spanish) * Translated using Weblate (Spanish) Currently translated at 7.2% (15 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 7.2% (15 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 7.2% (15 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 7.7% (16 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 76.3% (158 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 84.0% (174 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 84.0% (174 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (207 of 207 strings) Translation: Kyoo/Kyoo Translate-URL: https://hosted.weblate.org/projects/kyoo/kyoo/es/ --------- Co-authored-by: Shorsh Fields Co-authored-by: gallegonovato --- front/translations/es.json | 285 +++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 front/translations/es.json diff --git a/front/translations/es.json b/front/translations/es.json new file mode 100644 index 00000000..d440b14c --- /dev/null +++ b/front/translations/es.json @@ -0,0 +1,285 @@ +{ + "home": { + "recommended": "Recomendado", + "news": "Noticias", + "watchlist": "Continuar viendo", + "info": "Ver mas", + "none": "Sin episodios", + "watchlistLogin": "Para hacer un seguimiento de lo que has visto o piensas ver, tienes que iniciar sesión.", + "refreshMetadata": "Refrescar metadatos", + "episodeMore": { + "goToShow": "Ir al show", + "download": "Descarga", + "mediainfo": "Ver información del archivo" + } + }, + "show": { + "play": "Reproducir", + "trailer": "Ver el tráiler", + "studio": "Estudio", + "genre": "Géneros", + "genre-none": "Sin géneros", + "staff": "Equipo", + "staff-none": "El equipo es desconocido", + "noOverview": "Resumen no disponible", + "episode-none": "No hay episodios en esta temporada", + "episodeNoMetadata": "Metadatos no disponibles", + "tags": "Etiquetas", + "links": "Enlaces", + "jumpToSeason": "Ir a temporada", + "partOf": "Parte de la", + "watchlistAdd": "Agregar a su lista de seguimiento", + "watchlistEdit": "Modificar el estado de visto", + "watchlistRemove": "Marcar como no visto", + "watchlistLogin": "Inicia sesión para añadir a tu lista de seguimiento", + "watchlistMark": { + "completed": "Marcar como completado", + "planned": "Marcar como planificado", + "watching": "Marcar como viendo", + "droped": "Marcar como descartado", + "null": "Marcar como no visto" + }, + "nextUp": "Siguiente", + "season": "Temporada {{number}}" + }, + "browse": { + "mediatypekey": { + "all": "Todo", + "movie": "Películas", + "show": "Programas de TV", + "collection": "Colección" + }, + "mediatype-tt": "Tipos de medios", + "mediatypelabel": "Tipo de medio", + "sortby": "Ordenar por {{key}}", + "sortby-tt": "Ordenar por", + "sortkey": { + "relevance": "Relevancia", + "name": "Nombre", + "airDate": "Fecha de emisión", + "startAir": "Empieza emisión", + "endAir": "Finaliza emisión", + "addedDate": "Fecha de incorporación", + "rating": "Calificaciones" + }, + "sortord": { + "asc": "ascendente", + "desc": "descendente" + }, + "switchToGrid": "Cambiar a vista de cuadrícula", + "switchToList": "Cambiar a vista de lista" + }, + "genres": { + "Action": "Acción", + "Adventure": "Aventura", + "Animation": "Animación", + "Comedy": "Comedia", + "Crime": "Crimen", + "Documentary": "Documental", + "Drama": "Drama", + "Family": "Familia", + "Fantasy": "Fantasía", + "History": "Historia", + "Horror": "Horror", + "Music": "Musica", + "Mystery": "Misterio", + "Romance": "Romance", + "ScienceFiction": "Ciencia ficción", + "Thriller": "Suspenso", + "War": "Bélica", + "Western": "Del oeste", + "Kids": "Niños", + "News": "Noticias", + "Reality": "Realidad", + "Soap": "Novela", + "Talk": "Entrevista", + "Politics": "Política" + }, + "misc": { + "settings": "Ajustes", + "prev-page": "Pagina anterior", + "next-page": "Pagina siguiente", + "delete": "Eliminar", + "cancel": "Cancelar", + "more": "Mas", + "expand": "Agrandar", + "collapse": "Expandir", + "edit": "Modificar", + "or": "O", + "loading": "Cargando" + }, + "navbar": { + "home": "Principal", + "browse": "Navegar", + "download": "Descargar", + "search": "Buscar", + "login": "Acceder", + "admin": "Panel de administración" + }, + "settings": { + "general": { + "label": "General", + "theme": { + "label": "Tema", + "description": "Establece el tema de tu aplicación", + "auto": "Sistema", + "light": "Claro", + "dark": "Oscuro" + }, + "language": { + "label": "Idioma", + "description": "Establecer el idioma de tu aplicación", + "system": "Sistema" + } + }, + "playback": { + "label": "Reproducción", + "playmode": { + "label": "Modo de reproducción predeterminado", + "description": "El modo de reproducción predeterminado utilizado con este cliente. La calidad original requiere menos recursos en el servidor, pero no permite cambios automáticos de calidad" + }, + "audioLanguage": { + "label": "Idioma del audio", + "description": "El idioma del audio predeterminado utilizado al reproducir videos con múltiples pistas de audio" + }, + "subtitleLanguage": { + "label": "Idioma de los subtítulos", + "description": "Idioma de los subtítulos utilizado por defecto", + "none": "Ninguno" + } + }, + "account": { + "label": "Cuenta", + "username": { + "label": "Nombre de usuario" + }, + "avatar": { + "label": "Avatar", + "description": "Cambiar icono del perfil" + }, + "email": { + "label": "Correo electrónico" + }, + "password": { + "label": "Contraseña", + "description": "Cambiar tu contraseña", + "oldPassword": "Contraseña anterior", + "newPassword": "Contraseña nueva" + } + }, + "oidc": { + "label": "Cuentas vinculadas", + "connected": "Conectad@ como {{username}}.", + "not-connected": "Desconectad@", + "open-profile": "Abre tu perfil de {{provider}}", + "link": "Enlace", + "delete": "Desvincula tu cuenta de kyoo con tu cuenta de {{provider}}" + }, + "about": { + "label": "Acerca de", + "android-app": { + "label": "Aplicación de Android", + "description": "Descarga la aplicación de Android" + }, + "git": { + "label": "Github", + "description": "Abre el repositorio de GitHub donde puedes explorar el código de Kyoo" + } + } + }, + "player": { + "back": "Atrás", + "previous": "Episodio anterior", + "next": "Siguiente episodio", + "play": "Iniciar", + "pause": "Pausar", + "mute": "Alternar silencio", + "volume": "Volumen", + "quality": "Calidad", + "audios": "Audio", + "subtitles": "Subtítulos", + "subtitle-none": "Ninguno", + "fullscreen": "Pantalla completa", + "direct": "Prístino", + "transmux": "Original", + "auto": "Auto", + "notInPristine": "No disponible en prístino", + "unsupportedError": "Códec de video no compatible, transcodificación en progreso..." + }, + "search": { + "empty": "Sin resultados. Intente otra búsqueda." + }, + "login": { + "login": "Iniciar sesión", + "register": "Registrarse", + "guest": "Continuar como invitado", + "guest-forbidden": "Esta instancia de Kyoo no permite invitados.", + "via": "Continuar con {{provider}}", + "add-account": "Agregar cuenta", + "logout": "Desconectarse", + "server": "Dirección del servidor", + "email": "Correo electrónico", + "username": "Usuario", + "password": "Contraseña", + "confirm": "Confirmar Contraseña", + "or-register": "¿No tienes una cuenta? <1>Regístrate.", + "or-login": "¿Ya tienes una cuenta? <1>Iniciar sesión.", + "password-no-match": "La contraseña no coincide.", + "delete": "Elimine su cuenta", + "delete-confirmation": "Esta acción no se puede deshacer. ¿Está seguro?" + }, + "downloads": { + "empty": "Nada descargado aún, empieza a buscar algo que te guste", + "error": "Error: {{error}}", + "delete": "Eliminar elemento", + "deleteMessage": "¿Quieres eliminar este elemento de tu almacenamiento local?", + "pause": "Pausa", + "resume": "Reanudar", + "retry": "Reintentar" + }, + "errors": { + "connection": "No se pudo conectar al servidor de Kyoo", + "connection-tips": "Consejos para solucionar problemas:\n - ¿Estás conectado a Internet?\n - ¿Está en línea tu servidor de kyoo?\n - ¿Tu cuenta ha sido baneada?", + "unknown": "Error desconocido", + "try-again": "Intentar nuevamente", + "re-login": "Reiniciar sesión", + "offline": "No estás conectado a internet. Intenta de nuevo más tarde.", + "unauthorized": "Faltan los permisos {{permission}} para acceder a esta página.", + "needVerification": "Tu cuenta debe ser verificada por el administrador del servidor antes de que puedas usarla.", + "needAccount": "Esta página no se puede acceder en modo invitado. Necesitas crear una cuenta o iniciar sesión.", + "setup": { + "MissingAdminAccount": "Aún no se ha creado ninguna cuenta de administrador. Por favor, regístrate para crear una.", + "NoVideoFound": "No se encontró ningún video aún. ¡Añade películas o series dentro de la carpeta de tu biblioteca para que se muestren aquí!" + } + }, + "mediainfo": { + "file": "Archivo", + "container": "Recipiente", + "video": "Video", + "audio": "Audio", + "subtitles": "Subtítulos", + "forced": "Forzado", + "default": "Predeterminado", + "external": "Externo", + "duration": "Duración", + "size": "Tamaño", + "novideo": "Sin vídeo", + "nocontainer": "Contenedor invalido" + }, + "admin": { + "users": { + "label": "Usuarios", + "adminUser": "Administrador", + "regularUser": "Usuario", + "set-permissions": "Establecer permisos", + "delete": "Eliminar usuario", + "unverifed": "Sin verificar", + "verify": "Verificar usuario" + }, + "scanner": { + "label": "Escáner", + "scan": "Iniciar escaneo de la biblioteca", + "empty": "No se encontraron problemas. Todos tus elementos están registrados." + } + } +} From c78e2ed9e8ab25b3541606e5c4875312f99e13bd Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 20 Sep 2024 19:40:26 +0200 Subject: [PATCH 19/20] Update biome --- front/packages/models/src/accounts.tsx | 2 +- front/packages/ui/src/browse/header.tsx | 2 +- front/packages/ui/src/browse/index.tsx | 6 +++--- front/packages/ui/src/collection/index.tsx | 2 +- front/packages/ui/src/home/recommended.tsx | 2 +- front/packages/ui/src/player/index.tsx | 3 ++- front/packages/ui/src/player/video.tsx | 2 +- front/packages/ui/src/settings/general.tsx | 2 +- front/packages/ui/src/settings/playback.tsx | 4 ++-- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/front/packages/models/src/accounts.tsx b/front/packages/models/src/accounts.tsx index f2071a21..68d02818 100644 --- a/front/packages/models/src/accounts.tsx +++ b/front/packages/models/src/accounts.tsx @@ -191,7 +191,7 @@ export const AccountProvider = ({ { queryClient.invalidateQueries({ queryKey: ["auth", "me"] }); diff --git a/front/packages/ui/src/browse/header.tsx b/front/packages/ui/src/browse/header.tsx index e37a2e89..462b52e4 100644 --- a/front/packages/ui/src/browse/header.tsx +++ b/front/packages/ui/src/browse/header.tsx @@ -56,7 +56,7 @@ const MediaTypeTrigger = forwardRef { diff --git a/front/packages/ui/src/collection/index.tsx b/front/packages/ui/src/collection/index.tsx index eff48457..b90c133d 100644 --- a/front/packages/ui/src/collection/index.tsx +++ b/front/packages/ui/src/collection/index.tsx @@ -178,7 +178,7 @@ export const CollectionPage: QueryPage<{ slug: string }> = ({ slug }) => { watchStatus={(item.kind !== "collection" && item.watchStatus?.status) || null} unseenEpisodesCount={ item.kind === "show" - ? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount! + ? (item.watchStatus?.unseenEpisodesCount ?? item.episodesCount!) : null } {...css({ marginX: ItemGrid.layout.gap })} diff --git a/front/packages/ui/src/home/recommended.tsx b/front/packages/ui/src/home/recommended.tsx index 2462c1ba..b8e16ec3 100644 --- a/front/packages/ui/src/home/recommended.tsx +++ b/front/packages/ui/src/home/recommended.tsx @@ -307,7 +307,7 @@ export const Recommended = () => { watchStatus={(item.kind !== "collection" && item.watchStatus?.status) || null} unseenEpisodesCount={ item.kind === "show" - ? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount! + ? (item.watchStatus?.unseenEpisodesCount ?? item.episodesCount!) : null } /> diff --git a/front/packages/ui/src/player/index.tsx b/front/packages/ui/src/player/index.tsx index 4268dcf5..f5ad863a 100644 --- a/front/packages/ui/src/player/index.tsx +++ b/front/packages/ui/src/player/index.tsx @@ -92,7 +92,8 @@ export const Player = ({ const [playbackError, setPlaybackError] = useState(undefined); const { data, error } = useFetch(Player.query(type, slug)); const { data: info, error: infoError } = useFetch(Player.infoQuery(type, slug)); - const image = data && data.type === "episode" ? data.show?.poster ?? data?.poster : data?.poster; + const image = + data && data.type === "episode" ? (data.show?.poster ?? data?.poster) : data?.poster; const previous = data && data.type === "episode" && data.previousEpisode ? `/watch/${data.previousEpisode.slug}?t=0` diff --git a/front/packages/ui/src/player/video.tsx b/front/packages/ui/src/player/video.tsx index fcf75d71..24b968ac 100644 --- a/front/packages/ui/src/player/video.tsx +++ b/front/packages/ui/src/player/video.tsx @@ -147,7 +147,7 @@ export const AudiosMenu = ({ audios, ...props }: CustomMenu & { audios?: Audio[] {info.audioTracks.map((x) => ( setAudio(x as any)} /> diff --git a/front/packages/ui/src/settings/general.tsx b/front/packages/ui/src/settings/general.tsx index de8bc7fe..45ba61ed 100644 --- a/front/packages/ui/src/settings/general.tsx +++ b/front/packages/ui/src/settings/general.tsx @@ -71,7 +71,7 @@ export const GeneralSettings = () => { onValueChange={(value) => changeLanguage(value)} values={["system", ...Object.keys(i18n.options.resources!)]} getLabel={(key) => - key === "system" ? t("settings.general.language.system") : getLanguageName(key) ?? key + key === "system" ? t("settings.general.language.system") : (getLanguageName(key) ?? key) } /> diff --git a/front/packages/ui/src/settings/playback.tsx b/front/packages/ui/src/settings/playback.tsx index c8fe8171..8ef1982b 100644 --- a/front/packages/ui/src/settings/playback.tsx +++ b/front/packages/ui/src/settings/playback.tsx @@ -64,7 +64,7 @@ export const PlaybackSettings = () => { onValueChange={(value) => setAudio(value)} values={["default", ...allLanguages]} getLabel={(key) => - key === "default" ? t("mediainfo.default") : getLanguageName(key) ?? key + key === "default" ? t("mediainfo.default") : (getLanguageName(key) ?? key) } /> @@ -83,7 +83,7 @@ export const PlaybackSettings = () => { ? t("settings.playback.subtitleLanguage.none") : key === "default" ? t("mediainfo.default") - : getLanguageName(key) ?? key + : (getLanguageName(key) ?? key) } /> From 633a02af06391206543a82b9ccc51b1b44efb9d0 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 20 Sep 2024 19:34:48 +0200 Subject: [PATCH 20/20] Add an user agent on matcher's requests --- scanner/providers/kyoo_client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scanner/providers/kyoo_client.py b/scanner/providers/kyoo_client.py index 5920db4e..50d220aa 100644 --- a/scanner/providers/kyoo_client.py +++ b/scanner/providers/kyoo_client.py @@ -26,6 +26,9 @@ class KyooClient: async def __aenter__(self): jsons.set_serializer(lambda x, **_: format_date(x), type[Optional[date | int]]) self.client = ClientSession( + headers={ + "User-Agent": "kyoo", + }, json_serialize=lambda *args, **kwargs: jsons.dumps( *args, key_transformer=jsons.KEY_TRANSFORMER_CAMELCASE, **kwargs ),