Commit Graph

14 Commits

Author SHA1 Message Date
Christian Benincasa
83126c8659 fix: fix channel deletes when channel is associated with fillers (#889) 2024-10-18 12:31:05 -04:00
Christian Benincasa
0dd8dbc6ee fix: reimplement BackfillProgramGroupings fixer (#864)
This fixer ran extremely slow for large DBs due to mikro-orm having to
handle many thousands of entities, which it is not primed to do.
Instead, we can generate the updates using existing DB data with direct
updates. However, we will still have to implement a background updater
if we need to pull information from the media source (i.e. it is missing
from the DB)
2024-10-14 14:00:48 -04:00
Christian Benincasa
6e66cfe2f0 feat: introduce new default HLS stream mode (#780)
This commit introduces a new HLS stream mode, modeled off of the great
"HLS segmenter" in ErsatzTV. It also introduces the concept of "channel
stream modes" which are customizable in the UI.

Overall this commit performs a ton of refactoring and cleanup around the
streaming pipeline, consolidating a lot of logic and moving things
around to make all parts of it more flexible, understandable, and
extensible in the future.
2024-09-24 10:28:37 -04:00
Christian Benincasa
29956a81fe fix: simplify query in BackfillProgramGroupings fixer which could cause OOMs
Fixes #788
2024-09-22 13:41:24 -04:00
Christian Benincasa
5ef1fc8bb8 feat: major streaming overhaul (#749)
This commit features a major rewrite and refactoring of the streaming
pipeline and class hierachy in Tunarr. It introduces new default
streaming modes in an attempt to stabilize transitions between program
streams, reuse underlying resources (e.g. ffmpeg processes), reduce the
complexity of interaction between various streaming class components,
and increase flexibility for future development.

Some notable changes:
* The default MPEG-TS stream is now backed by HLS. IPTV and Web
  streaming sessions are shared
* Transcode readahead implementation. This should create smoother
  streams. For FFMPEG 7 (and future versions) we use the native
  "readrate_initial_burst" option. For earlier versions we use an
  artificial readahead. **NOTE**: FFMPEG <7 artificial readahead might
  have unintended side effects (e.g. loudness normalization issues) and
  as such FFMPEG 7+ is recommended
* Separation between building an ffmpeg process, spawning the process,
  and the notion of a 'transcode session'. These are all separate
  classes now with narrow concerns
* Other misc refactoring to cleanup code and remove leftover superfluous
  logic from DTV
2024-09-06 14:44:50 -04:00
Christian Benincasa
f52df44ef0 feat!: add support for Jellyfin media (#633)
This commit includes a huge amount of changes, including support for
adding Jellyfin servers as media sources and streaming content from
them.

These are breaking changes and touch almost every corner of the code,
but also pave the way for a lot more flexibility on the backend for
addinng different sources.

The commit also includes performance improvements to the inline modal,
lots of code cleanup, and a few bug fixes I found along the way.

Fixes #24
2024-08-22 07:41:33 -04:00
Christian Benincasa
c62b1bf40b Tons of potential performance improvements to lineup retrieval/updating (#552)
Includes:
* Batching inserts to external IDs
* Only saving critical external IDs (plex rating key) synchronously on
  lineup update. Non-critical external ID inserts are pushed to
  background tasks
* Program grouping / hierarchy updating has been pushed to a background
  task
* Optimized verison of content program converter function that is
  synchronous. This is much more performant for 1000s of items,
  specifically when no async actions (selecting extra data) is
  necessary. We saw a ~89% reduction in time spent here (>1s to ~30ms)
* A few places were selecting all channel programs when they only needed
  to select the channel
* Only select program IDs at the outset of lineup update, instead of all
  program details. We only need the IDs for relation diffing

Still some things we haven't solved:

1. There is non-trivial overhead in the mikro-orm ORM framework to
   mapping 1000s of selected entities. In a 2k program channel case,
   when loading all necessary entities (relations), we can see ~6k
   entities loaded by the framework. The select from the DB only takes
   about 800ms, but the entity mapping step can take >3s in this case.
   One solution here is to use a simpler library for these super large
   selects (kysely?)
2. It's probably overkill to have both Zodios on the client and then Zod
   checking incoming types on the server. On huge request, this can add
   ~100ms or more (server side) to requests as Zod validates incoming
   requests against the schema
3. We should think about replacing Zod on the server side with JSON
   Schema. There are converters out there. We have a lot invested in
   Zod, so a converter would be a good first step.
4. There's clearly some I/O contention happening in certain situations
   ... background tasks that query DB or Plex, getting responses to flow, logging, etc. I think most of it is DB-related.
5. Unclear if there is any actually _fast_ way to insert the amount of
   data we are currently generating for large channels.
2024-06-22 08:30:38 -04:00
Christian Benincasa
40cff2f719 Force regenerate program_external_id table (#539)
Latest migration incorrectly created the conditional indexes, which
potentially allowed duplicates into the table. There was also a bug
before the latest migration that allowed dupes, which would make the
migration fail when reinserting the data into the new table. Easiest
solution here, since this data 1. still largely exists for programs in
the program table, and 2. is largely unused, is to completely drop the
table, recreate it correctly, and let the BackfillProgramExternalIds run
and fix everything up.

Some verification:

No external_key mismatches after migration

> sqlite> select eid.*, p.title from program_external_id eid join
program p on eid.program_uuid = p.uuid where p.external_key !=
eid.external_key and eid.source_type = 'plex';

There are only 3 programs that we were not able to find rating keys for
however, this means that the programs will be "broken" anyway and will
have to be readded in the UI, since their rating_keys are dangling in
Plex.

> sqlite> select count(p.uuid) from program p left join
program_external_id eid on p.uuid = eid.program_uuid where eid.uuid is
null;
count(p.uuid)
-------------
3

Fixes #537
2024-06-18 07:40:35 -04:00
Christian Benincasa
502099dfdd Implement flexible Program external IDs - beginning impl for source-agnostic Program entities (#493)
* This is a mess

* Fixes

* IDK about this

* Checkpoint

* Checkpoint

* Save external_ids for programs using Plex provided "Guid" field

This is a rather large change to a do a relative simple thing: save IDs
for external sources, such as IMDB/TMDB/TVDB, using IDs provided from
Plex.

It does a few things:

* Creates a delineation in the program_external_id table between
  "single" and "multi" external IDs. Single IDs are global, while multi
IDs are scoped to some external source (e.g. Plex server)
* Starts the migration for Programs to be "source agnostic". Eventually,
  Programs should be a logical entity that has >=1 "source". An ideal
end state would be for a Tunarr instance to have multiple "sources"
where Programs are deduped across them, with the ability to pick the
source for each program per channel, have source fallbacks, and search
for desired settings (e.g. audio language, subtitles) _across_ sources
for a given program. Additionally, saving non-streaming-source external
IDs opens up another avenue of metadata collection for Tunarr
* Implements uniqueness in the program_external_id table using partial
  indexes. This made for a pretty messy change and I'm not super happy
with it.
* Migrates some queries away from the source/external_key fields on
  Program to joins against the program_external_id table
* Implements some backfill mechanisms for these IDs
2024-06-15 21:22:19 -04:00
Christian Benincasa
5f3bc0b70e Revert names of existing migrations -- these are keyed by name so we have to be careful. Going forward we can use better names 2024-06-03 14:46:37 -04:00
Christian Benincasa
330637ec37 Fix standalone executable files. Fixes #469 (#470)
* Attempt 1: Include package.json in the final binary

* Bundle native deps alongside; bundle migrations in main bundle file; zip everything up

* Upload the zip to the edge release
2024-06-03 14:05:10 -04:00
Christian Benincasa
6953e60155 Implement program_external_id table (#468)
Motiviation:
Adding this table is a bit of a future-proofing mechanism. We want to
decouple the notion of a 'program' from its external references.
Currently, the only external reference Tunarr knows of is 'plex'.
Eventually, we will add different sources for programming. In theory, a
program could exist in multiple sources. And not only in multiple media
sources, but on other sites that contain more metadata, such as IMDB and
TMDB. The new DB table generalizes Program to have a 1:M relationship
with external references. Additionally, this allows us to maintain
stable and _unstable_ IDs for various sources (ex. plex rating key)
without losing metadata sourcing for a Program.

Changes:
* Migration to add `program_external_id` table
* Backfill fixer task to fill in details from existing programs in the
  DB
* Changes to program DB save path to save to new table and to start
  saving Plex GUID
* Self-heal when Plex rating key changes. This code is triggered when
  starting a stream for an item. If we have a Plex GUID available, we
will attempt to heal the item in the DB and start the stream
* Use the new table in the stream item calculator

TODO:
* Start saving Plex GUID for program_groupings, which have a separate
  external key table
* Start saving other external IDs for programs, such as IMDB ID and TMDB
  ID
2024-06-01 12:11:05 -04:00
Christian Benincasa
96f33c914b Standardize on plex key that excludes '/library/metadata' prefix. Fixes #268 (#270)
Initially we were trying to mimic the dizqueTV data model. Programs in
DTV saved both Plex's 'ratingKey' and 'key' values. These are
effectively the same thing, from what I can tell, with the 'key' just
being a 'ratingKey' prefixed by '/library/metadata/'.

The issue in this bug was that on the frontend, we were creating
'uniqueId' using the 'ratingKey' and on the backend, we were creating it
using the 'key'. We would end up with LineupPrograms without a proper DB
Program reference, which would break various things on the save path.

This change does a few things:
1. Standardizes on Plex ratingKey as the 'externalKey' on a Program
2. Deprecates the plexRatingKey field on the Program, since eventually
   these will not be Plex-specific anyway and it just causes more
   confusion
3. Updates references to the aforementioned fields

Next steps:
1. Use branded types for additional string type safety - we have a lot
   of srtings flying around!
2024-04-11 09:43:38 -04:00
Christian Benincasa
5570631adc Packaging v0: Build and run server in a docker container (#139)
* This is a nightmare

* Checkpointing.... getting closer

* First cut - packaging the server in a docker container

* Remove busted bundles

* Minify build

* Some common commands for building - we're going to look into proper monorepo solutions soon

* Remove dependency on serve-static

* Add web serving, full-stack docker target, and Nvidia container support

* Remove test action graph for now
2024-03-05 13:13:26 -05:00