Compare commits

..
855 Commits
Author SHA1 Message Date
Jason Dove e363ab00bb prep for release v25.3.0 [no ci] 2025-07-24 20:43:49 -05:00
Jason DoveandGitHub dd9a6d5a06 add chapters to search index (#2199) 2025-07-24 21:03:58 +00:00
Jason Dove fde05a0299 fix docker tag typo 2025-07-24 15:29:12 -05:00
Jason DoveandGitHub d3f8163580 use updated ersatztv-ffmpeg base images (#2198) 2025-07-24 20:27:06 +00:00
Jason DoveandGitHub 07e4ff907f include docker-arm in unified image health check (#2196)
* include docker-arm in unified image health check

* update
2025-07-24 20:20:00 +00:00
Jason Dove 34874ac548 try to fix docker manifest step 2025-07-24 15:03:47 -05:00
Jason DoveandGitHub 03e4c0207b use multi-platform docker images (#2195) 2025-07-24 19:56:46 +00:00
Jason DoveandGitHub b9faf87887 don't use arm64 runner for arm32 builds (#2194) 2025-07-24 03:21:21 +00:00
Jason DoveandGitHub 2257d26173 fix some issues with live stream playback (#2193) 2025-07-24 01:58:32 +00:00
Jason DoveandGitHub 8f6d208e31 use arm64 runners for arm builds (#2192)
* use arm64 runners for arm builds

* use matrix for linux builds on prs

* remove unused "kind"
2025-07-23 21:36:20 +00:00
Jason DoveandGitHub 5ccea53131 fix media file path length for mysql (#2191) 2025-07-23 03:10:13 +00:00
Jason DoveandGitHub da6cb09658 fix tonemapping with amd vaapi (#2187)
* fix amd vaapi tonemap

* fixes
2025-07-22 17:35:06 +00:00
Jason DoveandGitHub 260949893c fix some stream continuity issues (#2186) 2025-07-22 15:56:14 +00:00
Jason DoveandGitHub 89b495dc90 qsv and pts fixes (#2184)
* try to fix qsv freezing

* update changelog

* fix unit tests
2025-07-21 19:00:07 +00:00
Jason DoveandGitHub 74d6b32828 change how qsv is initialized on windows (#2183) 2025-07-21 17:23:30 +00:00
Jason DoveandGitHub 626af6876b add start from beginning option to playback troubleshooting (#2182) 2025-07-21 16:17:16 +00:00
Jason DoveandGitHub 2a05cc6e32 add remote stream is_live property (#2181) 2025-07-21 13:19:51 +00:00
Jason DoveandGitHub 7a4c832156 add media card overflow menu (#2180)
* add media card overflow menu

* remove commented code
2025-07-21 11:00:39 +00:00
Jason DoveandGitHub 011f16da9f fix variant selection for hls remote streams (#2177) 2025-07-20 18:24:04 +00:00
Jason DoveandGitHub 79496e688b fix video stream selection for remote streams (#2176) 2025-07-20 17:31:21 +00:00
Jason DoveandGitHub 5c43ae47b1 add basic remote stream library (#2175)
* initial remote stream library support; scanning seems to work ok

* flood schedule remote streams kind of works

* switch remote stream definitions to yaml files

* implement remote stream script playback

* update changelog
2025-07-20 16:10:32 +00:00
Jason DoveandGitHub c29788bc3f add movie nfo country to search index (#2173) 2025-07-19 21:56:13 +00:00
Jason DoveandGitHub 3501e7c8d5 disable multiple mode select when not using playout mode multiple (#2172) 2025-07-19 20:42:05 +00:00
Jason DoveandGitHub 867c88d8fc add trakt playlist option (#2171)
* add generate playlist option; add system playlists

* fix official lists; sync items to playlist
2025-07-19 16:56:25 +00:00
Jason DoveandGitHub 70fbd4c746 add option to auto refresh trakt lists (#2169) 2025-07-19 14:19:07 +00:00
Jason DoveandGitHub 1cbd48cea0 log nfo file name with nfo parsing errors (#2168) 2025-07-19 02:22:06 +00:00
Jason DoveandGitHub c953176cee change watermark width and margins to allow decimals (#2167) 2025-07-18 21:28:32 +00:00
Jason DoveandGitHub e0cef62969 fix block playout epg time zone (#2166) 2025-07-18 17:14:11 +00:00
Jason DoveandGitHub 9e56f6552f support more multi-part grouping names (#2165) 2025-07-18 16:48:08 +00:00
Jason DoveandGitHub 6a84c564d6 add multi-episode group size (#2164) 2025-07-18 14:46:00 +00:00
Jason DoveandGitHub 54be3761dd add multiple mode to schedule items (#2163) 2025-07-18 14:03:56 +00:00
Jason DoveandGitHub cf6b9cf29a enable write-ahead logging on all sqlite databases (#2162) 2025-07-18 11:17:21 +00:00
Jason DoveandGitHub 464c1e2ea8 fix bugs with playout mode multiple (#2160) 2025-07-18 01:53:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
107e8cfded Bump Jint and System.CommandLine (#2152)
Bumps Jint from 4.3.0 to 4.4.0
Bumps System.CommandLine from 2.0.0-beta5.25306.1 to 2.0.0-beta6.25358.103

---
updated-dependencies:
- dependency-name: Jint
  dependency-version: 4.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: System.CommandLine
  dependency-version: 2.0.0-beta6.25358.103
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 18:23:04 +00:00
Jason DoveandGitHub 837f824660 include hardware info in troubleshooting archive (#2159)
* add cpu and gpu info to troubleshooting general

* include capabilities in troubleshooting archive
2025-07-17 16:23:57 +00:00
Jason DoveandGitHub 223bdff8d6 playback troubleshooting improvements (#2157) 2025-07-17 14:53:11 +00:00
Jason DoveandGitHub 578cdb1e14 add playback troubleshooting tool (#2155)
* support media info for more content types

* add playback troubleshooting page

* reorganize playback troubleshooting

* fix watermarks and delay

* update changelog
2025-07-17 03:51:36 +00:00
Jason DoveandGitHub 848b88bd2d link ffmpeg health check to ersatztv-ffmpeg release (#2154)
* link ffmpeg health check to ersatztv-ffmpeg release

* bump windows ffmpeg to use the same version as linux
2025-07-16 17:13:25 +00:00
Jason DoveandGitHub b85571b159 allow uploading large watermarks (#2151) 2025-07-15 13:51:26 +00:00
Jason DoveandGitHub 43e1cbd919 yaml playout watermarks (#2149) 2025-07-14 03:25:20 +00:00
Jason DoveandGitHub 39b107eb0f add matched_points to filler expression (#2148) 2025-07-13 15:14:13 +00:00
Jason DoveandGitHub 0ee62dbc7d fix recent regression to health check links (#2147) 2025-07-13 13:15:16 +00:00
Jason DoveandGitHub 833bf3506a rework schedule items editor (#2146) 2025-07-13 00:57:27 +00:00
Jason DoveandGitHub cd75046348 rework playlist editor (#2145) 2025-07-12 23:17:47 +00:00
Jason DoveandGitHub 448d29546c rework block editor (#2143) 2025-07-12 19:47:25 +00:00
Jason DoveandGitHub f2c49bd0fd rework alternate schedule and playout template editors (#2142)
* rework alternate schedules editor

* rework playout templates editor
2025-07-12 18:51:44 +00:00
Jason DoveandGitHub 174c743cb7 more mobile layout updates (#2141)
* update trash layout

* cleanup block and yaml playout editors

* spacing cleanup

* rework multi-collection editor

* rework deco template editor

* rework template editor
2025-07-12 15:50:44 +00:00
Jason DoveandGitHub 2a9f23cce6 update layout for group editors (#2140)
* update block group editor

* update playlist group editor

* update template group editor

* update deco group editor

* update deco template group editor

* update deco editor

* update logs layout

* update changelog
2025-07-12 13:45:50 +00:00
Jason DoveandGitHub 451c534062 allow block items to disable watermarks (#2139)
* allow block items to disable watermarks

* fix test
2025-07-12 03:55:18 +00:00
Jason DoveandGitHub e16cb30ab1 add mid-roll filler expression (#2138) 2025-07-12 01:23:16 +00:00
Jason DoveandGitHub e0df454ac6 more layout updates for mobile (#2137)
* update trakt, filler, filler editor ui

* update schedules and playouts

* update playout editor

* update dependencies

* update yaml playout editor

* update path replacement editor
2025-07-11 21:08:37 +00:00
Jason DoveandGitHub e79a03b522 fix displaying local tv artwork in ui (#2136) 2025-07-11 14:44:16 +00:00
Jason DoveandGitHub 1a09bb26d7 lots of mobile updates including detail pages (#2135)
* update artist page layout

* update season page layout

* rework collection view

* cleanup

* update collection editor
2025-07-11 14:21:39 +00:00
Jason DoveandGitHub ffd3e3604c rework many media pages (#2134)
* rework many list pages

* refactor

* rework movie details and season list
2025-07-11 02:47:30 +00:00
Jason DoveandGitHub 7e40a809ff improve search results ui on mobile (#2133)
* show brief health check messages on mobile

* update libraries layout

* improve search results ui on mobile
2025-07-10 19:19:34 +00:00
Jason DoveandGitHub cecf18a7b5 improve mobile layout for some media source pages (#2132) 2025-07-10 17:28:51 +00:00
Jason DoveandGitHub 7df33425fa improve health check display on mobile (#2131) 2025-07-10 15:23:10 +00:00
Jason DoveandGitHub 5dfaa1a7ad improve mobile layout for some pages with tables (#2130) 2025-07-10 14:38:17 +00:00
Jason DoveandGitHub 28a65e74bb use new form layout for local library editor (#2129) 2025-07-10 11:48:20 +00:00
Jason DoveandGitHub 4a66f0ae43 use new form layout for watermark editor (#2127)
* use new form layout for watermark editor

* cleanup
2025-07-09 21:24:03 +00:00
Jason DoveandGitHub fb2466d32d vaapi tonemap fixes (#2125) 2025-07-07 20:58:45 +00:00
Jason DoveandGitHub beaaa62ed9 fix nvidia edge case with missing bit depth info (#2123)
* fix nvidia edge case with missing bit depth info

* revert docker-compose changes
2025-07-07 16:31:11 +00:00
Jason DoveandGitHub 0b445f8cfd cache bust more css (#2119) 2025-07-07 02:50:34 +00:00
Jason DoveandGitHub 7e30444857 dependencies and code cleanup (#2117)
* fix validation in new form layout

* pin mediatr to last oss version

* update dependencies

* cleanup code in core

* cleanup code in ffmpeg

* cleanup code in infra

* cleanup code in scanner

* cleanup code in application

* cleanup main code

* cleanup test code

* solution-wide code cleanup
2025-07-06 15:56:17 +00:00
Jason DoveandGitHub fa6a31b4fc use new form layout for schedule editor (#2116) 2025-07-06 11:32:11 +00:00
Jason DoveandGitHub b01ad9dbae restore incorrectly deleted file (#2114) 2025-07-05 17:40:00 +00:00
Jason DoveandGitHub d324967afa use new form layout for ffmpeg profile editor (#2113) 2025-07-05 13:07:01 +00:00
Jason DoveandGitHub aff4fb0deb use new form layout for channel editor (#2112) 2025-07-05 11:20:53 +00:00
Jason DoveandGitHub 93afcd2f57 more settings updates (#2111)
* update logging settings layout

* update hdhomerun settings layout

* update scanner settings layout

* update playout settings layout

* update xmltv settings layout

* update changelog
2025-07-05 00:45:32 +00:00
Jason DoveandGitHub 921a108684 ui updates (#2109)
* split settings into multiple pages

* show health check badge in nav menu

* undo transcoding test changes
2025-07-04 18:20:22 +00:00
Jason DoveandGitHub a6fa93d44e fix nvidia compatibility with ffmpeg 7.2+ (#2108)
* tweak random seed

* fix dotnet install in docker test

* fix nvidia compatibility with ffmpeg 7.2+
2025-07-03 15:08:18 +00:00
Jason DoveandGitHub a42234a7c3 update plex plot during deep scan (#2105) 2025-07-02 17:14:21 +00:00
Jason DoveandGitHub 7c5137a4af remove some decode threading limits (#2103) 2025-07-02 00:34:07 +00:00
Jason DoveandGitHub 5a9d27e196 make yaml playout count an expression (#2102) 2025-07-01 16:24:11 +00:00
Jason DoveandGitHub cd4a9c1d16 fix hdhr endpoint classification (#2101) 2025-07-01 15:13:16 +00:00
Jason DoveandGitHub f6249d9fa4 channel logo and watermark fixes (#2100)
* channel logo and watermark fixes

* update changelog
2025-07-01 13:40:30 +00:00
Jason DoveandGitHub e2ffa70529 support episodedetails and musicvideo as top-level other video nfo tags (#2098) 2025-07-01 02:59:18 +00:00
Jason DoveandGitHub 3e07bc6136 fix history for playlists in yaml playouts (#2097) 2025-07-01 00:47:53 +00:00
Jason DoveandGitHub d6bfc2fd05 marathon playout history fixes (#2096) 2025-06-30 21:39:31 +00:00
Jason DoveandGitHub 35116c64cd fix potential crash with recent marathon updates (#2095) 2025-06-30 19:23:31 +00:00
Jason DoveandGitHub 037cee873f yaml marathon history (#2094)
* better playlist tests

* fix history for marathon content in yaml playouts
2025-06-30 19:05:09 +00:00
Jason DoveandGitHub cd28afcd91 dont reload appsettings.json at runtime (#2093)
* dont reload appsettings.json at runtime

* also disable here
2025-06-29 17:15:34 +00:00
Jason DoveandGitHub 7457301d3e yaml playout skip items expression (#2092) 2025-06-29 14:31:56 +00:00
Jason DoveandGitHub 7b7d378df7 run all mac builds on macos-14 (#2091) 2025-06-29 13:35:57 +00:00
Jason DoveandGitHub f6dcaf9108 fix qsv audio sync (#2090) 2025-06-29 03:49:04 +00:00
Jason DoveandGitHub 6cc2f1de17 yaml playout improvements (#2088)
* add stop_before_end

* more yaml playout improvements
2025-06-28 16:27:58 +00:00
Jason DoveandGitHub c6ee41484e allow other videos and images to use the same folders (#2087) 2025-06-28 14:50:33 +00:00
Jason DoveandGitHub 36d38c740f only scan plex networks on plex show libraries (#2086) 2025-06-28 12:56:49 +00:00
Jason DoveandGitHub 0f795e4e2f add plex network metadata (#2085)
* initial plumbing

* scan for plex networks

* save network contents to db as tags

* eliminate network tag id churn

* add network and show_network to search index

* update last networks scan

* show networks on tv show page

* update changelog
2025-06-28 12:49:26 +00:00
Jason DoveandGitHub 583cbf7b14 add channel active mode (#2083) 2025-06-27 21:19:26 +00:00
Jason DoveandGitHub 27c701b936 fix software tonemap with nvidia (#2082) 2025-06-27 20:47:09 +00:00
Jason DoveandGitHub 6e2c19d354 process missing language as und (#2081) 2025-06-27 14:45:30 +00:00
Jason DoveandGitHub 4d83dc019c don't return stream selection when subtitles don't match (#2080) 2025-06-27 14:25:07 +00:00
Jason DoveandGitHub 462057a4b1 prioritize stream selection by language (#2079) 2025-06-27 13:31:50 +00:00
Jason DoveandGitHub a04c72788f fix arm64 docker build (#2078) 2025-06-27 11:48:21 +00:00
Jason DoveandGitHub f94a440b62 stream selector improvements (#2077)
* add tests for audio blocklist and audio allowlist

* add subtitle allow list and block list

* add subtitle condition

* add audio condition

* cache bust mudblazor css
2025-06-27 11:40:06 +00:00
Jason DoveandGitHub f80069bb97 add custom channel stream selector system (#2076)
* add some basic channel stream selector models

* change windows ffmpeg url

* implement basic stream selection

* fixes
2025-06-27 03:00:59 +00:00
Jason DoveandGitHub c2769a08b4 stop building hwaccel-specific images (#2075)
* stop building hwaccel images

* update changelog
2025-06-26 17:26:11 +00:00
Jason Dove e679fee940 update CHANGELOG for clarity [no ci] 2025-06-24 19:11:04 -05:00
Jason Dove 2271d5497b prep for release v25.2.0 [no ci] 2025-06-24 12:03:56 -05:00
Jason DoveandGitHub f71b6527c0 allow external channel logo urls (#2067) 2025-06-24 00:43:24 +00:00
Jason DoveandGitHub 20d2fe71cd update macos app (#2066) 2025-06-23 21:19:42 +00:00
Jason DoveandGitHub 1994f171d5 update windows launcher to respect ETV_UI_PORT (#2065) 2025-06-23 20:15:03 +00:00
Jason DoveandGitHub 76f7c88375 add etv env vars to troubleshooting > general info (#2064) 2025-06-23 14:54:06 +00:00
Jason DoveandGitHub 3805b9e48c update dependencies that require api migrations (#2063)
* migrate system.commandline to 2.0.0-beta5 api

* bump sixlabors.imagesharp

* migrate skiasharp to 3.x api
2025-06-23 13:49:24 +00:00
Jason DoveandGitHub 9267edbcc9 fix scanning libraries (#2062) 2025-06-23 13:09:14 +00:00
Jason DoveandGitHub c4c164df6a detect cycles in yaml sequence definitions (#2060) 2025-06-23 02:55:35 +00:00
Jason DoveandGitHub b90463e3af detect cycles in smart collection queries (#2059) 2025-06-23 01:45:17 +00:00
Jason DoveandGitHub 1fb27e3cfa fix nuget issues with transitive dependencies (#2058)
* try to fix nuget issues

* another attempt at surfacing warnings

* restore proper runtime

* remove old an unneeded dependencies

* upgrade transitive dep

* use newer dotnet in github
2025-06-22 20:22:37 +00:00
Jason DoveandGitHub 06f233e5bd upgrade to dotnet 9 (#2056) 2025-06-22 19:04:39 +00:00
Jason DoveandGitHub 9917774671 allow searching by smart collection (#2055) 2025-06-22 15:43:12 +00:00
Jason DoveandGitHub 5be929da18 add collection (name) to search index (#2054) 2025-06-22 14:51:17 +00:00
Jason DoveandGitHub 25f4fb22e5 yaml sequence improvements (#2053) 2025-06-21 19:28:41 +00:00
Jason DoveandGitHub b04b517f7b add dockerfile to run transcoding test suite (#2052) 2025-06-21 18:08:23 +00:00
Jason DoveandGitHub d756c0c7c0 properly filter vaapi driver health check to vaapi ffmpeg profile (#2050) 2025-06-21 11:36:02 +00:00
Jason DoveandGitHub 20e5b8a11a add button to clone schedule item (#2048) 2025-06-20 21:52:45 +00:00
Jason DoveandGitHub 5c8489cbed improve vaapi driver health check (#2047)
* improve vaapi driver health check

* fix duplicate check

* cleanup again
2025-06-20 19:16:52 +00:00
Jason DoveandGitHub 7cfa298c72 fix xmltv grouping with post-roll filler (#2046) 2025-06-20 14:53:04 +00:00
Jason DoveandGitHub 4b0faf4da1 unify hardware acceleration in docker (#2045) 2025-06-20 02:52:05 +00:00
Jason DoveandGitHub 07cbf9936b fix shuffle in order and fill with group mode incompatibility (#2044) 2025-06-19 22:10:30 +00:00
Jason DoveandGitHub 2138d6437c use noautoscale when also using hwaccel cuda (#2043) 2025-06-18 17:06:19 +00:00
Jason DoveandGitHub 5b9601a57b maintain cuda pixel format throughout nvidia pipeline (#2042) 2025-06-18 02:59:10 +00:00
Jason DoveandGitHub aeda5050d3 nvidia decoder fixes (#2041)
* replace FluentAssertions with Shouldly

* fix song transcoding tests

* only specify hwaccel when hardware decode is required

* update changelog
2025-06-17 18:26:49 +00:00
Jason DoveandGitHub ea46a7a5ca add tonemap algorithm setting to ffmpeg profile (#2039) 2025-06-15 00:38:37 +00:00
Jason DoveandGitHub 69a1e718df use ffmpeg 7.1.1 for nvidia docker (#2038) 2025-06-14 23:20:22 +00:00
Jason DoveandGitHub 4a59dafe51 optimize tonemapping performance (#2037)
* add env var to disable vulkan

* tonemap after scaling

* vulkan tonemapping still needs to happen before scaling
2025-06-14 21:32:38 +00:00
Jason DoveandGitHub 6b90da8982 add pad_vaapi filter (#2036) 2025-06-14 13:36:24 +00:00
Jason DoveandGitHub 1184dc565c use ffmpeg 7.1.1 for base, arm, arm64 docker (#2035)
* use ffmpeg 7.1.1 for base, arm, arm64 docker

* keep newline
2025-06-14 11:16:30 +00:00
Jason DoveandGitHub d82ccf8fb5 use hardware-accelerated tonemapping with qsv (#2034)
* add tonemap for qsv

* update changelog
2025-06-14 01:19:35 +00:00
Jason DoveandGitHub 4d83cc705f use ffmpeg 7.1.1 for vaapi docker (#2033) 2025-06-14 00:34:17 +00:00
Jason DoveandGitHub f80addacba use the clip algorithm for software tonemapping (#2032) 2025-06-13 20:12:31 +00:00
Jason DoveandGitHub 18c2a816dc use ffmpeg 7.1.1 on windows (#2031) 2025-06-13 13:36:19 +00:00
Jason DoveandGitHub 4f085c1950 fix detecting nvidia capabilities on blackwell gpus (#2030) 2025-06-13 03:14:22 +00:00
Jason DoveandGitHub dad0662fa6 use libplacebo to tonemap with nvidia/vulkan (#2029) 2025-06-12 20:49:06 +00:00
Jason DoveandGitHub dfdfa6f349 use hardware-accelerated tonemapping with vaapi (#2028)
* add tonemap_vaapi filter

* let vaapi pipeline handle hdr content

* use tonemap_opencl with vaapi

* update changelog
2025-06-12 16:13:43 +00:00
Jason DoveandGitHub 5fe3e97b31 add software tonemap filter to support hdr content (#2027) 2025-06-12 14:47:59 +00:00
Jason DoveandGitHub da1cfab5f4 remove jellyfin admin user id requirement (#2025) 2025-06-09 13:33:47 +00:00
Jason DoveandGitHub 6d152e4b4a use more accurate BANDWIDTH value in multi-variant playlist (#2023) 2025-06-07 20:45:07 +00:00
Jason DoveandGitHub 2ca722523b improvements to plex connection management (#2020) 2025-05-31 18:44:55 +00:00
Jason DoveandGitHub d9a3496bf5 add fixed start time behavior option to schedules and schedule items (#2017) 2025-05-30 09:30:09 +00:00
Jason DoveandGitHub c8f5b51d93 use cache busting to avoid ui errors after upgrading mudblazor (#2016) 2025-05-30 00:22:49 +00:00
Jason DoveandGitHub 956734ce39 globalization fixes (#2014)
* fix crashes caused by decimal separator

* improvements to playout reset ui

* remove code quality workflow
2025-05-26 16:32:25 +00:00
Jason DoveandGitHub e44a391f00 fix navigation when using ETV_BASE_URL (#2013) 2025-05-25 15:29:36 +00:00
Jason DoveandGitHub b43b66dd35 start to make ui minimally responsive (#2004) 2025-04-28 21:39:48 +00:00
Jason DoveandGitHub 140a663da4 update dependencies (#2003)
* update dependencies

* more dependency updates
2025-04-28 17:20:45 +00:00
Jason Dove 876d79c11c update changelog [no ci] 2025-04-22 15:34:28 -05:00
Jason DoveandGitHub 57a4480c3f try to push docker images to ghcr.io (#2000) 2025-04-22 20:24:14 +00:00
Jason DoveandGitHub 8f1b57eb88 another attempt at fixing separate ports behind reverse proxy (#1994) 2025-04-14 18:53:25 +00:00
Jason DoveandGitHub 70472ac84e add public port env vars; allow streaming through ui port (#1993) 2025-04-14 18:21:05 +00:00
Jason Dove 6aab8f53b8 fix changelog [no ci] 2025-04-14 11:57:01 -05:00
Jason DoveandGitHub b30b458574 allow ui and streaming to run on different ports (#1992)
* allow ui and streaming to run on different ports

* revert global.json change
2025-04-14 16:48:19 +00:00
Jason DoveandGitHub 6fe6382485 add remote ip and user agent to http request logging (#1990) 2025-04-07 18:57:08 +00:00
Jason DoveandGitHub 100ae0adda add api endpoint to empty trash (#1988) 2025-04-06 14:26:40 +00:00
Jason DoveandGitHub f0d5200843 bump image sharp lib to fix build (#1986) 2025-04-04 02:48:12 +00:00
Jason DoveandGitHub af36218cc2 add linux-musl-x64 artifact (#1985) 2025-04-04 02:45:09 +00:00
Jason DoveandGitHub eca62e8bec fix error synchronizing collections from plex server that has zero collections (#1964) 2025-01-15 21:42:22 -06:00
Jason Dove 03d4ab8c72 no longer mark releases as pre-release [no ci] 2025-01-10 12:26:53 -06:00
Jason Dove 57b590cd0f prep for release v25.1.0 [no ci] 2025-01-10 12:12:39 -06:00
Jason DoveandGitHub f0a5d89f73 fix song progress with 4:3 resolutions (#1961) 2024-12-27 16:09:58 -06:00
Jason DoveandGitHub 17a77694a0 add tag_full field to search index (#1956) 2024-12-09 13:55:47 -06:00
Jason DoveandGitHub 838c2a1661 fix default song background for 4:3 resolutions (#1951)
* fix default song background for 4:3 resolutions

* use 16:9 progress overlay for now
2024-11-28 08:56:38 -06:00
Jason DoveandGitHub 375a306edc fix vaapi capabilities detection when display is not drm (#1949)
* fix vainfo device syntax

* update logging

* don't pass vaapi_device when display is not drm

* fix hw accel

* proper accel fix

* Revert "proper accel fix"

This reverts commit 5dbadb1f79.

* Revert "fix hw accel"

This reverts commit b6b132d7b9.

* Revert "don't pass vaapi_device when display is not drm"

This reverts commit 0459210718.

* still show vaapi device
2024-11-25 14:27:21 -06:00
Jason DoveandGitHub 759052c725 add vaapi display option to ffmpeg profile (#1948)
* add vaapi display option

* fix vaapi capabilities cache key

* update logging
2024-11-25 13:14:21 -06:00
Jason DoveandGitHub dc112f0c7d add default song album art; adjust default blurhashes (#1946)
* fix song progress on white backgrounds

* remove yellow from default song backgrounds

* add default album art for generated song images

* update changelog
2024-11-20 22:27:09 -06:00
Jason DoveandGitHub c8ec87b01f add optional progress bar to generated song videos (#1945)
* optionally include progress bar in generated song video

* update progress bar size/location

* move everything up 10% when song progress is enabled

* add watermark border to song progress bar

* always show accurate progress bar

* lower progress bar to 90% alpha

* update changelog
2024-11-20 15:57:00 -06:00
Jason DoveandGitHub fbb7a661fb fix guide group generation for duration yaml instruction (#1943) 2024-11-19 06:26:19 -06:00
Jason DoveandGitHub 37ceac5651 add advance option to epg_group yaml playout instruction (#1942) 2024-11-18 22:07:21 -06:00
Jason DoveandGitHub 0953e258a5 add rewind_on_reset option wait_until yaml playout instruction (#1941) 2024-11-18 19:31:51 -06:00
Jason DoveandGitHub fdbd8a07b6 update macos runner to macos-13; macos-12 is no longer supported (#1940) 2024-11-18 09:59:37 -06:00
Jason DoveandGitHub 5465c45a51 fix plex collection sync (#1939) 2024-11-18 09:41:52 -06:00
Jason DoveandGitHub a0bef3568b fix selecting audio stream with preferred title (#1937)
* update dependencies to fix build errors

* fix selecting audio stream with preferred title
2024-11-16 19:28:30 -06:00
Jason DoveandGitHub f75bb25a1a fix building playouts with certain playlist changes (#1935)
* fix building playouts with certain playlist changes

* update changelog
2024-11-13 14:38:07 -06:00
Jason DoveandGitHub e4ff825ae8 fix displaying playout item durations that are greater than 24 hours (#1932)
* fix displaying playout item durations that are greater than 24 hours

* upgrade refit to make build happy

* upgrade all dependencies
2024-11-08 10:11:57 -06:00
Jason DoveandGitHub 6c4f63ad91 fix scaling behavior crop (#1928) 2024-10-26 08:38:44 -05:00
Jason DoveandGitHub c0a14ba81c add button to reset all playouts (#1927) 2024-10-24 11:23:47 -05:00
Jason DoveandGitHub c063720169 use new channel identifiers in M3U and XMLTV to disambiguate in Plex (#1920) 2024-10-16 09:37:33 -05:00
Jason DoveandGitHub 5d0f40978d fix deleting local libraries with mysql (#1919) 2024-10-16 08:46:45 -05:00
Jason DoveandGitHub 063f463951 update dependencies (#1918) 2024-10-16 08:40:03 -05:00
Jason DoveandGitHub e7817b1460 fix deco default filler hardware acceleration (#1916)
* fix hw accel for deco default filler

* use custom filler kind for deco default filler
2024-10-11 14:33:30 -05:00
Jason DoveandGitHub 7d6faee27b properly destroy channel preview (#1910) 2024-10-04 12:29:40 -05:00
Jason DoveandGitHub 7bba422880 fix emptying trash (#1905) 2024-09-25 10:27:34 -05:00
Jason DoveandGitHub db6ae27384 fix mysql database cleaner (#1902) 2024-09-20 08:42:52 -05:00
Jason Dove 4b9fc5004f update changelog for release v0.8.8-beta [no ci] 2024-09-19 11:15:41 -05:00
f40eaef898 [scheduling] Add a new mode RandomRotation that randomly picks an item from a randomly choosen group (show/artist) for block schedule (#1885)
* init

* minor naming change

* address to comments round 1

* update dependencies

* formatting

* make sure it rotates

* update changelog

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-09-10 13:36:20 -05:00
91e85cc9c1 [Filler] Add random count for filler preset (#1886)
* init

* minor update

* clean up

* minor cleanup

* update changelog

* update changelog again

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-09-03 10:25:20 -05:00
Jason DoveandGitHub 2c44efb971 update dependencies (#1891) 2024-09-03 10:09:37 -05:00
Jason DoveandGitHub c2b7be66af restart hls session in some cases (#1880) 2024-08-23 12:59:48 -05:00
Jason DoveandGitHub 8b911332a6 fix watermark opacity for transparent watermarks (#1877) 2024-08-22 11:35:44 -05:00
Jason DoveandGitHub 4130f7316c fix block playout history loading (#1876) 2024-08-22 09:05:21 -05:00
Jason DoveandGitHub 3f6eb5a121 fix some collection related bugs with images (#1874) 2024-08-21 10:36:42 -05:00
Jason DoveandGitHub 1209c54eb9 prevent saving overlapping blocks (#1872) 2024-08-15 10:37:57 -05:00
Jason DoveandGitHub 94db4bf679 fix local subtitle scans for non-lowercase extensions (#1865)
* fix local subtitle scans for non-lowercase extensions

* remove some unneeded changes
2024-08-07 20:47:27 -05:00
Jason DoveandGitHub 2977590a14 add deco setting to use watermarks during filler (#1861) 2024-08-05 13:40:24 -05:00
Jason DoveandGitHub b4c168e85e use trakt user slug for proper url generation (#1859) 2024-08-05 09:41:24 -05:00
Jason DoveandGitHub 55b7a35689 fix missing movie metadata (#1854) 2024-08-03 08:05:50 -05:00
Jason DoveandGitHub a24592a8c4 add database cleaner (#1853)
* fix broken tests

* add database cleaner
2024-08-02 10:46:52 -05:00
Jason DoveandGitHub 9b60ff0863 optionally shuffle marathon groups (#1850) 2024-07-31 17:33:08 -05:00
Jason DoveandGitHub efdf0bb6d4 group music videos by album (#1849) 2024-07-31 16:13:13 -05:00
SylvainandGitHub 39ca27cb3d Overlay Generated Channel Logo when active but no artwork is found (#1848) 2024-07-31 10:04:42 -05:00
Jason DoveandGitHub 9e2f7b7815 fix deco selection for watermark and filler (#1847) 2024-07-30 21:57:21 -05:00
Jason DoveandGitHub 101d46e283 dont remove block items that are filler (#1846) 2024-07-30 20:38:13 -05:00
Jason DoveandGitHub 521e4eac41 add yaml marathon search content source (#1845)
* use search queries to populate marathons

* group marathon by artist

* add marathon group by album
2024-07-30 20:30:13 -05:00
Jason DoveandGitHub 894fc284b2 fix deco template name display (#1844)
* fix deco template name display

* try to fix mac build
2024-07-30 19:45:25 -05:00
Jason DoveandGitHub a8cf22e43e group marathon by season (#1843) 2024-07-30 19:24:56 -05:00
Jason DoveandGitHub 4c9c047530 add basic marathon content (#1842) 2024-07-30 18:21:24 -05:00
Jason DoveandGitHub 912f79097d add collection, smart collection, multi collection, playlist content sources to yaml playouts (#1841)
* add collection content to yaml playout

* add smart_collection content

* add multi_collection content

* add playlist content
2024-07-30 10:46:08 -05:00
Jason Dove 8aa55fdfce replace new_epg_group instruction with epg_group; copy sequence custom title to sequence items 2024-07-30 08:36:14 -05:00
Jason DoveandGitHub 8dc1cab222 fix media card selection (#1840) 2024-07-30 06:21:38 -05:00
Jason DoveandGitHub 961fe8bbf2 improve shuffling behavior; add custom_title (#1838)
* improve yaml shuffling behavior

* add custom_title to playout instructions
2024-07-29 19:53:06 -05:00
Jason DoveandGitHub 75f991d670 yaml history fix (#1836) 2024-07-29 16:21:51 -05:00
Jason DoveandGitHub e3c981004b show all items in epg by default (#1835) 2024-07-29 15:48:20 -05:00
Jason DoveandGitHub befaa037e2 default duration to make a new epg group per item; default duration to NOT use offline tail (#1834) 2024-07-29 15:26:13 -05:00
Jason DoveandGitHub 5e0fb31069 add reset playout and scan library api endpoints (#1833)
* add api to reset playout

* add library scan api

* update changelog
2024-07-29 13:50:33 -05:00
Jason DoveandGitHub 7d83e66ba6 add yaml playout history; allow yaml playouts to be extended (#1832)
* add multi_part; refactor skipping items

* save and apply history for yaml playouts

* do not remove history on yaml playout reset
2024-07-29 13:09:14 -05:00
Jason DoveandGitHub 391528cd94 add pad_until instruction (#1831)
* revert dotnet workaround

* add pad_until instruction
2024-07-29 06:36:14 -05:00
Jason DoveandGitHub b737775f9a add yaml skip to item instruction (#1830)
* work around MSB3374 error

* add skip to item instruction
2024-07-28 22:12:54 -05:00
Jason Dove 728c5130b5 try without quotes 2024-07-28 17:34:25 -05:00
Jason Dove e4253276e0 let's try completely separate folders 2024-07-28 16:49:07 -05:00
Jason Dove 1fc55bc693 try to fix build again 2024-07-28 16:44:38 -05:00
Jason DoveandGitHub 4ad22e402f use global.json dotnet version in workflows (#1829)
* use global.json dotnet version in workflows

* list output files

* work around weird folder emptying behavior on windows
2024-07-28 16:39:19 -05:00
Jason DoveandGitHub ec99d5976d add shuffle sequence instruction (#1827) 2024-07-28 13:51:26 -05:00
Jason DoveandGitHub 59f11f1a1a add yaml playout sequences (#1826) 2024-07-28 13:22:01 -05:00
Jason DoveandGitHub 694f25f8b3 upgrade to mudblazor 7 (#1825) 2024-07-28 11:31:04 -05:00
Jason DoveandGitHub 5947555e86 improve trakt list url validation (#1824)
* improve trakt url validation and logging

* update changelog
2024-07-28 09:24:54 -05:00
Jason DoveandGitHub fb63116b36 more subtitles fixes (#1823) 2024-07-27 22:20:44 -05:00
Jason DoveandGitHub 56a58d7a84 fix missing audio and subtitle language codes (#1822) 2024-07-27 20:44:49 -05:00
Jason DoveandGitHub 6f66909957 add "all" instruction (#1821)
* add "all" instruction

* use string for value we don't care about
2024-07-27 14:26:33 -05:00
SylvainandGitHub 01090f62e6 Fixing URL Encoding for logo generation (#1818) 2024-07-27 10:37:37 -05:00
Jason DoveandGitHub e4e4f68eb4 refactor yaml playout builder (#1820)
* update changelog

* refactor some handlers

* refactor skip items instruction

* more refactoring
2024-07-27 10:33:21 -05:00
8488fe5d3d Used a UUID in HDHomeRun config to allow multiple instances on a same network (#1810)
* Used a UUID in HDHomeRun config to allow multiple instances on a same network

* tweak some async calls

* try to fix line endings

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-07-27 08:51:45 -05:00
Jason DoveandGitHub f06ef5262a add new_epg_group instruction; add filler_kind propery (#1819) 2024-07-27 08:38:00 -05:00
Jason DoveandGitHub ae6bcc4933 add yaml playout skip items instruction (#1816) 2024-07-26 20:37:02 -05:00
Jason DoveandGitHub b83fe53ef1 add wait until instruction (#1815) 2024-07-26 19:52:43 -05:00
Jason DoveandGitHub d50f2ace07 fix regression selecting subtitle streams for certain languages (#1814) 2024-07-26 17:44:17 -05:00
23684f607a Generating Channel Logo when no logo is provided (#1807)
* Generating Channel Logo when none is provided

* Moved TTF in the cached Resources folder

* Using WebUtility.UrlEncode instead of Raw String Replace

* Fixed mistyping

* Moved Channel Logo Generator to etv.core

* Return 301 to static logo if there is any error during Logo generation

* minor fixes

* update changelog

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-07-26 15:13:00 -05:00
Jason DoveandGitHub fa20c5e01e template playout => yaml playout (#1813) 2024-07-26 14:33:55 -05:00
Jason DoveandGitHub 53bd745678 add playout template show content (#1812) 2024-07-26 13:10:27 -05:00
Jason DoveandGitHub f3e5a4e7d8 add playout template duration scheduler (#1811)
* fix loop with missing content

* implement template duration scheduler
2024-07-26 12:03:46 -05:00
Jason DoveandGitHub 0b29bb32b1 playout template pad to next fixes (#1809)
* prevent loop

* add discard attempts and fallback to pad_to_next
2024-07-26 10:23:38 -05:00
Jason DoveandGitHub d9a7615cf6 add experimental playout template system (#1808)
* add template playout kind

* add template scheduler count

* implement pad to next

* only allow resetting template playouts

* update changelog
2024-07-26 08:34:18 -05:00
Jason DoveandGitHub 50f2cb7a33 fix adding pad filler to short content (#1806) 2024-07-25 19:14:16 -05:00
Jason DoveandGitHub b1b2c2a1e0 add deco default filler trim to fit setting (#1800)
* add deco default filler trim to fit setting

* implement trim to fit

* update changelog
2024-07-22 14:14:02 -05:00
Jason DoveandGitHub d842cd57f6 fix building block playouts without default filler (#1799) 2024-07-22 09:05:31 -05:00
Jason DoveandGitHub 4f393d7b06 fix two letter language code stream selection (#1798) 2024-07-22 06:54:08 -05:00
Jason DoveandGitHub 46f7289db8 add deco default filler (#1797)
* first pass at default filler for block scheduling

* configure default filler in ui

* update changelog
2024-07-19 13:29:42 -05:00
Jason DoveandGitHub 80ccbbf299 fix duration playout loop (#1796) 2024-07-18 12:18:49 -05:00
Jason DoveandGitHub 3765894cb7 remove invalid values from filler preset editor (#1793) 2024-07-17 16:51:39 -05:00
Jason DoveandGitHub a8b658a5ea add "on demand" channel progress mode (#1790)
* update dependencies

* add channel progress mode

* implement on demand channel progress

* update changelog
2024-07-16 12:21:52 -05:00
SylvainandGitHub 0e3c32bd83 Adding more HEAD handling (https://github.com/ErsatzTV/ErsatzTV/pull/1786) (#1787) 2024-07-13 06:28:28 -05:00
Jason DoveandGitHub 9dd4a85bf9 fix adding items to empty playlists (#1784) 2024-07-11 12:17:41 -05:00
SylvainandGitHub a0a047ba18 Added API Artwork Router (#1776)
* Added API Artwork Router

* Simplifying code
2024-07-08 15:15:14 -05:00
SylvainandGitHub 687a4f4f10 Alow HEAD requests on /iptv/channels.m3u (#1779) 2024-07-08 13:10:36 -05:00
b91ab5d898 Fixing Artwork OtherVideo rel (#1774)
* Fixing Artwork OtherVideo rel

* show other video artwork in ui

* don't run code quality on PRs

* update changelog

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-07-05 09:16:09 -05:00
Jason DoveandGitHub 256042947d use macos-12 runners for github actions (#1773) 2024-07-05 06:39:09 -05:00
SylvainandGitHub 85029cbbcd Escaping & on xmltv file (#1772) 2024-07-05 05:56:14 -05:00
Jason DoveandGitHub b5d679212d cache bust xmltv images (#1771) 2024-07-03 12:14:40 -05:00
36e86587ef Allow Other Videos Library Type on Plex to be sync (#1766)
* Allow Other Videos Library Type on Plex to be sync

* Migrating database: Creating PlexOtherVideo table

* Using Plex Media path to create tags for OtherVideos

* missed these in the merge

* Getting PlexLibrary for Tag set on OtherVideo

* fix migrations

* set tag metadata on plex other videos

* update changelog

---------

Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>
2024-07-02 15:41:09 -05:00
Jason DoveandGitHub f41fa669be update media server scanning and paging (#1770)
* update media server scanning and paging

* remove unused types
2024-07-02 13:12:09 -05:00
Jason Dove f62e841af4 update changelog for release v0.8.7-beta [no ci] 2024-06-26 22:11:36 -05:00
Jason DoveandGitHub 109960c457 fix some more search queries (#1763) 2024-06-26 22:08:59 -05:00
Jason DoveandGitHub 6858103be5 fix transcoding 10-bit source content using qsv on windows (#1762) 2024-06-26 16:21:42 -05:00
Jason Dove f7f1a0493b update macos submodule again 2024-06-25 10:43:31 -05:00
Jason DoveandGitHub 6c18648fd7 update macos submodule (#1760) 2024-06-25 10:31:37 -05:00
Jason DoveandGitHub ab3afcfad0 use ffmpeg 6.1 everywhere instead of ffmpeg 7 (#1754) 2024-06-21 12:30:07 -05:00
Jason DoveandGitHub 5734356d29 fix template and deco template editors with mysql (#1750) 2024-06-17 15:36:14 -05:00
Jason DoveandGitHub beee038be3 fix video_bit_depth search field for plex media (#1745) 2024-06-10 15:46:23 -05:00
Jason DoveandGitHub a5c8e2b401 fix duration scheduling bug (#1744) 2024-06-10 12:30:56 -05:00
Jason DoveandGitHub c8113bdf25 fix vaapi decoder capability check (#1740)
* fix decoder video profile check

* clarify changelog
2024-06-04 06:10:02 -05:00
Jason DoveandGitHub 9466cf7626 update vaapi docker bundled ffmpeg (#1729) 2024-05-25 12:13:05 -05:00
Jason DoveandGitHub 43fcf9e63a upgrade to ffmpeg 7 on windows (#1727) 2024-05-24 11:31:28 -05:00
Jason DoveandGitHub 242a7ae382 add b-frames option to ffmpeg profile (#1726)
* add b-frames option to ffmpeg profile

* update dependencies

* cleanup
2024-05-24 08:50:11 -05:00
Jason DoveandGitHub 6dc526a817 fix dotnet in vaapi docker (#1724) 2024-05-23 07:38:01 -05:00
Jason DoveandGitHub 182c02b865 use ffmpeg 7 with vaapi again (#1723) 2024-05-22 20:09:38 -05:00
Jason DoveandGitHub 7c1f0d6dbd treat mid-roll as post-roll when chapter markers are missing (#1722) 2024-05-21 15:51:35 -05:00
Jason DoveandGitHub 2c97c49763 custom config folders env vars (#1714)
* use custom config folder location

* allow customizing config and transcode folder locations
2024-05-10 09:28:58 -05:00
Jason DoveandGitHub f5038c2b66 play all items in playlist before starting again (#1711) 2024-05-07 17:05:12 -05:00
Jason DoveandGitHub 85eb7623da fix playlist playouts (#1709)
* fix playlist playouts

* remove some logging
2024-05-06 20:40:10 -05:00
Jason DoveandGitHub a87ec2d75d cleanup (#1708)
* fix blazor naming

* code cleanup

* update dependencies
2024-05-06 17:00:52 -05:00
Jason DoveandGitHub ef6c8b0816 allow clearing (omitting) video profile setting (#1706) 2024-05-05 19:23:26 -05:00
Jason DoveandGitHub dc3578660f fix flood playout item watermark (#1705) 2024-05-05 09:08:08 -05:00
Jason DoveandGitHub ce958bb7bb enable video profile editing for qsv (#1704) 2024-05-05 08:53:34 -05:00
Jason DoveandGitHub 33a8b29a27 fix block playout random seeds (#1703) 2024-05-04 09:09:04 -05:00
Jason DoveandGitHub 48a504735c fix xmltv generation when no block items are selected for guide (#1702) 2024-05-03 09:18:06 -05:00
Jason DoveandGitHub 0b64e97df6 fix media card (#1701) 2024-05-02 16:38:20 -05:00
Jason DoveandGitHub 5d89f5d0a4 fix template editors (#1700) 2024-05-02 12:59:13 -05:00
Jason Dove 50a6ed4cea fix docker again 2024-05-02 10:31:01 -05:00
Jason DoveandGitHub 66b8c8aa0e fix docker builds (#1699) 2024-05-02 10:23:07 -05:00
Jason DoveandGitHub 398a3c041a detect /dev/dri/card* devices (#1698)
* remove unused nupkg

* cleanup obsolete properties

* detect /dev/dri/card devices
2024-05-02 10:17:38 -05:00
Jason DoveandGitHub 6ce7265427 add qodana workflow (#1697) 2024-05-02 08:34:17 -05:00
Jason DoveandGitHub b4fe38d4ae segmenter v2 fixes (#1696) 2024-05-01 12:53:56 -05:00
Jason DoveandGitHub e19b639527 add playlist item play all option (#1694) 2024-04-28 13:00:22 -05:00
Jason DoveandGitHub a6d5df3ca6 more add to playlist buttons; fix playlist preview (#1693) 2024-04-28 08:59:14 -05:00
Jason DoveandGitHub 202ae33e37 add individual media items to playlists (#1692)
* add movies to playlists

* add search results to playlist

* update changelog
2024-04-28 06:33:57 -05:00
Jason DoveandGitHub c46b3787d8 set guide mode filler as appropriate on playlist items (#1691) 2024-04-27 20:31:03 -05:00
Jason DoveandGitHub 706a2d14a2 add playlists (#1690)
* update dependencies

* add playlists

* add playlist support to schedules

* playout builder (flood) supports playlists

* update changelog
2024-04-27 19:58:40 -05:00
Jason DoveandGitHub 11f99216a3 fix plex library scans with mysql/mariadb (#1689) 2024-04-24 13:16:53 -05:00
Jason DoveandGitHub b9a7ad2f5a add video presets for nvenc, qsv, software h264 and hevc encoders (#1688)
* add video preset to ffmpeg profile

* add some hevc video presets
2024-04-23 14:26:30 -05:00
Jason DoveandGitHub 07e7e5fe66 fix block playout playback when no deco is configured (#1687) 2024-04-23 13:10:11 -05:00
Jason Dove 4a19d046e4 fix mysql db migrations 2024-04-22 22:40:53 -05:00
Jason DoveandGitHub c1d6ddcc57 add h264 profile option to ffmpeg profile (#1686)
* add video profile for nvenc/software h264 encoders

* add h264 profile for all other encoders

* update changelog
2024-04-22 22:03:18 -05:00
Jason DoveandGitHub 35eb200aee more segmenter v2 improvements (#1685)
* more segmenter v2 improvements

* changelog updates
2024-04-21 19:17:06 -05:00
Jason DoveandGitHub 19af303d76 improve segmenter v2 hevc compliance with fmp4 (#1684) 2024-04-21 10:01:11 -05:00
Jason DoveandGitHub da20393a39 allow m3u8 playlist head requests (#1683) 2024-04-21 08:18:05 -05:00
Jason DoveandGitHub d78110f6f1 fix macos config folder regression (#1682)
* migrate macos config folder on startup, if needed

* add macos config folder health check

* update macos fix; update changelog
2024-04-17 09:18:09 -05:00
Jason DoveandGitHub c1bedb661c rever vaapi docker to ffmpeg 6.1 (#1681) 2024-04-16 22:27:26 -05:00
Jason DoveandGitHub d31d6f20cf upgrade to ffmpeg 7 in all docker images (#1680)
* upgrade nvidia docker to ffmpeg 7

* upgrade all docker images to ffmpeg 7

* update changelog
2024-04-16 18:24:40 -05:00
Jason DoveandGitHub 7469559bb3 fix external subtitle detection (#1679) 2024-04-15 12:31:06 -05:00
Jason DoveandGitHub af5dc0968b add xmltv days to build setting (#1678) 2024-04-12 09:44:08 -05:00
Jason DoveandGitHub 6e7880386b update plex show title (#1677)
* update plex show title when changed in plex

* fix test build
2024-04-12 09:21:11 -05:00
Jason DoveandGitHub 8945a87f9b fix segmenter semaphore (#1676)
* disable dead air watermarks by default

* fix session worker semaphore

* update changelog
2024-04-11 19:38:57 -05:00
Jason DoveandGitHub e305222141 add dead air fallback to deco system (#1668)
* add dead air fallback to deco editor

* fix deco watermark logic

* use dead air fallback from decos
2024-04-05 23:11:14 -05:00
Jason DoveandGitHub 2e2523c380 another wildcard search fix (#1667) 2024-04-05 09:12:46 -05:00
Jason DoveandGitHub b461631be9 properly update plex part keys (#1666) 2024-04-04 22:40:21 -05:00
Jason DoveandGitHub 19fc068a73 add deco system (#1665)
* add deco groups and decos; set default deco for block playout

* use block playout default deco for watermark

* add deco templates, groups and deco template editor

* associate deco template with playout template

* use deco template item watermark for playback

* update changelog for decos
2024-04-04 14:38:56 -05:00
Jason DoveandGitHub e5f15df196 rework active date range; add tests (#1664) 2024-04-03 13:43:20 -05:00
Jason DoveandGitHub 6ac2384cbc add active date range to block playout templates editor (#1663)
* update dependencies

* add active date range to block playout templates editor
2024-04-03 10:50:43 -05:00
Jason Dove cd6673da82 update changelog for v0.8.6-beta [no ci] 2024-04-03 05:04:35 -05:00
Jason DoveandGitHub 8113827802 allow block durations with 5-minute increments (#1662) 2024-04-02 10:27:27 -05:00
Jason DoveandGitHub 4e56117e0e use jwt for mpeg-ts streaming mode (#1661) 2024-03-29 21:36:49 -05:00
Jason DoveandGitHub 7702999b9a properly restore all local library items from trash during scans (#1660) 2024-03-29 20:03:19 -05:00
Jason DoveandGitHub 14a707a4e2 improve plex item change detection (#1659) 2024-03-29 15:15:42 -05:00
Jason DoveandGitHub 340ab61a26 add more logging to other video library scans (#1658) 2024-03-29 14:02:12 -05:00
Jason DoveandGitHub d91d991251 always interpret asterisk as wildcard search (#1657) 2024-03-29 13:27:18 -05:00
Jason DoveandGitHub 3ce341eee5 fix build error for single file artifacts (#1656) 2024-03-29 12:21:58 -05:00
Jason DoveandGitHub 476fe991b6 other video library scanner improvements (#1655)
* improve scanner detection when developing on macos

* support ogv files in local libraries

* improve health check wording

* try to properly restore other videos that are no longer missing
2024-03-29 12:00:46 -05:00
Jason DoveandGitHub 39df3504fc content rating shouldn't ever be a phrase (#1653) 2024-03-22 09:43:51 -05:00
Jason DoveandGitHub 60bb369d0c add search query parser (#1652)
* add search query parser

* add some search tests; use consistent analyzers for indexing and searching
2024-03-22 09:41:08 -05:00
Jason DoveandGitHub aae704f3a5 fix searching id fields (#1651) 2024-03-21 19:33:50 -05:00
Jason DoveandGitHub a45583d77a fix type and tag searches (#1649) 2024-03-20 22:52:16 -05:00
Jason DoveandGitHub 923b36604c fix many search queries (#1648) 2024-03-20 22:29:51 -05:00
Jason DoveandGitHub b21d16b0f1 add show_content_rating to search index (#1647) 2024-03-20 19:46:19 -05:00
Jason DoveandGitHub a5aaceeee5 fix path replacement logic with inconsistent casing (#1646) 2024-03-19 08:50:00 -05:00
Jason DoveandGitHub e52d45fcf8 fix mysql migration (#1645)
* update dependencies; resync mysql db

* manually fix mysql migration
2024-03-18 11:09:27 -05:00
Jason DoveandGitHub 21d39bc26f fix multi collection editor id collision (#1644) 2024-03-15 09:48:01 -05:00
Jason DoveandGitHub 233a1c228a fix block creation (#1643)
* fix block creation

* update dependencies
2024-03-13 08:32:58 -05:00
Jason DoveandGitHub 56988be57b properly isolate library folders between library paths (#1642) 2024-03-10 20:06:37 -05:00
Jason DoveandGitHub aded03d962 fix deleting local libraries and local library paths (#1640) 2024-03-09 12:12:22 -06:00
Jason DoveandGitHub 2119c88c97 always downsample after loudnorm (#1639) 2024-03-07 09:15:02 -06:00
Jason DoveandGitHub a5d83a970a use mkv container for 8-bit segmenter v2 content (#1637) 2024-03-06 12:42:48 -06:00
Jason DoveandGitHub 986785d863 always use nv12 for 8 bit vaapi encoding (#1636) 2024-03-06 12:03:14 -06:00
Jason DoveandGitHub 087901d177 adjust block unique constraint (#1634)
* upgrade dependencies

* allow blocks with same name in different groups

* code cleanup
2024-03-05 10:39:06 -06:00
Jason DoveandGitHub 70c4036dc9 fix ten bit source and destination with segmenter v2 (#1633)
* fix software and vaapi 10 bit content

* fix nvidia 10 bit content

* some qsv improvements
2024-03-04 15:29:45 -06:00
Jason DoveandGitHub 955add1efd fix av1 software decoder priority (#1632) 2024-03-03 22:29:25 -06:00
Jason DoveandGitHub 99cd01f73b update iptv routing (#1631) 2024-03-02 22:16:46 -06:00
Jason DoveandGitHub ef29e8c5a1 fix vaapi transcoding with a53 cc data (#1625) 2024-02-23 11:27:51 -06:00
Jason DoveandGitHub 3b4c993530 add xmltv time zone option (#1624) 2024-02-21 12:46:09 -06:00
Jason DoveandGitHub bcc58bd668 allow segmenter v2 playback on intel vaapi/qsv (#1623) 2024-02-20 15:45:59 -06:00
Jason DoveandGitHub 6957a76156 fix concat segmenter for nvidia and qsv (#1622) 2024-02-19 15:29:19 -06:00
Jason DoveandGitHub 4bafc316cc fix playlist for segmenter v2 (#1621) 2024-02-19 14:11:06 -06:00
Jason DoveandGitHub 35817f09ac add hls segmenter v2 streaming mode (#1620)
* concat segmenter process kind of works

* segmenter v2 improvements

* rework to allow hw accel in concat segmenter

* remove shortest; use different audio alignment filter

* hls v2 improvements

* fix tests

* update changelog
2024-02-19 13:59:33 -06:00
Jason DoveandGitHub f4520a5520 allow decimal image duration (#1619)
* add missing mysql migration

* allow decimal image duration
2024-02-16 20:01:40 -06:00
Jason DoveandGitHub 3a906816fc allow playback of items with duration of 1 second (#1618) 2024-02-16 13:41:52 -06:00
Jason DoveandGitHub 707292c50f add configurable image duration (#1617) 2024-02-16 13:05:28 -06:00
Jason DoveandGitHub 71e9ea867a store local library folder hierarchy in db (#1616) 2024-02-16 06:16:19 -06:00
Jason DoveandGitHub c490832f66 fix episode artwork in channel guide (#1613) 2024-02-14 20:21:46 -06:00
Jason DoveandGitHub e00568cc23 fix build error (#1612) 2024-02-13 10:51:17 -06:00
Jason DoveandGitHub 356e0f101a fix edge case where channel would fail to start (#1611) 2024-02-13 10:01:02 -06:00
Jason DoveandGitHub 1f6e843a26 fix segmenter timestamp continuity (#1610) 2024-02-12 08:50:18 -06:00
Jason DoveandGitHub 9587692486 optimize image playback (#1609) 2024-02-11 12:54:18 -06:00
Jason DoveandGitHub f8c4f44216 add basic image library support (#1608)
* add basic image library support

* add image page

* update changelog
2024-02-11 11:24:19 -06:00
Jason DoveandGitHub d55ba235bf evenly divide epg time for schedule blocks (#1607)
* add checkbox to include block items in program guide

* evenly divide epg time for schedule blocks
2024-02-10 20:59:29 -06:00
Jason DoveandGitHub 60b479e330 scanning fixes (#1606)
* fix music video fallback metadata

* properly re-scan song metadata
2024-02-10 06:44:27 -06:00
Jason DoveandGitHub b866d07911 fix song libraries using string collections (#1605) 2024-02-09 13:38:54 -06:00
Jason DoveandGitHub 93db79f8c4 load song comment (#1604) 2024-02-09 11:37:55 -06:00
Jason DoveandGitHub a15854d0ad more guide templates; new song metadata library (#1603)
* refactor template processing

* use template for song xmltv entries

* use template for other video xmltv entries

* update changelog
2024-02-09 11:27:44 -06:00
Jason DoveandGitHub c743d07425 include external subtitles in search index (#1602) 2024-02-07 13:28:43 -06:00
Jason DoveandGitHub 8c3b8e81ca separate request logging into its own category (#1601) 2024-02-07 08:40:31 -06:00
Jason DoveandGitHub 49050a57d2 load music video artists for channel guide template (#1600) 2024-02-06 10:46:34 -06:00
Jason DoveandGitHub 49c53c5c96 add stale issue workflow (#1599) 2024-02-05 22:00:57 -06:00
Jason DoveandGitHub 1510c56e69 generate music video xmltv fragment from template (#1598)
* generate music video xmltv fragment from template

* load all music video data
2024-02-05 19:56:19 -06:00
Jason DoveandGitHub 3ec610d65f properly encode xmltv fragments (#1597) 2024-02-05 18:15:38 -06:00
Jason DoveandGitHub 69f9b6f137 add channel guide templates (#1596)
* generate channels xmltv fragment from template

* generate movie xmltv fragment from template

* generate episode xmltv fragment from template

* add channel guide template changelog
2024-02-05 14:55:08 -06:00
Jason DoveandGitHub 08837bda80 properly categorize some existing streaming debug logs (#1595)
* properly categorize some existing streaming debug logs

* cleanup
2024-02-05 06:37:48 -06:00
Jason DoveandGitHub 9089e2ee04 add iptv request logging (#1594) 2024-02-04 21:34:41 -06:00
Jason DoveandGitHub abed22b560 prevent unnecessary search index updates (#1592)
* add sub_language and sub_language_tag fields to search index

* prevent unnecessary search index updates

* update changelog

* update dependencies
2024-02-02 06:33:06 -06:00
Jason DoveandGitHub e0f9ab4b88 batch search index updates (#1591) 2024-02-01 21:30:33 -06:00
Jason DoveandGitHub 231a214223 add new subtitle settings (#1590) 2024-02-01 11:45:22 -06:00
Jason DoveandGitHub 82bfa8019e tweak subtitle display in media info (#1589) 2024-01-31 19:17:04 -06:00
Jason DoveandGitHub d9bbe4df1b auto generate jwt token for channel preview (#1588) 2024-01-31 15:08:23 -06:00
Jason DoveandGitHub e0aa44d41b fix updating jellyfin movies (#1587) 2024-01-31 13:38:35 -06:00
Jason DoveandGitHub 3d99c2593d allow previewing jwt channels (#1586) 2024-01-31 12:03:04 -06:00
Jason DoveandGitHub d6dfc1edaa persist data protection keys in etv config folder (#1585) 2024-01-30 19:41:39 -06:00
Jason DoveandGitHub 7d5cd229d4 add show_studio search field (#1584) 2024-01-30 16:50:57 -06:00
Jason Dove cd0219c5c3 update changelog for release v0.8.5-beta [no ci] 2024-01-30 15:34:28 -06:00
Jason DoveandGitHub 4cf8b83de4 ignore subtitles when they are unavailable (#1583) 2024-01-30 14:29:13 -06:00
Jason DoveandGitHub 6923b25177 add more log level switches (#1582)
* label block and json playouts as experimental

* add more log level switches
2024-01-30 13:10:19 -06:00
Jason DoveandGitHub 5dce905b8e clear block playout items without clearing history (#1581) 2024-01-30 09:10:49 -06:00
Jason DoveandGitHub 46c26b5ea7 include all block playout items in xmltv (#1580)
* include all block playout items in xmltv

* double check whether channel preview will work
2024-01-30 06:44:19 -06:00
Jason DoveandGitHub 7fffc8cf63 channel preview player (#1579)
* add channel preview

* add button to stop transcoding session
2024-01-29 20:52:52 -06:00
Jason DoveandGitHub 18deff0b83 add session api endpoints (#1578) 2024-01-29 11:31:16 -06:00
Jason DoveandGitHub 16007a888e fix actions and changelog (#1576) 2024-01-27 10:14:53 -06:00
Jason DoveandGitHub 7eb1227ba4 fix action version (#1575) 2024-01-26 06:17:55 -06:00
Jason DoveandGitHub 1d1d5bf9bc use software overlay for intermittent watermark on nvidia (#1574)
* use software overlay for intermittent watermark on nvidia

* update some github action versions

* update changelog
2024-01-26 06:16:00 -06:00
Jason DoveandGitHub 45c04366c9 remove dynaudnorm filter (#1573) 2024-01-25 19:56:14 -06:00
Jason DoveandGitHub 60b3bc92f4 use super shuffle in block playouts (#1572) 2024-01-24 15:29:19 -06:00
Jason DoveandGitHub 12234c3e21 allow shuffling block items (#1571)
* allow shuffling block items

* fix drop down search results
2024-01-23 22:42:28 -06:00
Jason DoveandGitHub d37ce2d38a update xmltv channel list on channel edit (#1570) 2024-01-23 13:10:52 -06:00
Jason DoveandGitHub 6f49233864 fix image upload corruption (#1569) 2024-01-23 10:49:19 -06:00
WillandGitHub a67a6047c1 Update README.md (#1567)
Remove the link for Hardware-accelerated transcoding which was just linking back to itself
2024-01-22 14:33:16 -06:00
Jason DoveandGitHub 33f67b88f0 show chapter markers in media info (#1568) 2024-01-22 14:19:35 -06:00
Jason DoveandGitHub b88deaafe5 add tests, replace playout items when collections are updated (#1566) 2024-01-22 10:10:22 -06:00
Jason DoveandGitHub 83fc3081d8 add some logging around playlist trimming (#1565) 2024-01-22 05:47:00 -06:00
Jason DoveandGitHub 15d4b0f82b remove v2 ui and node (#1564) 2024-01-16 13:28:46 -06:00
Jason DoveandGitHub 88fac0de04 add option to stop scheduling before or after block duration end (#1563) 2024-01-16 12:53:56 -06:00
Jason DoveandGitHub 4805d0d40f add button to copy block item (#1562) 2024-01-16 10:30:50 -06:00
Jason DoveandGitHub ef3b941a39 fix mysql migration (#1561)
* clean up block playout preview logic

* fix some bugs with playout templates editor

* fix mysql migration
2024-01-16 05:40:29 -06:00
Jason DoveandGitHub a59f71039c preview block playout in block editor (#1558)
* block editor cleanup

* preview block playout

* cleanup
2024-01-15 19:39:18 -06:00
Jason DoveandGitHub 1ad42fffb1 fix mac builds (#1557) 2024-01-15 10:29:07 -06:00
Jason DoveandGitHub 2ce8db9e01 basic block duration enforcement (#1556) 2024-01-15 06:26:14 -06:00
Jason DoveandGitHub c409fd8b47 fix playout build hang for block playouts (#1555) 2024-01-14 19:40:29 -06:00
Jason DoveandGitHub 907b8074f1 allow more collection types and playback orders in blocks (#1554) 2024-01-14 12:51:45 -06:00
Jason DoveandGitHub adbd0bcec0 block schedule refactor (#1553)
* erase block playout history and items from playouts page

* remove block from template

* refactor block scheduling; improve history behavior
2024-01-14 10:22:04 -06:00
Jason DoveandGitHub 2c4379886a limit blocks to television shows and seasons (#1551) 2024-01-14 06:46:38 -06:00
Jason DoveandGitHub caef4a139e block scheduling skip unchanged blocks (#1550)
* schedule blocks in order

* block minutes must be multiple of 15

* improve block minutes entry, validation and display

* confirm deleting blocks and block groups

* confirm deleting templates and template groups

* skip unchanged blocks in playout
2024-01-14 06:16:53 -06:00
Jason DoveandGitHub dcbe4837bf first pass at block scheduling (#1548)
* add blocks, block groups

* basic block and block item editing

* add template groups and basic template editing (name)

* add blocks to template calendar

* edit playout templates

* add calendar preview to playout templates

* add basic block playout building

* add mysql migration

* update changelog
2024-01-13 22:01:21 -06:00
Jason DoveandGitHub 5e530b9301 fix scale behavior crop with qsv (#1546) 2024-01-12 13:21:49 -06:00
Jason DoveandGitHub 2a28bf68bf fix crop mode with nvidia accel (#1545) 2024-01-11 11:42:26 -06:00
Jason DoveandGitHub f39eac97c0 fix fill with group when show is also included individually (#1544) 2024-01-11 10:44:50 -06:00
Jason DoveandGitHub 9fd6589831 disambiguate seasons (#1543) 2024-01-11 09:08:52 -06:00
Jason DoveandGitHub e2a516f5e8 fix external json playouts with mysql (#1542) 2024-01-09 05:26:57 -06:00
Jason DoveandGitHub 64502315a3 generate xmltv for external json playouts (#1541) 2024-01-08 20:54:40 -06:00
Jason DoveandGitHub 56bc58fce9 reorganize to fix build (#1540) 2024-01-08 20:00:35 -06:00
Jason DoveandGitHub 0330b9326d add external json playout type for dizquetv interop (#1539)
* add external json playout

* basic local playback works

* fallback to streaming from plex

* update external json file

* update changelog
2024-01-08 19:45:43 -06:00
Jason DoveandGitHub 6708d6b4d7 support filling with groups of song artists (#1537) 2024-01-05 10:32:04 -06:00
Jason DoveandGitHub c18be5559b fix delete old segments (#1536)
* code cleanup

* ignore errors deleting old hls segments
2024-01-04 10:42:04 -06:00
Jason DoveandGitHub 18ed20e203 fix multiple zero when using fill with group (#1535) 2024-01-02 15:29:50 -06:00
Jason Dove 965c7d0eac update changelog [no ci] 2024-01-02 10:34:57 -06:00
Jason DoveandGitHub 545bf1b775 fill with group (#1534)
* use browser's accept-language header

* add fill with group mode to schedule items

* update dependencies

* fixes

* fix tests
2024-01-02 10:18:49 -06:00
Jason DoveandGitHub bb299d4ee7 maybe these don't need npm (#1533) 2023-12-30 20:13:11 -06:00
Jason DoveandGitHub 0e6c7d2bc3 fix npm in docker builds (#1532) 2023-12-30 20:03:29 -06:00
Jason DoveandGitHub 576f0cd7e7 more dotnet 8 fixes (#1530) 2023-12-30 13:47:45 -06:00
Jason DoveandGitHub 9471cb55dd upgrade from dotnet 7 to dotnet 8 (#1529)
* upgrade sdk

* fix warnings in ersatztv.ffmpeg

* fix warnings in ersatztv.core

* fix warnings in ersatztv.infrastructure

* fix warnings in ersatztv.application

* disable analysis for migrations projects

* fix warnings in ersatztv.scanner

* fix warnings in ersatztv

* upgrade project framework

* update github actions and dockerfiles
2023-12-30 13:29:57 -06:00
Jason DoveandGitHub 3a84af1626 update dependencies (#1527) 2023-12-27 04:44:30 -06:00
Jason DoveandGitHub 3d3bb64844 fix path replacements page with mysql (#1521) 2023-12-11 17:54:15 -06:00
Jason DoveandGitHub 8fc1f36638 use explorer to open logs folder on windows (#1520) 2023-12-05 18:28:10 -06:00
Jason Dove 1823a5bae5 update changelog for release v0.8.4-beta [no ci] 2023-12-02 16:45:33 -06:00
Jason DoveandGitHub fc871e6f74 fix detection of amf hw accel on windows (#1519) 2023-12-02 09:05:02 -06:00
Jason DoveandGitHub 24780cbe84 fix disappearing collection tags (#1517) 2023-11-30 20:31:37 -06:00
Jason DoveandGitHub c6ed258021 validate filler mode pad settings (#1516) 2023-11-26 12:54:06 -06:00
Jason DoveandGitHub 7586647b73 fix ffmpeg version health check on windows (#1515) 2023-11-23 06:05:49 -06:00
Jason Dove d91e945124 update changelog for release v0.8.3-beta [no ci] 2023-11-22 11:36:31 -06:00
Jason DoveandGitHub 9dabffbac1 support more formats for show fallback metadata (#1514) 2023-11-21 15:52:25 -06:00
Jason DoveandGitHub d310b5c09d fix nvidia hardware decoding on windows (#1513) 2023-11-21 06:36:05 -06:00
Jason DoveandGitHub ba48b3a676 update dependencies (#1512) 2023-11-20 21:57:43 -06:00
Jason DoveandGitHub d8a51b5d6d fix season display bug (#1511) 2023-11-20 21:17:11 -06:00
Jason DoveandGitHub 97674cff89 fix bug scheduling duration filler (#1510) 2023-11-20 21:02:26 -06:00
Jason DoveandGitHub 4820615308 proper fix to the sdk mismatch (#1509) 2023-11-16 13:37:20 -06:00
Jason DoveandGitHub 1ddf27ce88 pin dotnet sdk version used in github actions (#1508) 2023-11-16 13:21:51 -06:00
Jason DoveandGitHub cd98a89acd enable docker arm builds again (#1507) 2023-11-16 13:07:49 -06:00
Jason DoveandGitHub a2a6afc3e3 temp disable arm docker builds (#1506) 2023-11-16 09:58:46 -06:00
Jason DoveandGitHub dfaba8c7b0 use release version of ffmpeg 6.1 (#1505) 2023-11-16 09:57:13 -06:00
Jason DoveandGitHub 5d11a6b46f use separate model for plex collection scanning since the api types are inconsistent (#1504) 2023-11-16 06:43:48 -06:00
Jason DoveandGitHub b95a89b11f plex collection rework (#1503)
* start to rework plex collection scanning

* sync plex collections to db

* sync plex collection items

* update changelog
2023-11-14 10:41:21 -06:00
Jason DoveandGitHub 948b3735bd fix file not found music videos (#1502)
* fix indexing music videos in file not found state

* update dependencies
2023-11-14 05:50:51 -06:00
Jason DoveandGitHub 5ecf271773 fix jellyfin library scan (#1501)
* update dependencies

* fix jellyfin library scan
2023-11-10 06:26:23 -06:00
Jason DoveandGitHub b287c0d6ec add jellyfin season number fallback (#1497) 2023-11-06 09:37:12 -06:00
Jason DoveandGitHub b667659c05 use notarytool directly instead of gon (#1493) 2023-11-05 07:46:15 -06:00
Jason DoveandGitHub 22d3025e8e include noto cjk fonts in docker (#1492) 2023-11-05 06:15:57 -06:00
Jason DoveandGitHub 8f5b181372 mysql media server library scan fixes (#1491)
* fix some mysql movie library updates

* fix some mysql show library updates

* update dependencies
2023-10-30 06:45:00 -05:00
Jason DoveandGitHub f5060522aa windows nvidia h264 workaround (#1487)
* work around bad h264_cuvid behavior on windows with ffmpeg snapshot

* use latest ffmpeg build on windows

* nvdec => cuda
2023-10-16 11:40:12 -05:00
Jason DoveandGitHub 14a88bd225 optimize ffmpeg capability cache (#1486)
* minimize cached ffmpeg capabilities

* use set intersect

* try disabling work ahead on nvidia/windows
2023-10-16 08:42:26 -05:00
Jason DoveandGitHub 0550c60a78 allow older ffmpeg for testing (#1485)
* allow older ffmpeg for testing

* use proper option name
2023-10-14 21:13:18 -05:00
Jason DoveandGitHub d3bdcf9bc4 allow plex personal media show libraries (#1483) 2023-10-13 13:33:10 -05:00
Jason DoveandGitHub 714f68a887 add language_tag and seconds fields to search index (#1479)
* add `language_tag` and `seconds` fields to search index

* simplify
2023-10-10 20:36:50 -05:00
Jason DoveandGitHub 17bed524f2 fix ui display of multiple languages (#1474) 2023-10-08 18:21:48 -05:00
Jason DoveandGitHub c3fe263978 validate hardware accel, use hw accel for error messages (#1471)
* only display supported hw accels in ffmpeg profile editor

* qsv capability improvements

* qsv fixes

* update changelog
2023-10-08 11:21:04 -05:00
Jason DoveandGitHub 5291832e6c fix clipboard and logs (#1466)
* fix copy to clipboard in some cases

* improve subtitle language selection logging

* log playout item details
2023-10-06 19:36:42 -05:00
Jason DoveandGitHub b39dd693f0 update dependencies and windows ffmpeg (#1462)
* update dependencies

* update windows ffmpeg version
2023-10-05 19:14:06 -05:00
Jason DoveandGitHub 46bf9ef990 fix intel vaapi pgs subtitle pixel format (#1455) 2023-09-30 13:10:23 -05:00
Jason DoveandGitHub bc845b1327 schedule filler using ticks instead of milliseconds (#1454)
* add script to set db provider

* don't extract embedded subtitles with DEBUG_NO_SYNC

* fix playout filler precision bug
2023-09-30 06:41:15 -05:00
Jason DoveandGitHub 3ab8e5bc3a optimize jellyfin collection scanning (#1453) 2023-09-29 09:47:57 -05:00
Jason DoveandGitHub e8bc051f73 transcoding improvements (#1452)
* use noautoscale with vaapi encoder

* only use one input file for vaapi with radeonsi driver

* fix vaapi 8-bit to 10-bit

* fix nvidia subtitle scaling

* optimize nvidia subtitle scaling

* fix test pgs subtitle
2023-09-29 06:29:59 -05:00
Jason DoveandGitHub b008fcfd85 fix scheduling precision error (#1451)
* fix scheduling precision error

* update dependencies
2023-09-27 06:07:48 -05:00
Jason DoveandGitHub 547db5fb51 add kodiprop to channels.m3u (#1448) 2023-09-26 15:47:55 -05:00
Jason DoveandGitHub 58fae1b0cc add crop scaling behavior (#1443)
* add scaling behavior - crop

* fix ffmpeg version check on windows (snapshot)

* update dependencies
2023-09-22 08:23:49 -05:00
Jason DoveandGitHub 694b6bbd91 scaling behavior and normalize loudness (#1439)
* update changelog [no ci]

* add ffmpeg profile scaling behavior

* update dependencies

* add normalize loudness mode

* update changelog
2023-09-21 02:46:43 -05:00
Jason DoveandGitHub e0f8b7d7ae use ffmpeg 6.1 snapshot for windows (#1435) 2023-09-14 19:33:40 -05:00
Jason DoveandGitHub b16215fcd6 improve hls throttle (#1434)
* throttle using ffmpeg option

* update ffmpeg version
2023-09-14 19:28:15 -05:00
Jason Dove 85f2c658aa update changelog for release v0.8.2-beta [no ci] 2023-09-14 09:19:21 -05:00
Jason DoveandGitHub 78356314e6 update dependencies (#1433) 2023-09-14 08:46:59 -05:00
Jason DoveandGitHub b00a25bbee fix parsing show title from nfo (#1426) 2023-09-10 19:46:51 -05:00
4d77576be2 update dependencies (#1425)
Co-authored-by: Jason <jason@mbp-touch.local>
2023-09-10 09:22:35 -05:00
Jason DoveandGitHub a90348740d fix subsequent hls session work ahead (#1419) 2023-09-06 20:01:58 -05:00
Jason DoveandGitHub 8081845ef1 fix adding alternate schedule (#1418) 2023-09-04 14:06:39 -05:00
Jason DoveandGitHub d014eb4274 mysql ui fixes (#1417) 2023-09-04 13:12:00 -05:00
Jason DoveandGitHub 8c9cf7b6f2 fix mid roll pad; fix mysql queries (#1416)
* fix mysql queries

* fix mid roll pad with hls segmenter
2023-09-04 10:01:39 -05:00
Jason DoveandGitHub 5d9c8d4f7d update checkout actions (#1415) 2023-09-04 08:38:10 -05:00
Jason DoveandGitHub 82de3136cd fix hls session worker lifetime (#1414) 2023-09-04 08:30:01 -05:00
Jason DoveandGitHub b1cd324f9c fix docker builds (#1413) 2023-09-03 08:54:48 -05:00
Jason DoveandGitHub 5caf8f7f98 fix builds (#1412) 2023-09-03 08:52:00 -05:00
Jason DoveandGitHub 245c4ec359 code analysis and cleanup (#1411)
* cleanup scanner project

* cleanup infrastructure projects

* cleanup ffmpeg project

* cleanup core project

* cleanup app project

* cleanup main project

* update dependencies

* code cleanup
2023-09-03 06:23:42 -05:00
Jason DoveandGitHub 6414471ace fix emby movie libraries (#1410) 2023-09-02 13:03:48 -05:00
Jason DoveandGitHub 8b0b927a5c use d3d11va for qsv accel on windows (#1408) 2023-09-01 11:50:56 -05:00
Jason DoveandGitHub e5962699a4 schedule item editor updates (#1407) 2023-09-01 06:44:31 -05:00
Jason DoveandGitHub 27504e42bc fix mysql show queries (#1406)
* ignore artwork when sync is disabled

* fix delete playout

* fix some mysql queries
2023-09-01 06:05:36 -05:00
Jason DoveandGitHub deb0ac49b5 show plex server names in libraries page (#1402)
* cleanup plex libraries query

* show plex server names in libraries page
2023-08-29 08:46:36 -05:00
Jason DoveandGitHub 225b95449c rework hls session state (#1401) 2023-08-29 05:59:35 -05:00
Jason DoveandGitHub cb43c28d00 fix hls session when starting with very short content (#1400) 2023-08-29 05:01:13 -05:00
Jason DoveandGitHub 0a75136223 fix transcoding short content (#1399) 2023-08-28 22:01:38 -05:00
Jason Dove efc710749e fix test 2023-08-28 20:44:35 -05:00
Jason DoveandGitHub 8f241f49fc optimize transcoding speed (#1398)
* fix "empty trash" button blinking when loading trash page

* clean channel guide cache on startup

* only work-ahead in hls session for 2 minutes
2023-08-28 20:42:02 -05:00
Jason DoveandGitHub 20d224fcfd more mysql fixes (#1397)
* fix channels and movie page crashes with mysql

* update dependencies
2023-08-26 10:39:33 -05:00
Jason DoveandGitHub b3fda4e88d allow shared plex servers (#1391)
* allow shared plex servers

* update dependencies
2023-08-22 08:49:26 -05:00
Jason DoveandGitHub 560cb826b3 fix local subtitles display (#1388)
* show external subtitles in media info

* fix mysql saved page size
2023-08-19 11:26:52 -05:00
Jason DoveandGitHub b038a58fa2 fix saving smart collection (#1384) 2023-08-15 15:53:56 -05:00
Jason DoveandGitHub c28e201e47 fix adding shows directly to schedule (#1383) 2023-08-15 15:34:53 -05:00
Jason DoveandGitHub b84bb6b437 fix ui crashes (#1382)
* fix ffmpeg editor crash

* fix watermark editor

* fix multi collection editor

* fix filler preset editor crash
2023-08-15 06:39:29 -05:00
Jason DoveandGitHub 641b8bcd10 fix windows dependency (#1380) 2023-08-14 19:44:18 -05:00
Jason DoveandGitHub 4b08ed5381 fix mysql migration (initial data) (#1379) 2023-08-14 15:21:42 -05:00
Jason Dove 5486dcdcab remove docs files and workflow [no ci] 2023-08-13 20:16:49 -05:00
Jason DoveandGitHub 77b32b0f09 fix github repo links (#1378) 2023-08-13 09:59:48 -05:00
Jason Dove 2c6c08becf update submodule [no ci] 2023-08-13 09:33:33 -05:00
Jason DoveandGitHub 99bc19cf26 fix docker builds (#1376) 2023-08-12 22:02:45 -05:00
Jason DoveandGitHub a7661c8498 add mysql database provider (#1375)
* refactor sqlite into separate library

* support mysql

* fixes

* sql fixes

* cleanup

* update changelog
2023-08-12 21:44:14 -05:00
Jason DoveandGitHub d951035183 fix bulk removing items from elasticsearch index (#1374) 2023-08-12 08:51:12 -05:00
Jason DoveandGitHub 097c60169c elasticsearch relative queries (#1373)
* remove unused code

* fix relative queries with elasticsearch

* fix some double page loads

* simplify language model
2023-08-12 06:36:28 -05:00
Jason DoveandGitHub d64d8b0454 don't always rebuild elasticsearch index on startup (#1372) 2023-08-11 15:47:17 -05:00
Jason DoveandGitHub 7486304ed9 fix elasticsearch smart collection playouts (#1371) 2023-08-11 15:28:57 -05:00
Jason DoveandGitHub c62cc98c9f add elasticsearch search index provider (#1370)
* wip

* first pass at elasticsearch; movies kind of work

* use field name constants

* properly sort search results

* fix some crashes

* fix page map/jump letters

* optimize page map using terms aggregation

* index all item types

* optionally use elastic search

* code cleanup

* automatically rebuild lucene index after improper shutdown

* update changelog
2023-08-11 13:57:50 -05:00
Jason Dove 22a13cb1b3 Revert "add debug logs for other video folder scanning (#1369)"
This reverts commit 5e573461f3.
2023-08-10 12:51:47 -05:00
Jason DoveandGitHub 5e573461f3 add debug logs for other video folder scanning (#1369) 2023-08-10 11:26:48 -05:00
Jason DoveandGitHub 76c596a7d8 fix logs page (#1368)
* fix log viewer crash

* update dependencies
2023-08-10 10:45:43 -05:00
Jason DoveandGitHub f945f16d97 fix qsv subtitle scaling (#1367) 2023-08-10 10:16:10 -05:00
Jason DoveandGitHub 797d4005e2 replace moq with nsubstitute (#1365) 2023-08-09 20:14:18 -05:00
Jason Dove 55903430ae update changelog for release v0.8.1-beta [no ci] 2023-08-07 13:32:07 -05:00
Jason DoveandGitHub f929dc92d1 update dependencies; code cleanup (#1357)
* update dependencies

* code cleanup
2023-08-07 09:34:25 -05:00
Jason DoveandGitHub 2ad27c2be0 update dependencies (#1348)
* update dependencies

* silence mudblazor debug logs
2023-07-24 20:40:32 -05:00
Jason DoveandGitHub df2db5caf7 add plex file name logging (#1342) 2023-07-12 19:27:16 -05:00
Jason DoveandGitHub 5978e8ecb1 fix vaapi rate control mode (#1340) 2023-07-08 12:36:07 -05:00
Jason Dove a540efc2e1 add community to readme [no ci] 2023-07-03 13:07:42 -05:00
Jason DoveandGitHub 1938cef6ae add community link to docs (#1339) 2023-07-03 13:01:54 -05:00
Jason DoveandGitHub b23d798aff update dependencies (#1329) 2023-06-26 11:11:40 -05:00
Jason DoveandGitHub ebad7664b0 force hw accel to use one thread (#1327) 2023-06-25 09:56:58 -05:00
Jason DoveandGitHub a9c93ff498 add custom resolution management (#1326)
* update some dependencies

* add custom resolution management
2023-06-25 09:14:19 -05:00
Jason DoveandGitHub 8277894f7b show database and search index initialization in ui (#1325)
* unblock startup, show database initialization message

* wait on search index to be ready (rebuild)

* clean logging and fake delay
2023-06-24 09:12:46 -05:00
Jason DoveandGitHub 0d66f752b6 add global mutex to ensure single instance (#1324) 2023-06-24 06:30:55 -05:00
Jason Dove c128f72a54 update changelog for release v0.8.0 [no ci] 2023-06-23 22:16:26 -05:00
Jason DoveandGitHub 4af2d7aa61 don't trust emby's anamorphic flag (#1321) 2023-06-22 20:07:58 -05:00
Jason DoveandGitHub 20a6727158 fix vaapi hw decoding (#1320) 2023-06-22 15:05:02 -05:00
Jason DoveandGitHub 52e1874426 vaapi cqp rate control mode (#1319) 2023-06-22 11:42:11 -05:00
Jason DoveandGitHub 015f5e9798 fix playout build loop that was recently introduced (#1318) 2023-06-22 09:40:20 -05:00
Jason DoveandGitHub 1fc461e476 update dapper (#1316) 2023-06-21 15:51:35 -05:00
Jason DoveandGitHub 85792f0811 fix nvidia color normalization (#1314) 2023-06-20 09:23:41 -05:00
Jason DoveandGitHub 0f91a43e3f fix scaling subtitles with nvidia accel (#1313) 2023-06-20 06:25:15 -05:00
Jason DoveandGitHub 7a25996ab4 scale subtitles with all accels (#1311)
* properly scale subtitles with qsv and vaapi

* fixes
2023-06-19 15:55:23 -05:00
Jason DoveandGitHub 6985826072 add mpeg-ts output format for hls direct (#1310) 2023-06-19 11:19:19 -05:00
Jason DoveandGitHub 52482ef2fb only discard items with random or shuffle playback order (#1309) 2023-06-19 09:17:10 -05:00
Jason DoveandGitHub c148f2eb11 fix discard to fill calculation (#1308) 2023-06-17 05:11:08 -05:00
Jason DoveandGitHub d490cc6f4b dont give up on scheduling filler while some should fit (#1306) 2023-06-14 16:58:37 -05:00
Jason DoveandGitHub 99bd827bd9 fix multi episode shuffle (#1305) 2023-06-14 16:40:18 -05:00
Jason DoveandGitHub e8cbcc935f rework pad and duration filler (#1304) 2023-06-14 15:54:41 -05:00
Jason DoveandGitHub a2acfe4d80 add finish column to playout detail table (#1302) 2023-06-13 19:09:19 -05:00
Jason DoveandGitHub 5da2bdbab4 add duration discard to fill attempts (#1301) 2023-06-13 17:02:31 -05:00
Jason DoveandGitHub 66607b95bb update dependencies (#1300) 2023-06-13 13:58:44 -05:00
Jason DoveandGitHub 81a6251f65 properly lock playout before build (#1299) 2023-06-13 13:45:00 -05:00
Jason DoveandGitHub c554d83d60 playout management ui improvements (#1298) 2023-06-13 13:26:34 -05:00
Jason Dove 875010bbf4 update changelog for release v0.7.9-beta [no ci] 2023-06-10 10:40:05 -05:00
Jason DoveandGitHub c5692ef5f1 update dependencies (#1296) 2023-06-08 09:20:06 -05:00
Jason DoveandGitHub 147ab6143d hls direct mkv container (#1292)
* use mkv container for hls direct

* add setting for mp4/mkv container with hls direct

* cleanup

* update changelog
2023-06-06 10:21:09 -05:00
Jason DoveandGitHub aca441074e subtitle improvements with hls direct (#1290)
* wip: hls direct subtitles

* convert picture subtitles with hls direct

* use mp4 for hls direct to support more codecs

* disable subtitle conversion in hls direct

* fix tests

* update changelog
2023-06-04 12:29:47 -05:00
Jason DoveandGitHub ef6adf9cbb update dependencies (#1289) 2023-06-02 06:35:31 -05:00
Jason DoveandGitHub ddb7e1887f fix nvidia h264 decoder (#1281) 2023-05-22 21:26:48 -05:00
Jason DoveandGitHub 4997699b4d sync jf and emby episode actors (#1280) 2023-05-22 15:58:08 -05:00
Jason DoveandGitHub c27b906cd5 update docs (#1277)
* tweak mkdocs config; update install

* path replacement doc updates
2023-05-21 10:43:48 -05:00
Jason DoveandGitHub bec3cb864d update dependencies (#1276)
* update dependencies

* fix ide warnings

* tweak ef config
2023-05-21 10:13:44 -05:00
Jason DoveandGitHub 03df2a6c8a overdue code cleanup (#1271) 2023-05-10 13:18:18 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6142dcf153 Bump jetbrains.resharper.globaltools from 2022.1.0 to 2023.1.1 (#1264)
Bumps jetbrains.resharper.globaltools from 2022.1.0 to 2023.1.1.

---
updated-dependencies:
- dependency-name: jetbrains.resharper.globaltools
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-10 08:56:08 -05:00
Jason DoveandGitHub b287f791e6 fix pgs subtitle burn in from media servers (#1270) 2023-05-09 22:43:25 -05:00
Jason DoveandGitHub 2ccba9e476 timeout playout builds after 2 minutes (#1269)
* add cancellation token support to playout builds and collection enumerators

* fix playout bug with shuffle in order

* update changelog
2023-05-08 11:53:02 -05:00
Jason DoveandGitHub e215807e56 add worker service debug logs (#1267)
* add worker service debug logs

* update mudblazor
2023-05-05 08:42:33 -05:00
Jason DoveandGitHub b0333e89cd fix fallback filler (#1265) 2023-05-03 12:08:14 -05:00
Jason DoveandGitHub bc240a40e0 fix extracting text subtitles (#1262) 2023-04-29 21:46:20 -05:00
Jason Dove ed816e4b06 update changelog for release [no ci] 2023-04-29 20:48:24 -05:00
Jason DoveandGitHub 7c0f26ed3e update dependencies (#1261) 2023-04-29 09:42:06 -05:00
Jason Dove a54e37a648 proper fix 2023-04-26 19:35:40 -05:00
Jason DoveandGitHub c0656114b8 ignore dot underscore files (#1259) 2023-04-26 11:46:38 -05:00
Jason DoveandGitHub 7628ec7921 fix vobsub subtitle burn in from media server libraries (#1258) 2023-04-25 15:18:53 -05:00
Jason DoveandGitHub 30850329f3 fix external subtitle playback on windows (#1256) 2023-04-23 09:53:57 -05:00
Jason DoveandGitHub 6bb1c4299f fix colorspace filter for some files with invalid color metadata (#1254) 2023-04-20 11:59:31 -05:00
Jason DoveandGitHub 73c6758537 add show media info button to movie and episode detail pages (#1253) 2023-04-20 10:45:32 -05:00
ef1400d3f8 Corrected issue with channel delete (#1247)
* Channel delete now updates the guide cache.

* update changelog

---------

Co-authored-by: Jason Dove <jason@jasondove.me>
2023-04-14 19:37:46 -05:00
Jason DoveandGitHub 494142f026 fix media server text subtitle extraction (#1246) 2023-04-14 15:09:12 -05:00
Jason DoveandGitHub b89deffda3 cleanup delete channel handler syntax (#1245) 2023-04-14 12:53:33 -05:00
Jason DoveandGitHub ab55978732 add season, episode playback order for shows (#1243) 2023-04-13 11:06:48 -05:00
Jason DoveandGitHub b8dcd26e3a fix hls direct regression (#1242) 2023-04-12 15:47:25 -05:00
Jason DoveandGitHub 24f7544c9f improve schedule editor performance (#1240)
* rename

* async cleanup

* use autocomplete fields in schedule items editor
2023-04-11 14:16:39 -05:00
Jason DoveandGitHub afb2caa95d fix qsv watermark alpha (#1239) 2023-04-11 10:30:19 -05:00
Jason DoveandGitHub a684dcced9 scheduling bug fixes (#1238) 2023-04-11 09:22:51 -05:00
Jason DoveandGitHub cf1552910a limit hls direct streams to realtime speed (#1237) 2023-04-09 20:21:03 -05:00
Jason Dove e53e2b36cf update copyright in docs 2023-04-07 21:42:49 -05:00
Jason Dove 793e85f889 update changelog for release v0.7.7-beta [no ci] 2023-04-07 21:41:50 -05:00
Jason DoveandGitHub 126304bb8a fix subtitles from media server libraries (#1233)
* fix embedded subtitles from media servers

* fix plex external subtitles

* fix artwork bug, delete orphaned subtitles

* jellyfin subtitles work again

* emby subtitles work

* rescan all media server libraries
2023-04-07 13:22:45 -05:00
Jason DoveandGitHub 7b3b9b4aad fix search index bug (#1231) 2023-04-05 21:52:31 -05:00
Jason DoveandGitHub cf3b8d90e3 fix bt470bg color normalization using qsv (#1230) 2023-04-05 10:02:46 -05:00
Jason DoveandGitHub 109b244676 use software decoding for mpeg4 part2 with nvidia accel (#1229) 2023-04-05 08:48:58 -05:00
Jason DoveandGitHub 9f42333465 cache data for xmltv (#1228)
* cache channel list for xmltv

* used cached channel data for xmltv

* fixes

* update changelog
2023-04-03 23:09:54 -05:00
Jason DoveandGitHub c9141b0d86 fix colorspace filter when using vaapi (#1226) 2023-04-02 10:09:05 -05:00
Jason DoveandGitHub d2c4a58528 minor bug fixes (#1225) 2023-04-01 22:51:48 -05:00
Jason DoveandGitHub e93d678b97 add more logging (#1224)
* more logging

* update dependencies
2023-04-01 12:24:15 -05:00
Jason DoveandGitHub 307940d732 add duplicate file logging (#1223) 2023-04-01 09:54:19 -05:00
Jason DoveandGitHub 721f0df82a fix library scanning with non-english season folder names (#1222) 2023-03-31 13:37:54 -05:00
Jason DoveandGitHub aa87abc53d prioritize poster artwork for xmltv (#1221) 2023-03-29 08:18:03 -05:00
Jason DoveandGitHub 83d4aa0cb1 use other video plot as xmltv description (#1219) 2023-03-27 19:22:44 -05:00
Jason DoveandGitHub 46034aff54 fix updating trakt lists (#1218) 2023-03-25 05:58:41 -05:00
Jason Dove 3e447ac7e4 fix changelog links [no ci] 2023-03-24 14:41:10 -05:00
Jason Dove bda27faaa3 update changelog for release v0.7.6-beta [no ci] 2023-03-24 14:40:04 -05:00
Jason DoveandGitHub 80d89a2530 fix color normalization from bt470bg to bt709 (#1217) 2023-03-22 08:04:21 -05:00
Jason Dove e849ef5dfa remove bad file 2023-03-21 22:35:09 -05:00
Jason DoveandGitHub a26ecb91b8 ignore sidecar subtitle files from media server libraries (#1216) 2023-03-21 20:44:13 -05:00
Jason Dove 2853e13edc update changelog [no ci] 2023-03-17 10:05:28 -05:00
9ba0b844a1 JWT Query Parameter Auth for IPTV Links (#1215)
* JWT Auth

* Standardized url variable additions

* formatting and minor refactoring

* this isn't needed

* allow channel logos without auth

* update changelog

---------

Co-authored-by: Ministorm3 <4474921+Ministorm3@users.noreply.github.com>
2023-03-17 09:33:15 -05:00
Jason DoveandGitHub fdab54a055 limit console log output on windows (#1212) 2023-03-16 06:21:02 -05:00
Jason DoveandGitHub 7e0801119e maintain collection progress across alternate schedules (#1211) 2023-03-15 20:34:25 -05:00
Jason DoveandGitHub b2f7bcaf1e add more fields to music video credit templates (#1210) 2023-03-15 20:10:24 -05:00
Jason DoveandGitHub 71b8be37da restyle troubleshooting page (#1208) 2023-03-13 13:24:27 -05:00
Jason DoveandGitHub f7d19e3747 duration mode guide fixes (#1207)
* fix playout mode duration xmltv

* fix playout mode duration wrapping midnight
2023-03-13 09:00:59 -05:00
Jason DoveandGitHub 17dcbfc344 add troubleshooting page (#1206) 2023-03-12 13:00:54 -05:00
Jason DoveandGitHub 78745de0ca rework emby collection scanning (#1205)
* optimize emby collection scan frequency

* add button to sync emby collections

* update changelog

* fix scanning; add progress indicator
2023-03-11 22:29:10 -06:00
Jason DoveandGitHub 35445e2b3d proxy external subtitle files (#1203) 2023-03-09 19:31:59 -06:00
Jason DoveandGitHub bd2f0f6236 song normalization (#1202)
* add tests to verify song normalization

* simplify song setup, include watermarks and album art

* fix song path

* update changelog
2023-03-09 10:40:59 -06:00
Jason DoveandGitHub 4c67965b50 fix emby scanning (#1201) 2023-03-09 07:59:26 -06:00
Jason DoveandGitHub 234e93349b rework concurrency (#1199) 2023-03-08 21:23:18 -06:00
Jason DoveandGitHub e7e20de502 include multiple display-name entries in xmltv (#1198) 2023-03-07 09:53:00 -06:00
Jason DoveandGitHub dfc36b4581 case-insensitive file extensions in local scanner (#1197) 2023-03-06 18:21:34 -06:00
Jason DoveandGitHub c56e2526c4 fix media server scanning (#1196)
* fix media server scans

* update dependencies
2023-03-06 08:28:35 -06:00
Jason DoveandGitHub 8ff6bf652c fix jellyfin streaming and sar calculation (#1195) 2023-03-05 21:40:20 -06:00
Jason Dove a386fe9ba1 update changelog for release v0.7.5-beta [no ci] 2023-03-05 10:12:21 -06:00
Jason DoveandGitHub 4d84fc242b plex scanner improvement (#1193)
* fix crash with some plex multi-episode files

* comments cleanup
2023-03-03 06:08:42 -06:00
Jason DoveandGitHub 40e79a3a14 fix plex scanner crash (#1192) 2023-03-02 22:49:21 -06:00
Jason Dove c653bb32a7 plex scanner logging 2023-03-02 20:57:38 -06:00
Jason DoveandGitHub b032e70d7e support more local season folder names (#1191) 2023-03-02 20:13:34 -06:00
Jason DoveandGitHub 074816be50 simplify qsv accel syntax on linux (#1189) 2023-03-02 06:00:36 -06:00
Jason DoveandGitHub 3fafd5192f fix hevc_nvenc encoder on sm < 75 (#1187) 2023-03-01 20:17:58 -06:00
Jason DoveandGitHub 1d63197b56 fix yuv444p10le (#1186) 2023-03-01 19:56:14 -06:00
Jason DoveandGitHub b2c57e7407 upgrade to ffmpeg 6 (#1185) 2023-03-01 19:21:53 -06:00
Jason DoveandGitHub 581aa51792 fix trash display for certain episodes (#1184)
* fix trash display for certain episodes

* fix multi-episode fallback metadata
2023-02-28 09:37:30 -06:00
Jason DoveandGitHub 4d57ece30d check ffmpeg for available decoders, filters, encoders (#1183)
* check ffmpeg for available decoders, filters, encoders

* revert csproj change
2023-02-27 19:28:42 -06:00
Jason DoveandGitHub eddbf07b11 vaapi: decode vp9 and av1 (#1181) 2023-02-27 05:57:13 -06:00
Jason Dove 450ea063b4 update vaapi docker bundled ffmpeg version 2023-02-26 21:39:22 -06:00
Jason DoveandGitHub f320d84874 fix ffmpeg version health check for vaapi docker (#1179) 2023-02-25 08:18:49 -06:00
Jason DoveandGitHub c832c8e860 prioritize default audio tracks (#1178) 2023-02-24 15:45:30 -06:00
Jason DoveandGitHub e5ef8eaf72 fix some cases where vaapi hwdownload would fail; use libvpl in docker (#1177)
* fix some cases where vaapi hwdownload would fail

* update changelog
2023-02-24 09:10:42 -06:00
Jason DoveandGitHub 6db71f525d remove duplicate filter from search index (#1172) 2023-02-21 22:15:57 -06:00
Jason Dove 3ab66ef12a update intel media driver in vaapi docker image 2023-02-21 19:02:54 -06:00
Jason DoveandGitHub 018f759fa4 improve vaapi capability detection (#1171) 2023-02-21 09:53:23 -06:00
Jason DoveandGitHub 1afff11063 software decoder fixes (#1169)
* fix software decoder pipeline bugs

* tweak nvidia scaling logic

* update changelog

* update dependencies
2023-02-20 19:45:25 -06:00
Jason DoveandGitHub 7e3436e68f direct stream content from emby as needed (#1168) 2023-02-19 20:29:09 -06:00
Jason DoveandGitHub b751f1054b direct stream content from jellyfin if needed (#1167)
* redirect to jellyfin stream as needed

* get jellyfin playback info

* sync chapters from jellyfin

* update changelog

* cleanup
2023-02-19 10:37:48 -06:00
Jason DoveandGitHub 900e9e75f3 sync chapter markers from plex (#1166) 2023-02-18 14:51:51 -06:00
Jason DoveandGitHub 62c28d9f51 direct stream content from plex if needed (#1165)
* start to stream directly from plex

* update metadata and statistics with one plex api call

* stream movies from plex

* scanning bug fix; update changelog
2023-02-18 10:40:05 -06:00
Jason DoveandGitHub 132ca99f94 fix default dockerfile (#1156) 2023-02-13 05:38:11 -06:00
Jason Dove c309ab430e update changelog for release v0.7.4-beta [no ci] 2023-02-12 18:21:11 -06:00
Jason DoveandGitHub 13e21bbcce sync episode tags and genres (#1155)
* sync episode tags and genres

* update dependencies

* property update local episode genres and tags

* fix test
2023-02-12 09:53:20 -06:00
Jason DoveandGitHub 0eb36f0ce1 prioritize default audio streams (#1154) 2023-02-10 09:31:55 -06:00
Jason DoveandGitHub 6429f0f064 fix filler padding (#1153)
* fix filler padding

* update dependencies
2023-02-07 19:55:30 -06:00
Jason DoveandGitHub 7412ac6fc9 fix mid and post roll filler ordering (#1152) 2023-02-07 12:25:22 -06:00
Jason DoveandGitHub e58e3c786d fix last scan check (#1150) 2023-02-06 05:38:08 -06:00
Jason DoveandGitHub 93fc1e4eb4 fix fallback filler looping (#1146) 2023-02-04 08:49:52 -06:00
Jason DoveandGitHub cacde26796 merge other video folder tags with nfo tags (#1144) 2023-02-01 05:58:26 -06:00
Jason DoveandGitHub 0a3db92c60 fix schedule copy (#1142) 2023-01-30 10:23:18 -06:00
Jason DoveandGitHub 8bb0cd5ab5 add copy schedule feature (#1141) 2023-01-30 06:38:34 -06:00
Jason DoveandGitHub e497dc4e36 fix nvidia vp9 color normalization (#1140) 2023-01-29 16:02:29 -06:00
Jason DoveandGitHub 2689a67eb8 qsv and vaapi fixes (#1139)
* lots of qsv fixes

* update changelog

* fix qsv mpeg2

* vaapi fixes

* update changelog

* upgrade mudblazor

* fix bug with undefined input colorspace
2023-01-29 10:00:52 -06:00
Jason Dove 3d821043bb update changelog for v0.7.3-beta [no ci] 2023-01-25 12:01:38 -06:00
Jason DoveandGitHub e69c58e615 conditionally disable v2 apis (#1135)
* conditionally disable v2 apis

* update changelog
2023-01-25 11:37:43 -06:00
Jason DoveandGitHub a21b6f9f4e add oidc logout url to support auth0 (#1134) 2023-01-25 09:30:36 -06:00
Jason DoveandGitHub 99b8038852 add oidc support (#1133) 2023-01-25 08:37:59 -06:00
Jason DoveandGitHub ef8ca9f8c6 build mac artifacts on macos 11 (#1132) 2023-01-24 15:04:55 -06:00
Jason DoveandGitHub d9186df157 minor logging and doc updates (#1130) 2023-01-23 05:28:17 -06:00
Jason DoveandGitHub aca6bfb0bb fix multiple gcs after extracting subtitles (#1129) 2023-01-22 13:10:13 -06:00
Jason DoveandGitHub 587fc3a98f release memory after extracting embedded subtitles (#1128) 2023-01-22 12:34:42 -06:00
Jason DoveandGitHub ab1c67e60e memory improvements (#1127)
* regularly release memory

* don't aggressively GC while legacy streaming

* update changelog
2023-01-22 09:16:24 -06:00
Jason DoveandGitHub e271f43066 more scan check fixes (#1126) 2023-01-21 08:22:56 -06:00
Jason DoveandGitHub 6bf8feb26e fix local library scan check with new install (#1125) 2023-01-21 08:10:42 -06:00
Jason DoveandGitHub ffd66f6a21 fix removing media server libraries (#1124) 2023-01-20 09:31:18 -06:00
Jason DoveandGitHub 3b135df4c1 scan with below-normal priority when unforced (#1123) 2023-01-20 06:05:39 -06:00
Jason DoveandGitHub 4369d04940 scanner improvements (#1122)
* optimize periodic scanning

* set scanner process priority

* update dependencies
2023-01-20 05:37:39 -06:00
Jason Dove faaa78fed7 update changelog [no ci] 2023-01-18 15:40:00 -06:00
Jason DoveandGitHub 6bea1660ea disable mac compression; this is needed until dotnet 7.0.3 (#1120) 2023-01-18 15:13:07 -06:00
Jason DoveandGitHub 8d46676c25 try to fix mac scanning (#1119) 2023-01-18 14:43:26 -06:00
Jason DoveandGitHub 4c75e638a2 fix bug with smart collection progress (#1118) 2023-01-18 14:09:54 -06:00
Jason DoveandGitHub dd73a3803a fix schedule editor crash (#1115)
* fix schedule editor crash due to bad music video artist data

* update dependencies
2023-01-15 06:35:51 -06:00
Jason Dove f6c345d7cf fix build 2023-01-10 15:13:42 -06:00
Jason DoveandGitHub 585b56a668 bug fixes (#1107)
* don't search an empty search index

* fix bug with flood filler prediction check

* extract subtitles on primary worker thread
2023-01-10 14:45:04 -06:00
Jason Dove f18f3b4f35 try to fix develop artifacts 2023-01-09 08:46:55 -06:00
Jason DoveandGitHub eb7871a048 fix alternate schedule playout update check (#1106)
* fix alternate schedule playout update check

* Revert "use mknejp/delete-release-assets again"

This reverts commit 07ac833067.
2023-01-09 05:36:15 -06:00
Jason DoveandGitHub 000fc78fd3 add alternate schedule system (#1105)
* start to add program schedule alternates

* edit days of the week

* editor improvements

* save changes

* build playouts using alternate schedules

* reset playout as needed

* add priority message
2023-01-08 23:22:17 -06:00
Jason DoveandGitHub ba676ef956 add jellyfin admin error logging (#1102) 2023-01-07 09:55:02 -06:00
Jason DoveandGitHub 36ea88e2d6 fix error display (#1099)
* fix error display by ignoring hw accel setting

* update changelog

* revert background change
2023-01-05 20:02:48 -06:00
Jason Dove 5237e6fa50 update changelog for release v0.7.2-beta [no ci] 2023-01-05 11:51:57 -06:00
Jason DoveandGitHub 99bde1819c use mknejp/delete-release-assets again (#1098) 2023-01-05 10:26:42 -06:00
Jason Dove f5d7ec2890 update workflow [no ci] 2023-01-05 10:06:32 -06:00
Jason DoveandGitHub 13c65435d3 update dependencies (#1097) 2023-01-05 09:27:12 -06:00
Jason DoveandGitHub 315420f1a5 fix log viewer on windows (#1095)
* fix log viewer on windows

* catch cancellation on trakt page

* update changelog
2023-01-04 22:26:35 -06:00
Jason DoveandGitHub ab7051f075 reimplement log viewer (#1094) 2023-01-04 10:09:11 -06:00
Jason Dove a43e5bbe9d update changelog for release v0.7.1-beta [no ci] 2023-01-03 09:40:59 -06:00
Jason DoveandGitHub b7bd4541b1 hide windows on windows (#1091)
* hide windows on windows

* update dependencies
2023-01-03 09:14:50 -06:00
Jason Dove 648f25e9cc fix terminating server process 2023-01-01 14:20:52 -06:00
Jason Dove ccbe85a46a also hide the main server window 2023-01-01 14:12:41 -06:00
Jason DoveandGitHub d168d79fe0 hide windows wrapper console (#1088) 2023-01-01 13:42:39 -06:00
Jason Dove d37dde2477 try to fix windows build 2023-01-01 13:19:12 -06:00
Jason DoveandGitHub 8e13b07c84 convert windows project from dotnet to rust (#1087)
* convert windows project from dotnet to rust

* update pr jobs

* pr job fixes

* don't bother building mac app in prs for now

* build windows wrapper with rust
2023-01-01 13:01:58 -06:00
Jason DoveandGitHub 927e7724f0 fix search bug (#1086)
* fix removing media items from search index

* update changelog
2023-01-01 08:49:15 -06:00
Jason DoveandGitHub 6558c5bd69 fix subtitle update bug (#1085)
* fix saving some subtitles to database

* fix ffprobe regression
2022-12-31 19:57:14 -06:00
Jason DoveandGitHub 5f7efbb69c properly fall back to software pipeline (#1084) 2022-12-31 14:06:00 -06:00
Jason DoveandGitHub b79795af50 add debug logging to local subtitle provider (#1083) 2022-12-31 11:36:08 -06:00
Jason DoveandGitHub 9479806cb0 scanner refactoring and other cleanup (#1082)
* move subtitles provider into scanner

* move more stuff into scanner

* move nfo into scanner

* add scan subcommand

* fix a bunch of nfo build warnings

* more subcommands

* fix warnings

* cleanup logging

* remove unused code

* cleanup old ffmpeg stuff

* rename complex filter

* refactor wrapped segmenter
2022-12-31 10:57:20 -06:00
Jason DoveandGitHub 6e49ea78ec music video template contrib (#1081)
* add another music video template

* add more music video credit templates
2022-12-30 13:26:07 -06:00
Jason DoveandGitHub 7b1edd9c54 add new scanner process (#1080)
* start moving local scans to separate process

* send progress updates to main process

* move scanners and tests

* simplify dependencies; sync search index

* commit search index more often when scanning

* support forced scan and cancellation

* use scanner process for plex libraries

* update changelog

* update dockerfiles

* fix search index for local folder scanning

* rework plex scanners

* rework scanner handlers

* emby works again

* sync jellyfin

* cleanup

* update build

* update changelog

* remove scanner dependency in pr and artifacts workflows

* fix mac sed syntax

* fix pr build
2022-12-30 12:53:05 -06:00
Jason DoveandGitHub aeaafd2964 add scanner subtitle logging (#1079) 2022-12-29 06:00:00 -06:00
Jason DoveandGitHub 622fa01602 update dependencies and fix some types (#1077) 2022-12-28 14:21:25 -06:00
Jason DoveandGitHub e2b3c1ce8e properly unflag local movies that are now present on disk (#1076) 2022-12-28 13:41:01 -06:00
Jason DoveandGitHub 6c5db650e7 nvidia pixel format and song fixes (#1075)
* fix nvidia pixel format edge case

* fix nvidia song playback
2022-12-24 20:39:22 -06:00
Jason DoveandGitHub 731072425b fix nvidia pipeline that only requires setparams (#1074) 2022-12-24 13:19:27 -06:00
Jason DoveandGitHub 0f817308a8 limit library scan interval (#1073)
* limit library scan interval

* fix condition
2022-12-24 12:58:38 -06:00
Jason DoveandGitHub 0fc1e15cac colorspace fixes; song playback fixes (#1072)
* fix colorspace bug, vaapi song playback

* more colorspace fixes, nvidia fixes

* nvidia colorspace fixes

* fix some qsv output color metadata

* update changelog

* update changelog
2022-12-23 15:11:05 -06:00
Jason Dove acf30384b7 update changelog [no ci] 2022-12-20 20:14:31 -06:00
Jason DoveandGitHub d2040eaac9 pipeline fixes when colorspace filter is used (#1068)
* fix colorspace filter with missing input transfer or input primaries

* properly download before applying colorspace filter

* fix extra hwupload/hwdownload with nvidia pipeline

* colorspace tests

* update dependencies
2022-12-20 20:12:27 -06:00
Jason DoveandGitHub 93673fce03 add more logging to vaapi capabilities detection (#1059) 2022-12-15 19:32:48 -06:00
Jason Dove d7a432068b fix arm docker builds 2022-12-15 14:26:00 -06:00
Jason Dove cb9215980a fix dockerfiles, focal to jammy 2022-12-15 14:14:07 -06:00
Jason DoveandGitHub a4fc1f1c6f upgrade to dotnet 7, ffmpeg 5.1.2 (#1058)
* wip

* update dockerfiles

* more net6 to net7

* update dependencies

* update builds
2022-12-15 14:08:21 -06:00
Jason Dove cbbdb11938 update changelog for release v0.7.0-beta [no ci] 2022-12-11 06:53:05 -06:00
Jason DoveandGitHub a2274bca7b detect vaapi capabilities (#1051)
* remove unused pipeline

* spike vaapi hardware capabilities

* more vaapi capabilities

* use proper vaapi driver

* update readme

* update dependencies
2022-12-10 14:10:19 -06:00
Jason DoveandGitHub f84496b09d extract attached fonts (#1050) 2022-12-09 22:22:15 -06:00
Jason DoveandGitHub 3abf310a3b add amf pipeline (#1048) 2022-12-09 15:23:00 -06:00
Jason DoveandGitHub f12e361c2e fix videotoolbox color normalization (#1047) 2022-12-08 13:11:46 -06:00
Jason DoveandGitHub cd0f1e98cc fix qsv color normalization (#1046) 2022-12-08 08:17:59 -06:00
Jason DoveandGitHub 325ef80951 normalize bit depth via new pipeline (#1045)
* fix subtitles test and nvidia subtitles

* new ffmpeg pipelines; software and nvidia

* partial qsv

* fix qsv

* fix software pipeline

* add vaapi pipeline

* fix qsv 10-bit h264 output

* nvidia fixes

* properly disable 10-bit h264 hardware encoders

* more nvidia fixes

* add video toolbox pipeline
2022-12-07 21:25:55 -06:00
Jason DoveandGitHub 9a30d7c7da error report bug fixes (#1042)
* fix some potential null reference exceptions

* searching isn't actually async

* add search query breadcrumb
2022-12-03 05:47:04 -06:00
Jason DoveandGitHub 25ea75b761 more color fixes (#1040) 2022-11-25 21:25:04 -06:00
Jason DoveandGitHub 32edf77d35 fix bt709 check (#1039) 2022-11-25 10:09:58 -06:00
Jason DoveandGitHub 47fbb2b1b7 properly unlock trakt (#1035) 2022-11-23 18:34:55 -06:00
Jason DoveandGitHub e388f81e1f re-enable bugsnag auto notification (#1034) 2022-11-22 20:09:25 -06:00
Jason DoveandGitHub f0bea295c4 add video stats to search index (#1033) 2022-11-22 09:35:28 -06:00
Jason DoveandGitHub 7439ded43d normalize bit depth (#1031)
* normalize bit depth and color for nvenc

* fix hls direct

* update changelog

* add bit depth option to ffmpeg profile
2022-11-21 20:20:07 -06:00
Jason DoveandGitHub 6a640d3708 fix ogg song metadata (#1030) 2022-11-20 12:44:08 -06:00
Jason DoveandGitHub 776bce9087 use base path in channel playlist and channel guide (#1028) 2022-11-20 08:28:14 -06:00
Jason Dove 3c499f9e97 proper fix 2022-11-19 10:41:16 -06:00
Jason DoveandGitHub 114ff7a3e3 fix develop build cleanup (#1027) 2022-11-19 10:16:12 -06:00
Jason DoveandGitHub 527cdf523c fix work-ahead limit setting (#1023) 2022-11-16 21:17:23 -06:00
Jason Dove 91eb8ab824 try to fix develop release 2022-11-04 11:26:05 -05:00
Jason DoveandGitHub 7a87fb1c2e fix removing emby and jellyfin libraries (#1011)
* fix removing jellyfin and emby libraries

* remove unneeded change
2022-11-04 06:25:51 -05:00
Jason DoveandGitHub d8cc6b4c22 fix audio stream selection (#1010) 2022-11-02 14:55:43 -05:00
Jason DoveandGitHub c9bd94d9f8 use javascript instead of lua for external scripts; add audio stream selector scripts (#1005)
* use js instead of lua

* update dependencies

* add audio stream selector script for episodes

* add audio stream selector script for movies

* update changelog
2022-10-28 17:05:07 -05:00
Jason Dove 93bf818882 fix syntax [no ci] 2022-10-22 11:20:42 -05:00
Jason Dove 723fb3848d try to fix release by skipping unnecessary step 2022-10-22 11:19:39 -05:00
Jason Dove 6a213e2249 update changelog for release v0.6.9-beta [no ci] 2022-10-21 21:15:43 -05:00
Jason Dove a6c5c3a317 bump search index version 2022-10-21 15:21:33 -05:00
Jason DoveandGitHub 9313d2c8eb fix guide mode filler in xmltv (#1000) 2022-10-16 13:23:26 -05:00
Jason DoveandGitHub 485a874ab5 fix automatic playout reset (#999) 2022-10-16 10:30:54 -05:00
Jason DoveandGitHub f2bc884632 fix x-forwarded-proto (#998) 2022-10-13 13:16:51 -05:00
Jason DoveandGitHub 39d6653f8e temporarily enable http logging (#997) 2022-10-13 12:58:15 -05:00
Jason DoveandGitHub 2ce0fcb264 proxy server improvements (#996) 2022-10-13 12:17:13 -05:00
Jason DoveandGitHub 8bf5e18ae5 fix nfo reader (#995)
* fix nfo reader

* fix template fade

* update dependencies
2022-10-13 05:08:41 -05:00
Jason DoveandGitHub 88f4d8074a fix stream_seek type (#988)
* fix stream seek type

* cleanup
2022-10-09 21:20:34 -05:00
Jason DoveandGitHub f5aa2fcac8 add multi-episode shuffle playout order (#987) 2022-10-09 10:49:07 -05:00
Jason DoveandGitHub 6f892bea6b optionally place watermark within source content (#986) 2022-10-09 08:16:59 -05:00
Jason Dove cbf0c9c988 fix transcoding tests 2022-10-08 21:41:49 -05:00
Jason DoveandGitHub 393c67213d add stream_seek to music video credits template (#985) 2022-10-08 19:45:24 -05:00
Jason DoveandGitHub f69de9f071 fix all_artists in music video credits template (#984) 2022-10-08 12:40:46 -05:00
Jason DoveandGitHub 2e400c0d22 simplify music video credits config (#983) 2022-10-08 07:52:57 -05:00
Jason DoveandGitHub 6035c10550 add music video credits template system (#982)
* add music video credits template system

* fix search index bug

* cleanup csproj
2022-10-07 21:02:29 -05:00
Jason DoveandGitHub 555b156154 fix tail and fallback filler scheduling (#981) 2022-10-07 09:21:19 -05:00
Jason Dove a0ea2e8910 update changelog for release v0.6.8-beta [no ci] 2022-10-05 11:09:03 -05:00
Jason DoveandGitHub 734ca39cbd add guids to search index (#980) 2022-10-04 19:52:19 -05:00
Jason DoveandGitHub e0e5cfada5 fix numeric range search queries (#979) 2022-10-04 18:49:35 -05:00
Jason DoveandGitHub 7e0c43bc46 update dependencies (#977) 2022-10-01 07:56:14 -05:00
Jason DoveandGitHub be1125a9ab properly sync updated file paths from plex (#976) 2022-09-30 20:41:32 -05:00
Jason DoveandGitHub d21c985a77 fix emby tag sync for movies and shows (#975) 2022-09-30 19:56:19 -05:00
Jason DoveandGitHub 28f2b9b27e disable anamorphic edge case in scaling calculations (#971) 2022-09-26 15:11:05 -05:00
Jason DoveandGitHub 9b185e19e9 fix other video search results when nfo metadata is used (#970)
* add debug no sync build config

* fix other video search results

* update dependencies
2022-09-25 22:27:34 -05:00
Jason DoveandGitHub 27b923b462 qsv and vaapi scaling fixes (#966)
* add qsv device option to ffmpeg profile

* fix vaapi scaling

* cleanup
2022-09-18 21:02:09 -05:00
Jason DoveandGitHub 357dfee050 nvidia and software mode scaling improvements (#965)
* convert to square pixels before software scaling

* convert to square pixels in nvidia scale filter

* more scaling fixes; position watermark within padded content

* fix image subtitle scaling

* fix qsv scaling

* update dependencies
2022-09-18 11:04:02 -05:00
Jason DoveandGitHub 7f4004c228 fix qsv hevc encoder (#956)
* update dependencies

* fix typo in qsv hevc encoder param

* update changelog
2022-09-10 15:44:59 -05:00
Jason Dove 9b8dc0ed80 update changelog for release v0.6.7-beta [no ci] 2022-09-05 13:15:04 -05:00
Jason DoveandGitHub 3cc1286271 include other videos (ungrouped) in shuffle in order (#953)
* include other videos (ungrouped) in shuffle in order

* fix id conflict
2022-09-05 09:20:11 -05:00
Jason DoveandGitHub df281758b7 properly fix infinite playout build loop (#952) 2022-09-05 08:41:39 -05:00
Jason DoveandGitHub 25273c18c8 stop infinite playout building loop (#951) 2022-09-04 20:28:28 -05:00
Jason DoveandGitHub f1be945423 add qsv extra hardware frames setting (#950)
* wip add qsv extra_hw_frames setting

* fix ffmpeg profile editor

* update changelog
2022-09-04 18:07:03 -05:00
Jason DoveandGitHub 9a4f772f53 fix image subtitle scaling (#949)
* properly scale image-based subtitles for nvidia and software

* fix vaapi image subtitle scaling

* fix qsv image subtitle scaling

* update changelog
2022-09-04 14:25:05 -05:00
Jason DoveandGitHub d669e8114b more scaling fixes (#948)
* remove force_original_aspect_ratio from scale_cuda

* remove force_original_aspect_ratio from scale_cuda

* fix qsv scaling

* fix qsv scaling on linux

* fix vaapi scaling edge cases

* update changelog
2022-09-03 20:28:18 -05:00
Jason DoveandGitHub 3972e3603b add amf acceleration (#947) 2022-09-03 10:39:54 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
acc22fcb62 Bump MudBlazor from 6.0.14 to 6.0.15 (#945)
Bumps [MudBlazor](https://github.com/MudBlazor/MudBlazor) from 6.0.14 to 6.0.15.
- [Release notes](https://github.com/MudBlazor/MudBlazor/releases)
- [Changelog](https://github.com/MudBlazor/MudBlazor/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/MudBlazor/MudBlazor/compare/v6.0.14...v6.0.15)

---
updated-dependencies:
- dependency-name: MudBlazor
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 19:41:34 -05:00
Jason DoveandGitHub 2df360d7fb fix xmltv filler bug (#944) 2022-08-31 20:15:25 -05:00
Jason DoveandGitHub 46331ed2c6 add preferred audio title feature (#943)
* use consistent edit/delete icons

* add preferred audio title feature

* update dependencies
2022-08-30 17:04:41 -05:00
Jason Dove 3aee3b0515 fix windows build 2022-08-26 09:07:46 -05:00
Jason DoveandGitHub 72c45692b2 update dependencies (#937) 2022-08-26 08:52:59 -05:00
Jason DoveandGitHub 8edf71ca55 downgrade libva, include vainfo in docker (#936) 2022-08-25 16:29:20 -05:00
Jason DoveandGitHub 612b9e6524 fix scanner crash caused by invalid mtime (#934) 2022-08-20 19:56:37 -05:00
Jason DoveandGitHub 7aff65f07b explicitly copy all audio streams with hls direct (#933)
* ensure audio streams are always copied with hls direct

* update changelog
2022-08-18 14:23:51 -05:00
Jason Dove 5d350fcfad update changelog for release v0.6.6-beta [no ci] 2022-08-17 20:40:50 -05:00
Jason DoveandGitHub 5546ad204c upgrade to ffmpeg 5.1 (#931)
* use ffmpeg 5.1 on windows

* remove some debug logs

* use latest ffmpeg on arm

* use ffmpeg 5.1 base images

* update ffmpeg health check for 5.1

* update changelog
2022-08-17 14:57:23 -05:00
Jason DoveandGitHub d66efa0a1d prioritize container aspect ratio over stream aspect ratio (#930)
* prioritize container aspect ratio over stream aspect ratio

* use setdar filter
2022-08-16 19:57:38 -05:00
Jason DoveandGitHub 36d3d38530 remove all use of setsar filter (#928) 2022-08-16 12:25:59 -05:00
Jason DoveandGitHub 8e79141860 use multi-variant playlists for hls segmenter (#926)
* use multi-variant playlists for hls segmenter

* use lowercase mime type
2022-08-13 19:58:08 -05:00
Jason DoveandGitHub 9b3545f7ca add some temporary debug logging (#925) 2022-08-13 19:19:47 -05:00
Jason DoveandGitHub 56db20faa0 limit segmenter delay to 8s (#924)
* always return initial hls playlist after 8 seconds

* update dependencies

* make fluentvalidation happy
2022-08-12 19:48:24 -05:00
Jason DoveandGitHub b0bd4c9fed add ogg file formats to local song library scanner (#914)
* add ogg file formats to local song library scanner

* update dependencies
2022-08-04 16:01:17 -05:00
Jason DoveandGitHub ba079452e2 add dff and dsf to local song library scanner (#911) 2022-08-03 11:01:13 -05:00
Jason Dove f0f2b3da4b update changelog for release v0.6.5-beta [no ci] 2022-08-02 07:36:45 -05:00
Jason DoveandGitHub 866049543c fix db initializer (#907) 2022-07-31 12:30:33 -05:00
Jason Dove 40ed4b8b0e update changelog for release v0.6.4-beta [no ci] 2022-07-28 12:23:33 -05:00
Jason DoveandGitHub b43d08ca67 fix repeating schedules (#901) 2022-07-26 13:04:05 -05:00
Jason DoveandGitHub 5e7e386108 fix search result filtering for episodes and other videos (#900) 2022-07-25 20:02:07 -05:00
Jason DoveandGitHub 4176df9940 fix nvidia capabilities for second-gen maxwell (#899) 2022-07-24 12:23:30 -05:00
Jason DoveandGitHub de2ef959fe add 640x480 resolution (#898)
* update dependencies

* add 640x480 resolution
2022-07-24 08:17:27 -05:00
Jason DoveandGitHub b53cfebac1 fix bug with unsupported aac channel layouts (#893)
* fix bug with unsupported aac channel layouts

* update dependencies
2022-07-14 10:52:25 -05:00
Jason DoveandGitHub 6895b9cc6b fix search repo caching bug (#886)
* add failing test

* fix search repo bug

* update dependencies
2022-07-10 15:32:06 -05:00
Jason Dove c60d6e46f1 fix changelog [no ci] 2022-07-04 15:23:26 -05:00
Jason Dove c66d190174 update changelog for release v0.6.3-beta [no ci] 2022-07-04 15:20:53 -05:00
Jason DoveandGitHub 5e8da591be update dependencies (#883)
* fix database initialization

* update dependencies
2022-07-02 20:42:07 -05:00
Jason DoveandGitHub 9c02a6738b fix missing trashed episodes (#881)
* fix episodes missing from trash

* cleanup
2022-06-29 15:01:49 -05:00
Jason DoveandGitHub 5ed0184bca add minimum log level setting (#877) 2022-06-27 10:29:04 -05:00
Jason DoveandGitHub ae64ca4a93 fix arm images by using ls55 (#876) 2022-06-26 17:41:39 -05:00
Jason DoveandGitHub c47099895e include item state in search index duplicate filter (#875) 2022-06-26 13:34:54 -05:00
Jason Dove a2529febba use brew for gon 2022-06-26 08:30:32 -05:00
Jason Dove 521e0ba8b3 get a new build 2022-06-26 06:46:30 -05:00
Jason Dove ee0efac9be only publish docs when docs are updated 2022-06-26 06:21:27 -05:00
Jason DoveandGitHub bfe7635489 work around github actions issue on mac (#874) 2022-06-25 19:30:21 -05:00
Jason DoveandGitHub aa1735f024 fix song and other video search index (#873) 2022-06-25 18:13:39 -05:00
Jason DoveandGitHub 8deae983c7 add some startup log messages (#872) 2022-06-25 13:03:37 -05:00
Jason DoveandGitHub f349646703 apply plex episode metadata updates (#871)
* update more plex episode metadata

* update dependencies
2022-06-22 19:41:05 -05:00
Jason DoveandGitHub 5003e80500 maintain stream continuity after playout reset (#868)
* maintain stream continuity after playout reset

* maintain continuity after error streams
2022-06-18 21:38:25 -05:00
Jason Dove 940d9cd6b5 update changelog for release v0.6.2-beta [no ci] 2022-06-18 13:46:45 -05:00
Jason DoveandGitHub 197c166789 fix jellyfin admin id selection (#867) 2022-06-17 18:25:22 -05:00
Jason DoveandGitHub d114db091e use proper nvidia accel output format for 10-bit content (#865) 2022-06-17 11:33:10 -05:00
Jason DoveandGitHub 3204da8e43 adjust nvidia capabilities (#864)
* adjust nvidia capabilities logic

* fallback to software encoding for 10-bit h264

* cleanup

* more tweaks
2022-06-17 10:50:36 -05:00
Jason DoveandGitHub 100eb14408 fix epg sorting (#863)
* fix epg sorting

* update dependencies
2022-06-17 08:44:26 -05:00
Jason DoveandGitHub 025017ace5 regularly delete old segments (#856)
* regularly delete old segments

* cleanup
2022-06-15 21:12:07 -05:00
Jason DoveandGitHub 6a690c7c10 add more filler logging (#854) 2022-06-15 10:21:05 -05:00
Jason DoveandGitHub dd7f77751c detect nvidia capabilities (#853)
* fallback to software codecs for old nvidia cards

* update dependencies
2022-06-14 19:44:34 -05:00
Jason DoveandGitHub 0c13b8ef1a force amd64 for arm32v7 sdk build layer (#843) 2022-06-12 13:54:55 -05:00
Jason DoveandGitHub c6ca58ab97 build arm32v7 docker image (#842)
* build arm32v7 docker image

* fix
2022-06-12 13:42:27 -05:00
Jason DoveandGitHub 0846fc1d96 update workflow dependencies (#841) 2022-06-11 13:40:47 -05:00
Jason DoveandGitHub e41dd68ee0 fix automatic playout building (#840) 2022-06-11 13:11:04 -05:00
Jason DoveandGitHub 0a92996da8 fix repeating content (#838)
* fix repeating content

* update dependencies
2022-06-08 10:37:56 -05:00
Jason Dove 082bc6145c update changelog for release v0.6.1-beta [no ci] 2022-06-03 15:05:06 -05:00
Jason DoveandGitHub bf3f16451b music video credits tweaks (#834)
* fix song subtitles

* always use generated subtitles

* file not found/unavailable fixes
2022-06-03 14:44:52 -05:00
3cb37003cb UI rewrite - ffmpeg profiles (#823)
* ffmpeg profile functionality, sweetalert2

* add new files

* cleanup controller; remove unused classes

* apply formatting

* cleanup core project

* don't use bom

* whitespace

* remove generated css

* remove generated js/map

* Remove attempted linter fix, channels button, watermarks page. Fixed handlerror.

* Changed deleted confirmation to toast.

* Localized strings for language change. Modified Action icons to buttons and left default sizes. Changed Cancel to No where Yes is an option

* lint

Co-authored-by: Jason Dove <jason@jasondove.me>
2022-06-03 06:28:32 -05:00
Jason DoveandGitHub 9acfd2cd06 fix plex server identification (#833) 2022-06-03 05:53:41 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3242e7ebb8 Bump HtmlSanitizer from 7.1.488 to 7.1.509 (#830)
Bumps [HtmlSanitizer](https://github.com/mganss/HtmlSanitizer) from 7.1.488 to 7.1.509.
- [Release notes](https://github.com/mganss/HtmlSanitizer/releases)
- [Commits](https://github.com/mganss/HtmlSanitizer/compare/v7.1.488...v7.1.509)

---
updated-dependencies:
- dependency-name: HtmlSanitizer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-02 21:21:17 -05:00
Jason DoveandGitHub 7644d628e7 generate music video credits (#832) 2022-06-02 20:50:55 -05:00
Jason DoveandGitHub b4f19e6de4 fix jellyfin tv paging (#831) 2022-06-02 12:16:59 -05:00
Jason Dove 0388425763 update changelog for release v0.6.0-beta [no ci] 2022-06-01 18:01:07 -05:00
Jason DoveandGitHub ca5d303ac7 fix qsv encoder regression and memory errors (#828)
* fix qsv encoders; only use 64 extra hw frames

* update changelog
2022-05-31 06:07:50 -05:00
Jason DoveandGitHub 18e66a92ad add paging to media server show and collection calls (#827)
* add paging to media server show library calls

* add paging to media server season and episode library calls

* formatting

* add paging to media server collection calls

* add paging to media server collection item calls

* update changelog
2022-05-31 05:56:48 -05:00
Jason DoveandGitHub 7d0a56ab98 disable lower-power mode for qsv encoders (#826) 2022-05-29 20:44:27 -05:00
Jason Dove 5069792d12 update dependencies 2022-05-28 20:41:54 -05:00
Jason Dove c9789458b9 page media server movie libraries 2022-05-28 20:41:22 -05:00
Jason DoveandGitHub 777a0d09ed hls segmenter fixes (#824)
* fix pts warning when channel first starts streaming

* rework playlist filtering
2022-05-25 21:05:55 -05:00
Jason DoveandGitHub 4e2ebcc48a fix watermark opacity filter (#820) 2022-05-23 10:29:04 -05:00
Jason DoveandGitHub 90fe1d7709 fix hw accel health check for qsv in vaapi docker (#818) 2022-05-22 19:42:14 -05:00
Jason DoveandGitHub 1576dd026e enable qsv accel for vaapi docker images (#817) 2022-05-22 18:43:24 -05:00
Jason DoveandGitHub ee7a64eea9 fix other video libraries (#816)
* update depdendencies

* reset other video libraries
2022-05-22 12:29:35 -05:00
2744 changed files with 1129161 additions and 50009 deletions
+3 -8
View File
@@ -3,16 +3,11 @@
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
"version": "2022.1.0",
"version": "2025.1.4",
"commands": [
"jb"
]
},
"swashbuckle.aspnetcore.cli": {
"version": "5.6.2",
"commands": [
"swagger"
]
],
"rollForward": false
}
}
}
+7 -2
View File
@@ -1,8 +1,8 @@
[*]
charset=utf-8-bom
charset=utf-8
end_of_line=lf
trim_trailing_whitespace=false
trim_trailing_whitespace=true
insert_final_newline=false
indent_style=space
indent_size=4
@@ -83,3 +83,8 @@ tab_width=4
[*.yml]
indent_style = space
indent_size = 2
[*.cs]
# disable CA1848: Use the LoggerMessage delegates`
dotnet_diagnostic.ca1848.severity = none
+8
View File
@@ -0,0 +1,8 @@
# Auto detect text files and perform LF normalization
* text=auto
*.cs text diff=csharp
*.cshtml text diff=html
*.csx text diff=csharp
*.sln text eol=crlf
*.csproj text eol=crlf
+38 -47
View File
@@ -33,33 +33,23 @@ jobs:
strategy:
matrix:
include:
- os: macos-latest
- os: macos-14
kind: macOS
target: osx-x64
- os: macos-latest
- os: macos-14
kind: macOS
target: osx-arm64
steps:
- name: Get the sources
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache NPM dependencies
uses: bahmutov/npm-install@v1.4.5
with:
working-directory: ErsatzTV/client-app
dotnet-version: 9.0.203
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
@@ -68,7 +58,7 @@ jobs:
run: dotnet restore -r "${{ matrix.target}}"
- name: Import Code-Signing Certificates
uses: Apple-Actions/import-codesign-certs@v1
uses: Apple-Actions/import-codesign-certs@v2
with:
p12-file-base64: ${{ secrets.apple_developer_certificate_p12_base64 }}
p12-password: ${{ secrets.apple_developer_certificate_password }}
@@ -81,7 +71,10 @@ jobs:
- name: Build
shell: bash
run: dotnet publish ErsatzTV/ErsatzTV.csproj --framework net6.0 --runtime "${{ matrix.target }}" -c Release -o publish -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
run: |
sed -i '' '/Scanner/d' ErsatzTV/ErsatzTV.csproj
dotnet publish ErsatzTV.Scanner/ErsatzTV.Scanner.csproj --framework net9.0 --runtime "${{ matrix.target }}" -c Release -o publish -p:RestoreEnablePackagePruning=true -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=false -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net9.0 --runtime "${{ matrix.target }}" -c Release -o publish -p:RestoreEnablePackagePruning=true -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=false -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
- name: Bundle
shell: bash
@@ -94,7 +87,7 @@ jobs:
- name: Sign
shell: bash
run: scripts/macOS/sign.sh
- name: Create DMG
shell: bash
run: |
@@ -108,18 +101,16 @@ jobs:
--icon "ErsatzTV.app" 200 190 \
--hide-extension "ErsatzTV.app" \
--app-drop-link 600 185 \
--skip-jenkins \
--no-internet-enable \
"ErsatzTV.dmg" \
"ErsatzTV.app/"
- name: Notarize
shell: bash
run: |
curl -o gon.zip -L -s "https://github.com/mitchellh/gon/releases/latest/download/gon_macos.zip"
unzip -o -q gon.zip
./gon -log-level=debug -log-json ./gon.json
env:
AC_USERNAME: ${{ secrets.ac_username }}
AC_PASSWORD: ${{ secrets.ac_password }}
xcrun notarytool submit ErsatzTV.dmg --apple-id "${{ secrets.ac_username }}" --password "${{ secrets.ac_password }}" --team-id 32MB98Q32R --wait
xcrun stapler staple ErsatzTV.dmg
- name: Cleanup
shell: bash
@@ -130,6 +121,7 @@ jobs:
- name: Delete old release assets
uses: mknejp/delete-release-assets@v1
if: ${{ inputs.release_tag == 'develop' }}
with:
token: ${{ secrets.gh_token }}
tag: ${{ inputs.release_tag }}
@@ -140,7 +132,7 @@ jobs:
- name: Publish
uses: softprops/action-gh-release@v1
with:
prerelease: true
prerelease: false
tag_name: ${{ inputs.release_tag }}
files: |
${{ env.RELEASE_NAME }}.dmg
@@ -158,8 +150,11 @@ jobs:
target: linux-x64
- os: ubuntu-latest
kind: linux
target: linux-arm
target: linux-musl-x64
- os: ubuntu-latest
kind: linux
target: linux-arm
- os: ubuntu-24.04-arm
kind: linux
target: linux-arm64
- os: windows-latest
@@ -167,24 +162,14 @@ jobs:
target: win-x64
steps:
- name: Get the sources
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Cache NPM dependencies
uses: bahmutov/npm-install@v1.4.5
with:
working-directory: ErsatzTV/client-app
dotnet-version: 9.0.203
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
@@ -192,12 +177,12 @@ jobs:
- name: Install dependencies
run: dotnet restore -r "${{ matrix.target }}"
- uses: suisei-cn/actions-download-file@v1
- uses: suisei-cn/actions-download-file@v1.3.0
if: ${{ matrix.kind == 'windows' }}
id: downloadffmpeg
name: Download ffmpeg
with:
url: "https://github.com/GyanD/codexffmpeg/releases/download/5.0/ffmpeg-5.0-full_build.7z"
url: "https://github.com/ErsatzTV/ErsatzTV-ffmpeg/releases/download/7.1.1/ffmpeg-n7.1.1-56-gc2184b65d2-win64-gpl-7.1.zip"
target: ffmpeg/
- name: Build
@@ -208,11 +193,18 @@ jobs:
echo "RELEASE_NAME=${release_name}" >> $GITHUB_ENV
# Build everything
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net6.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name" -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
sed -i '/Scanner/d' ErsatzTV/ErsatzTV.csproj
dotnet publish ErsatzTV.Scanner/ErsatzTV.Scanner.csproj --framework net9.0 --runtime "${{ matrix.target }}" -c Release -o "scanner" -p:RestoreEnablePackagePruning=true -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net9.0 --runtime "${{ matrix.target }}" -c Release -o "main" -p:RestoreEnablePackagePruning=true -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
mkdir "$release_name"
mv scanner/* "$release_name/"
mv main/* "$release_name/"
# Build Windows launcher
if [ "${{ matrix.kind }}" == "windows" ]; then
dotnet publish ErsatzTV-Windows/ErsatzTV-Windows.csproj --framework net6.0-windows --runtime "${{ matrix.target }}" -c Release -o "$release_name" -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
cargo build --manifest-path=ErsatzTV-Windows/Cargo.toml --release --all-features
ls -l ErsatzTV-Windows/target/release
mv ErsatzTV-Windows/target/release/ersatztv_windows.exe "$release_name/ErsatzTV-Windows.exe"
fi
# Download ffmpeg
@@ -230,12 +222,10 @@ jobs:
# Delete output directory
rm -r "$release_name"
env:
AC_USERNAME: ${{ secrets.ac_username }}
AC_PASSWORD: ${{ secrets.ac_password }}
- name: Delete old release assets
uses: mknejp/delete-release-assets@v1
if: ${{ inputs.release_tag == 'develop' }}
with:
token: ${{ secrets.gh_token }}
tag: ${{ inputs.release_tag }}
@@ -243,10 +233,11 @@ jobs:
assets: |
*${{ matrix.target }}.zip
*${{ matrix.target }}.tar.gz
- name: Publish
uses: softprops/action-gh-release@v1
with:
prerelease: true
prerelease: false
tag_name: ${{ inputs.release_tag }}
files: |
${{ env.RELEASE_NAME }}.zip
+5 -5
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Get the sources
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract Docker Tag
@@ -19,14 +19,14 @@ jobs:
tag=$(git describe --tags --abbrev=0)
tag2="${tag:1}"
short=$(git rev-parse --short HEAD)
final="${tag2/beta/$short}"
final="${tag2}-${short}"
echo "GIT_TAG=${final}" >> $GITHUB_ENV
- name: Extract Artifacts Version
shell: bash
run: |
tag=$(git describe --tags --abbrev=0)
short=$(git rev-parse --short HEAD)
final="${tag/beta/$short}"
final="${tag}-${short}"
echo "ARTIFACTS_VERSION=${final}" >> $GITHUB_ENV
echo "INFO_VERSION=${tag:1}" >> $GITHUB_ENV
outputs:
@@ -34,7 +34,7 @@ jobs:
artifacts_version: ${{ env.ARTIFACTS_VERSION }}
info_version: ${{ env.INFO_VERSION }}
build_and_upload:
uses: jasongdove/ersatztv/.github/workflows/artifacts.yml@main
uses: ersatztv/ersatztv/.github/workflows/artifacts.yml@main
needs: calculate_version
with:
release_tag: develop
@@ -47,7 +47,7 @@ jobs:
ac_password: ${{ secrets.AC_PASSWORD }}
gh_token: ${{ secrets.GITHUB_TOKEN }}
build_and_push:
uses: jasongdove/ersatztv/.github/workflows/docker.yml@main
uses: ersatztv/ersatztv/.github/workflows/docker.yml@main
needs: calculate_version
with:
base_version: develop
+69 -31
View File
@@ -1,4 +1,4 @@
name: Build & Publish to Docker Hub
name: Build & Publish to Docker Hub
on:
workflow_call:
inputs:
@@ -22,72 +22,110 @@ on:
jobs:
build_and_push:
name: Build & Publish
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
if: contains(github.event.head_commit.message, '[no build]') == false
strategy:
matrix:
include:
- name: base
- name: amd64
os: ubuntu-latest
path: ''
suffix: ''
qemu: false
- name: nvidia
path: 'nvidia/'
suffix: '-nvidia'
qemu: false
- name: vaapi
path: 'vaapi/'
suffix: '-vaapi'
suffix: '-amd64'
qemu: false
platform: 'linux/amd64'
- name: arm32v7
os: ubuntu-latest
path: 'arm32v7/'
suffix: '-arm'
qemu: true
platform: 'linux/arm/v7'
- name: arm64
os: ubuntu-24.04-arm
path: 'arm64/'
suffix: '-arm64'
qemu: true
platform: 'linux/arm64'
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v3
if: ${{ matrix.qemu == true }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
id: docker-buildx
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
username: ${{ secrets.docker_hub_username }}
password: ${{ secrets.docker_hub_access_token }}
- name: Build and push
uses: docker/build-push-action@v2
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
builder: ${{ steps.docker-buildx.outputs.name }}
context: .
file: ./docker/${{ matrix.path }}Dockerfile
push: true
build-args: |
INFO_VERSION=${{ inputs.info_version }}-docker${{ matrix.suffix }}
tags: |
jasongdove/ersatztv:${{ inputs.base_version }}${{ matrix.suffix }}
jasongdove/ersatztv:${{ inputs.tag_version }}${{ matrix.suffix }}
if: ${{ matrix.name != 'arm64' }}
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v5
with:
builder: ${{ steps.docker-buildx.outputs.name }}
context: .
file: ./docker/${{ matrix.path }}Dockerfile
push: true
platforms: 'linux/arm64'
provenance: false
platforms: ${{ matrix.platform }}
build-args: |
INFO_VERSION=${{ inputs.info_version }}-docker${{ matrix.suffix }}
tags: |
jasongdove/ersatztv:${{ inputs.base_version }}${{ matrix.suffix }}
jasongdove/ersatztv:${{ inputs.tag_version }}${{ matrix.suffix }}
if: ${{ matrix.name == 'arm64' }}
ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }}${{ matrix.suffix }}
ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }}${{ matrix.suffix }}
merge_manifests:
name: Merge Manifests
runs-on: ubuntu-latest
needs: build_and_push
steps:
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.docker_hub_username }}
password: ${{ secrets.docker_hub_access_token }}
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifests
run: |
docker manifest create jasongdove/ersatztv:${{ inputs.base_version }} \
jasongdove/ersatztv:${{ inputs.base_version }}-amd64 \
jasongdove/ersatztv:${{ inputs.base_version }}-arm64 \
jasongdove/ersatztv:${{ inputs.base_version }}-arm
docker manifest push jasongdove/ersatztv:${{ inputs.base_version }}
docker manifest create jasongdove/ersatztv:${{ inputs.tag_version }} \
jasongdove/ersatztv:${{ inputs.tag_version }}-amd64 \
jasongdove/ersatztv:${{ inputs.tag_version }}-arm64 \
jasongdove/ersatztv:${{ inputs.tag_version }}-arm
docker manifest push jasongdove/ersatztv:${{ inputs.tag_version }}
docker manifest create ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }} \
ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }}-amd64 \
ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }}-arm64 \
ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }}-arm
docker manifest push ghcr.io/ersatztv/ersatztv:${{ inputs.base_version }}
docker manifest create ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }} \
ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }}-amd64 \
ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }}-arm64 \
ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }}-arm
docker manifest push ghcr.io/ersatztv/ersatztv:${{ inputs.tag_version }}
-18
View File
@@ -1,18 +0,0 @@
name: Publish docs via GitHub Pages
on:
push:
branches:
- main
jobs:
build:
name: Deploy docs
runs-on: ubuntu-latest
steps:
- name: Checkout master
uses: actions/checkout@v2
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CUSTOM_DOMAIN: ersatztv.org
+27
View File
@@ -0,0 +1,27 @@
name: 'Close stale issues'
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
ascending: true
days-before-stale: 120
days-before-pr-stale: -1
days-before-close: 21
days-before-pr-close: -1
operations-per-run: 500
exempt-issue-labels: 'regression,security,roadmap,future,feature,enhancement,confirmed'
stale-issue-label: 'stale'
stale-issue-message: |-
This issue has gone 120 days without an update and will be closed within 21 days if there is no new activity. To prevent this issue from being closed, please confirm the issue has not already been fixed by providing updated examples or logs.
If you have any questions you can use one of several ways to [contact us](https://ersatztv.org).
close-issue-message: |-
This issue was closed due to inactivity.
+78 -10
View File
@@ -2,20 +2,16 @@
on:
pull_request:
jobs:
build_and_test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ windows-latest, ubuntu-latest, macos-latest ]
build_and_test_windows:
runs-on: windows-latest
steps:
- name: Get the sources
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: 9.0.203
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
@@ -23,8 +19,80 @@ jobs:
- name: Install dependencies
run: dotnet restore
- name: Prep project file
run: sed -i '/Scanner/d' ErsatzTV/ErsatzTV.csproj
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal
run: dotnet test --blame-hang-timeout "2m" --no-restore --verbosity normal
- name: Build Windows
run: |
cd ErsatzTV-Windows
cargo build --release --all-features
build_and_test_linux:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: linux-x64
- os: ubuntu-latest
target: linux-musl-x64
- os: ubuntu-latest
target: linux-arm
- os: ubuntu-24.04-arm
target: linux-arm64
steps:
- name: Get the sources
uses: actions/checkout@v4
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.203
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
- name: Install dependencies
run: dotnet restore -p:RestoreEnablePackagePruning=true -r "${{ matrix.target }}"
- name: Prep project file
run: sed -i '/Scanner/d' ErsatzTV/ErsatzTV.csproj
- name: Build
run: dotnet build ErsatzTV/ErsatzTV.csproj --runtime "${{ matrix.target }}" --configuration Release --no-restore && dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --blame-hang-timeout "2m" --no-restore --verbosity normal
build_and_test_mac:
runs-on: macos-14
steps:
- name: Get the sources
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.203
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
- name: Install dependencies
run: dotnet restore
- name: Prep project file
run: sed -i '' '/Scanner/d' ErsatzTV/ErsatzTV.csproj
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --blame-hang-timeout "2m" --no-restore --verbosity normal
+4 -4
View File
@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Get the sources
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract Docker Tag
@@ -16,7 +16,7 @@ jobs:
run: |
tag=$(git describe --tags --abbrev=0)
echo "GIT_TAG=${tag:1}" >> $GITHUB_ENV
echo "DOCKER_TAG=${tag/-beta/}" >> $GITHUB_ENV
echo "DOCKER_TAG=${tag}" >> $GITHUB_ENV
- name: Extract Artifacts Version
shell: bash
run: |
@@ -29,7 +29,7 @@ jobs:
artifacts_version: ${{ env.ARTIFACTS_VERSION }}
info_version: ${{ env.INFO_VERSION }}
build_and_upload:
uses: jasongdove/ersatztv/.github/workflows/artifacts.yml@main
uses: ersatztv/ersatztv/.github/workflows/artifacts.yml@main
needs: calculate_version
with:
release_tag: ${{ needs.calculate_version.outputs.artifacts_version }}
@@ -42,7 +42,7 @@ jobs:
ac_password: ${{ secrets.AC_PASSWORD }}
gh_token: ${{ secrets.GITHUB_TOKEN }}
build_and_push:
uses: jasongdove/ersatztv/.github/workflows/docker.yml@main
uses: ersatztv/ersatztv/.github/workflows/docker.yml@main
needs: calculate_version
with:
base_version: latest
-22
View File
@@ -1,22 +0,0 @@
name: Lint VueJS Files on PR Request
on:
pull_request:
jobs:
vue-lint:
runs-on: ubuntu-latest
steps:
# Checkout the current repo
- name: Checkout current repository
uses: actions/checkout@v2
# Setup NodeJS version 14
- name: Setup NodeJS V14.x.x
uses: actions/setup-node@v2
with:
node-version: '14'
# CD into the current client directory and lint and build the client
- name: Lint and Build the client
run: |
cd ./ErsatzTV/client-app/
npm ci --no-optional
npm run lint
npm run build --if-present
+1
View File
@@ -40,6 +40,7 @@ msbuild.wrn
core
scripts/generate-api-sdk/swagger.json
scripts/download-test-content.sh
docker-compose.override.yml
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "ErsatzTV-macOS"]
path = ErsatzTV-macOS
url = git@github.com:jasongdove/ErsatzTV-macOS.git
url = git@github.com:ErsatzTV/ErsatzTV-macOS.git
+1347 -105
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,5 +1,6 @@
<Project>
<PropertyGroup>
<InformationalVersion>develop</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
</Project>
+2
View File
@@ -0,0 +1,2 @@
target/
+1035
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "ersatztv_windows"
version = "0.1.0"
edition = "2021"
[dependencies]
tray-item = { git = "https://github.com/olback/tray-item-rs" }
special-folder = { git = "https://github.com/masinc/special-folder-rs" }
process_path = "0.1.4"
[dependencies.windows]
version = "0.43.0"
features = [
"Win32_System_Console",
"Win32_Foundation"
]
[build-dependencies]
windres = "*"
static_vcruntime = "2.0"
-33
View File
@@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<RootNamespace>ErsatzTV_Windows</RootNamespace>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>Ersatztv.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="Ersatztv.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.4.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ErsatzTV.Core\ErsatzTV.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Program.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Compile>
</ItemGroup>
</Project>
-14
View File
@@ -1,14 +0,0 @@
namespace ErsatzTV_Windows;
public static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new TrayApplicationContext());
}
}
@@ -1,81 +0,0 @@
using ErsatzTV.Core;
using System.Diagnostics;
using CliWrap;
namespace ErsatzTV_Windows;
public class TrayApplicationContext : ApplicationContext
{
private readonly NotifyIcon _trayIcon;
private readonly CancellationTokenSource _tokenSource;
public TrayApplicationContext()
{
_trayIcon = new NotifyIcon
{
Icon = new Icon("./Ersatztv.ico"),
ContextMenuStrip = new ContextMenuStrip(),
Visible = true
};
_tokenSource = new CancellationTokenSource();
AddMenuItem("Launch Web UI", LaunchWebUI);
AddMenuItem("Show Logs", ShowLogs);
_trayIcon.ContextMenuStrip.Items.Add(new ToolStripSeparator());
AddMenuItem("Exit", Exit);
string folder = AppContext.BaseDirectory;
string exe = Path.Combine(folder, "ErsatzTV.exe");
if (File.Exists(exe))
{
Cli.Wrap(exe)
.WithWorkingDirectory(folder)
.WithValidation(CommandResultValidation.None)
.ExecuteAsync(_tokenSource.Token);
}
}
private void AddMenuItem(string name, EventHandler action)
{
var item = new ToolStripMenuItem(name);
item.Click += action;
_trayIcon.ContextMenuStrip.Items.Add(item);
}
private void LaunchWebUI(object? sender, EventArgs e)
{
var process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = "http://localhost:8409";
process.Start();
}
private void ShowLogs(object? sender, EventArgs e)
{
if (!Directory.Exists(FileSystemLayout.LogsFolder))
{
Directory.CreateDirectory(FileSystemLayout.LogsFolder);
}
var process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = FileSystemLayout.LogsFolder;
process.Start();
}
protected override void Dispose(bool disposing)
{
_tokenSource?.Cancel();
base.Dispose(disposing);
}
private void Exit(object? sender, EventArgs e)
{
// Hide tray icon, otherwise it will remain shown until user mouses over it
_trayIcon.Visible = false;
Application.Exit();
}
}
+6
View File
@@ -0,0 +1,6 @@
use windres::Build;
fn main() {
static_vcruntime::metabuild();
Build::new().compile("ersatztv_windows.rc").unwrap();
}
+2
View File
@@ -0,0 +1,2 @@
id ICON "ersatztv.ico"
ersatztv-icon ICON "ersatztv.ico"
+115
View File
@@ -0,0 +1,115 @@
#![windows_subsystem = "windows"]
use special_folder::SpecialFolder;
use std::env;
use std::fs;
use std::os::windows::process::CommandExt;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use windows::Win32::System::Console;
use {std::sync::mpsc, tray_item::TrayItem};
const CREATE_NO_WINDOW: u32 = 0x08000000;
enum Message {
Exit,
}
fn main() {
let mut tray = TrayItem::new("ErsatzTV", "ersatztv-icon").unwrap();
let (tx, rx) = mpsc::channel();
tray.add_menu_item("Launch Web UI", || {
let ui_port = env::var("ETV_UI_PORT")
.ok()
.and_then(|val| val.parse::<u16>().ok())
.unwrap_or(8409);
let _ = Command::new("cmd")
.creation_flags(CREATE_NO_WINDOW)
.arg("/C")
.arg("start")
.arg(format!("http://localhost:{}", ui_port))
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn();
})
.unwrap();
tray.add_menu_item("Show Logs", || {
let path = SpecialFolder::LocalApplicationData
.get()
.unwrap()
.join("ersatztv")
.join("logs");
match path.to_str() {
None => {}
Some(folder) => {
fs::create_dir_all(folder).unwrap();
let _ = Command::new("explorer.exe")
.arg(folder)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn();
}
}
})
.unwrap();
tray.inner_mut().add_separator().unwrap();
tray.add_menu_item("Exit", move || {
tx.send(Message::Exit).unwrap();
})
.unwrap();
let path = process_path::get_executable_path();
let mut child: Option<Child> = None;
match path {
None => {}
Some(path) => {
let etv = path.parent().unwrap().join("ErsatzTV.exe");
if etv.exists() {
match etv.to_str() {
None => {}
Some(etv) => {
child = Some(
Command::new(etv)
.creation_flags(CREATE_NO_WINDOW)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.unwrap(),
);
}
}
}
}
}
loop {
match rx.recv() {
Ok(Message::Exit) => {
match child {
None => {}
Some(mut child) => {
unsafe {
if Console::AttachConsole(child.id()) == true
{
Console::GenerateConsoleCtrlEvent(Console::CTRL_C_EVENT, 0);
}
}
child.wait().unwrap();
}
}
break;
}
_ => {}
}
}
}
+5
View File
@@ -0,0 +1,5 @@
[*.cs]
# disable CA1711: Identifiers should not have incorrect suffix
dotnet_diagnostic.ca1711.severity = none
# disable CA1848: Use the LoggerMessage delegates
dotnet_diagnostic.ca1848.severity = none
+5 -5
View File
@@ -29,12 +29,12 @@ internal static class Mapper
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
return languages
.Distinct()
.Map(
lang => allCultures.Filter(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lang, StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Map(lang => allCultures.Filter(ci => string.Equals(
ci.ThreeLetterISOLanguageName,
lang,
StringComparison.OrdinalIgnoreCase)))
.Flatten()
.Distinct()
.ToList();
}
}
@@ -1,22 +1,41 @@
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using static ErsatzTV.Application.MediaItems.Mapper;
namespace ErsatzTV.Application.Artists;
public class GetAllArtistsHandler : IRequestHandler<GetAllArtists, List<NamedMediaItemViewModel>>
{
private readonly IArtistRepository _artistRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public GetAllArtistsHandler(IArtistRepository artistRepository) => _artistRepository = artistRepository;
public GetAllArtistsHandler(IDbContextFactory<TvContext> dbContextFactory) => _dbContextFactory = dbContextFactory;
public Task<List<NamedMediaItemViewModel>> Handle(
public async Task<List<NamedMediaItemViewModel>> Handle(
GetAllArtists request,
CancellationToken cancellationToken) =>
_artistRepository.GetAllArtists()
.Map(
list => list.Filter(
a => !string.IsNullOrWhiteSpace(
a.ArtistMetadata.HeadOrNone().Match(am => am.Title, () => string.Empty))))
.Map(list => list.Map(ProjectToViewModel).ToList());
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
List<Artist> allArtists = await dbContext.Artists
.AsNoTracking()
.Include(a => a.ArtistMetadata)
.ToListAsync(cancellationToken);
return allArtists.Bind(a => ProjectArtist(a)).ToList();
}
private static Option<NamedMediaItemViewModel> ProjectArtist(Artist a)
{
foreach (ArtistMetadata metadata in a.ArtistMetadata.HeadOrNone())
{
if (!string.IsNullOrWhiteSpace(metadata.Title))
{
return ProjectToViewModel(a);
}
}
return None;
}
}
@@ -24,7 +24,7 @@ public class GetArtistByIdHandler : IRequestHandler<GetArtistById, Option<Artist
async artist =>
{
List<string> mediaCodes = await _searchRepository.GetLanguagesForArtist(artist);
List<string> languageCodes = await _searchRepository.GetAllLanguageCodes(mediaCodes);
List<string> languageCodes = await _searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes);
return ProjectToViewModel(artist, languageCodes);
},
() => Task.FromResult(Option<ArtistViewModel>.None));
@@ -0,0 +1,17 @@
using System.Net;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Artworks;
public record ArtworkContentTypeModel(string Path, string ContentType)
{
public static readonly ArtworkContentTypeModel None = new(string.Empty, string.Empty);
public bool IsExternalUrl => Artwork.IsExternalUrl(Path);
public bool HasContentType => !string.IsNullOrWhiteSpace(ContentType);
public string UrlWithContentType => string.IsNullOrWhiteSpace(ContentType)
? Path
: $"{Path}?contentType={WebUtility.UrlEncode(ContentType)}";
}
@@ -0,0 +1,6 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Artworks;
public record GetArtwork(int Id) : IRequest<Either<BaseError, Artwork>>;
@@ -0,0 +1,42 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Artworks;
public class GetArtworkHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetArtwork, Either<BaseError, Artwork>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory = dbContextFactory;
public async Task<Either<BaseError, Artwork>> Handle(
GetArtwork request,
CancellationToken cancellationToken)
{
try
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Option<Artwork> artwork = await dbContext.Artwork
.AsNoTracking()
.SelectOneAsync(a => a.Id, a => a.Id == request.Id)
.MapT(Project);
return artwork.ToEither(BaseError.New("Artwork not found"));
}
catch (Exception ex)
{
return BaseError.New(ex.ToString());
}
}
private static Artwork Project(Artwork artwork) =>
new()
{
Id = artwork.Id,
Path = artwork.Path,
ArtworkKind = artwork.ArtworkKind
};
}
@@ -1,4 +1,6 @@
using ErsatzTV.Core.Domain;
using System.Net;
using ErsatzTV.Application.Artworks;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels;
@@ -9,11 +11,22 @@ public record ChannelViewModel(
string Group,
string Categories,
int FFmpegProfileId,
string Logo,
ArtworkContentTypeModel Logo,
ChannelStreamSelectorMode StreamSelectorMode,
string StreamSelector,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
ChannelProgressMode ProgressMode,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,
int PlayoutCount,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode SubtitleMode);
ChannelSubtitleMode SubtitleMode,
ChannelMusicVideoCreditsMode MusicVideoCreditsMode,
string MusicVideoCreditsTemplate,
ChannelSongVideoMode SongVideoMode,
ChannelActiveMode ActiveMode)
{
public string WebEncodedName => WebUtility.UrlEncode(Name);
}
@@ -1,19 +1,27 @@
using ErsatzTV.Core;
using ErsatzTV.Application.Artworks;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels;
public record CreateChannel
(
public record CreateChannel(
string Name,
string Number,
string Group,
string Categories,
int FFmpegProfileId,
string Logo,
ArtworkContentTypeModel Logo,
ChannelStreamSelectorMode StreamSelectorMode,
string StreamSelector,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
ChannelProgressMode ProgressMode,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode SubtitleMode) : IRequest<Either<BaseError, CreateChannelResult>>;
ChannelSubtitleMode SubtitleMode,
ChannelMusicVideoCreditsMode MusicVideoCreditsMode,
string MusicVideoCreditsTemplate,
ChannelSongVideoMode SongVideoMode,
ChannelActiveMode ActiveMode) : IRequest<Either<BaseError, CreateChannelResult>>;
@@ -1,111 +1,113 @@
using System.Globalization;
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using System.Threading.Channels;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
using Channel = ErsatzTV.Core.Domain.Channel;
namespace ErsatzTV.Application.Channels;
public class CreateChannelHandler : IRequestHandler<CreateChannel, Either<BaseError, CreateChannelResult>>
public class CreateChannelHandler(
ChannelWriter<IBackgroundServiceRequest> workerChannel,
IDbContextFactory<TvContext> dbContextFactory,
ISearchTargets searchTargets)
: IRequestHandler<CreateChannel, Either<BaseError, CreateChannelResult>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public CreateChannelHandler(IDbContextFactory<TvContext> dbContextFactory) => _dbContextFactory = dbContextFactory;
public async Task<Either<BaseError, CreateChannelResult>> Handle(
CreateChannel request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Channel> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => PersistChannel(dbContext, c));
return await validation.Apply(c => PersistChannel(dbContext, c));
}
private static async Task<CreateChannelResult> PersistChannel(TvContext dbContext, Channel channel)
private async Task<CreateChannelResult> PersistChannel(TvContext dbContext, Channel channel)
{
await dbContext.Channels.AddAsync(channel);
await dbContext.SaveChangesAsync();
searchTargets.SearchTargetsChanged();
await workerChannel.WriteAsync(new RefreshChannelList());
return new CreateChannelResult(channel.Id);
}
private static async Task<Validation<BaseError, Channel>> Validate(TvContext dbContext, CreateChannel request) =>
(ValidateName(request), await ValidateNumber(dbContext, request),
await FFmpegProfileMustExist(dbContext, request),
ValidatePreferredAudioLanguage(request),
ValidatePreferredSubtitleLanguage(request),
await WatermarkMustExist(dbContext, request),
await FillerPresetMustExist(dbContext, request))
.Apply(
(
name,
number,
ffmpegProfileId,
preferredAudioLanguageCode,
preferredSubtitleLanguageCode,
watermarkId,
fillerPresetId) =>
.Apply((
name,
number,
ffmpegProfileId,
watermarkId,
fillerPresetId) =>
{
var artwork = new List<Artwork>();
if (!string.IsNullOrWhiteSpace(request.Logo?.Path))
{
var artwork = new List<Artwork>();
if (!string.IsNullOrWhiteSpace(request.Logo))
string logo = request.Logo.Path;
if (logo.StartsWith("iptv/logos/", StringComparison.Ordinal))
{
artwork.Add(
new Artwork
{
Path = request.Logo,
ArtworkKind = ArtworkKind.Logo,
DateAdded = DateTime.UtcNow,
DateUpdated = DateTime.UtcNow
});
logo = logo.Replace("iptv/logos/", string.Empty);
}
var channel = new Channel(Guid.NewGuid())
{
Name = name,
Number = number,
Group = request.Group,
Categories = request.Categories,
FFmpegProfileId = ffmpegProfileId,
StreamingMode = request.StreamingMode,
Artwork = artwork,
PreferredAudioLanguageCode = preferredAudioLanguageCode,
PreferredSubtitleLanguageCode = preferredSubtitleLanguageCode,
SubtitleMode = request.SubtitleMode
};
artwork.Add(
new Artwork
{
Path = logo,
ArtworkKind = ArtworkKind.Logo,
OriginalContentType = !string.IsNullOrEmpty(request.Logo.ContentType)
? request.Logo.ContentType
: null,
DateAdded = DateTime.UtcNow,
DateUpdated = DateTime.UtcNow
});
}
foreach (int id in watermarkId)
{
channel.WatermarkId = id;
}
var channel = new Channel(Guid.NewGuid())
{
Name = name,
Number = number,
Group = request.Group,
Categories = request.Categories,
FFmpegProfileId = ffmpegProfileId,
ProgressMode = request.ProgressMode,
StreamingMode = request.StreamingMode,
Artwork = artwork,
StreamSelectorMode = request.StreamSelectorMode,
StreamSelector = request.StreamSelector,
PreferredAudioLanguageCode = request.PreferredAudioLanguageCode,
PreferredAudioTitle = request.PreferredAudioTitle,
PreferredSubtitleLanguageCode = request.PreferredSubtitleLanguageCode,
SubtitleMode = request.SubtitleMode,
MusicVideoCreditsMode = request.MusicVideoCreditsMode,
MusicVideoCreditsTemplate = request.MusicVideoCreditsTemplate,
SongVideoMode = request.SongVideoMode,
ActiveMode = request.ActiveMode
};
foreach (int id in fillerPresetId)
{
channel.FallbackFillerId = id;
}
foreach (int id in watermarkId)
{
channel.WatermarkId = id;
}
return channel;
});
foreach (int id in fillerPresetId)
{
channel.FallbackFillerId = id;
}
return channel;
});
private static Validation<BaseError, string> ValidateName(CreateChannel createChannel) =>
createChannel.NotEmpty(c => c.Name)
.Bind(_ => createChannel.NotLongerThan(50)(c => c.Name));
private static Validation<BaseError, string> ValidatePreferredAudioLanguage(CreateChannel createChannel) =>
Optional(createChannel.PreferredAudioLanguageCode ?? string.Empty)
.Filter(
lc => string.IsNullOrWhiteSpace(lc) || CultureInfo.GetCultures(CultureTypes.NeutralCultures).Any(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lc, StringComparison.OrdinalIgnoreCase)))
.ToValidation<BaseError>("Preferred audio language code is invalid");
private static Validation<BaseError, string> ValidatePreferredSubtitleLanguage(CreateChannel createChannel) =>
Optional(createChannel.PreferredSubtitleLanguageCode ?? string.Empty)
.Filter(
lc => string.IsNullOrWhiteSpace(lc) || CultureInfo.GetCultures(CultureTypes.NeutralCultures).Any(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lc, StringComparison.OrdinalIgnoreCase)))
.ToValidation<BaseError>("Preferred subtitle language code is invalid");
private static async Task<Validation<BaseError, string>> ValidateNumber(
TvContext dbContext,
CreateChannel createChannel)
@@ -167,8 +169,7 @@ public class CreateChannelHandler : IRequestHandler<CreateChannel, Either<BaseEr
.Map(Optional)
.Filter(c => c > 0)
.MapT(_ => Optional(createChannel.FallbackFillerId))
.Map(
o => o.ToValidation<BaseError>(
$"Fallback filler {createChannel.FallbackFillerId} does not exist."));
.Map(o => o.ToValidation<BaseError>(
$"Fallback filler {createChannel.FallbackFillerId} does not exist."));
}
}
@@ -2,4 +2,4 @@
namespace ErsatzTV.Application.Channels;
public record DeleteChannel(int ChannelId) : IRequest<Either<BaseError, Task>>;
public record DeleteChannel(int ChannelId) : IRequest<Either<BaseError, Unit>>;
@@ -1,23 +1,66 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.Repositories;
using System.Threading.Channels;
using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
using Channel = ErsatzTV.Core.Domain.Channel;
namespace ErsatzTV.Application.Channels;
public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseError, Task>>
public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseError, Unit>>
{
private readonly IChannelRepository _channelRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem;
private readonly ISearchTargets _searchTargets;
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
public DeleteChannelHandler(IChannelRepository channelRepository) => _channelRepository = channelRepository;
public DeleteChannelHandler(
ChannelWriter<IBackgroundServiceRequest> workerChannel,
IDbContextFactory<TvContext> dbContextFactory,
ILocalFileSystem localFileSystem,
ISearchTargets searchTargets)
{
_workerChannel = workerChannel;
_dbContextFactory = dbContextFactory;
_localFileSystem = localFileSystem;
_searchTargets = searchTargets;
}
public async Task<Either<BaseError, Task>> Handle(DeleteChannel request, CancellationToken cancellationToken) =>
(await ChannelMustExist(request))
.Map(DoDeletion)
.ToEither<Task>();
public async Task<Either<BaseError, Unit>> Handle(DeleteChannel request, CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Channel> validation = await ChannelMustExist(dbContext, request);
return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken));
}
private Task DoDeletion(int channelId) => _channelRepository.Delete(channelId);
private async Task<Unit> DoDeletion(TvContext dbContext, Channel channel, CancellationToken cancellationToken)
{
dbContext.Channels.Remove(channel);
await dbContext.SaveChangesAsync(cancellationToken);
private async Task<Validation<BaseError, int>> ChannelMustExist(DeleteChannel deleteChannel) =>
(await _channelRepository.Get(deleteChannel.ChannelId))
.ToValidation<BaseError>($"Channel {deleteChannel.ChannelId} does not exist.")
.Map(c => c.Id);
_searchTargets.SearchTargetsChanged();
// delete channel data from channel guide cache
string cacheFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channel.Number}.xml");
if (_localFileSystem.FileExists(cacheFile))
{
File.Delete(cacheFile);
}
// refresh channel list to remove channel that has no playout
await _workerChannel.WriteAsync(new RefreshChannelList(), cancellationToken);
return Unit.Default;
}
private static async Task<Validation<BaseError, Channel>> ChannelMustExist(
TvContext dbContext,
DeleteChannel deleteChannel)
{
Option<Channel> maybeChannel = await dbContext.Channels
.SelectOneAsync(c => c.Id, c => c.Id == deleteChannel.ChannelId);
return maybeChannel.ToValidation<BaseError>($"Channel {deleteChannel.ChannelId} does not exist.");
}
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record RefreshChannelData(string ChannelNumber) : IRequest, IBackgroundServiceRequest;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record RefreshChannelList : IRequest, IBackgroundServiceRequest;
@@ -0,0 +1,147 @@
using System.Data.Common;
using System.Net;
using System.Xml;
using Dapper;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Iptv;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.IO;
using Scriban;
using Scriban.Runtime;
using WebMarkupMin.Core;
namespace ErsatzTV.Application.Channels;
public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem;
private readonly ILogger<RefreshChannelListHandler> _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
public RefreshChannelListHandler(
RecyclableMemoryStreamManager recyclableMemoryStreamManager,
IDbContextFactory<TvContext> dbContextFactory,
ILocalFileSystem localFileSystem,
ILogger<RefreshChannelListHandler> logger)
{
_recyclableMemoryStreamManager = recyclableMemoryStreamManager;
_dbContextFactory = dbContextFactory;
_localFileSystem = localFileSystem;
_logger = logger;
}
public async Task Handle(RefreshChannelList request, CancellationToken cancellationToken)
{
_localFileSystem.EnsureFolderExists(FileSystemLayout.ChannelGuideCacheFolder);
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
string templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "channel.sbntxt");
// fall back to default template
if (!_localFileSystem.FileExists(templateFileName))
{
templateFileName = Path.Combine(FileSystemLayout.ChannelGuideTemplatesFolder, "_channel.sbntxt");
}
// fail if file doesn't exist
if (!_localFileSystem.FileExists(templateFileName))
{
_logger.LogError(
"Unable to generate channel list without template file {File}; please restart ErsatzTV",
templateFileName);
return;
}
var minifier = new XmlMinifier(
new XmlMinificationSettings
{
MinifyWhitespace = true,
RemoveXmlComments = true,
CollapseTagsWithoutContent = true
});
string text = await File.ReadAllTextAsync(templateFileName, cancellationToken);
var template = Template.Parse(text, templateFileName);
var templateContext = new XmlTemplateContext();
await using RecyclableMemoryStream ms = _recyclableMemoryStreamManager.GetStream();
await using var xml = XmlWriter.Create(
ms,
new XmlWriterSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment });
await foreach (ChannelResult channel in GetChannels(dbContext).WithCancellation(cancellationToken))
{
bool hasLogo = !string.IsNullOrWhiteSpace(channel.ArtworkPath);
bool hasExternalLogo = hasLogo && Artwork.IsExternalUrl(channel.ArtworkPath);
var data = new
{
ChannelId = ChannelIdentifier.FromNumber(channel.Number),
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(channel.Number),
ChannelNumber = channel.Number,
ChannelName = channel.Name,
ChannelCategories = GetCategories(channel.Categories),
ChannelHasExternalArtwork = hasExternalLogo,
ChannelHasArtwork = hasLogo,
ChannelArtworkPath = channel.ArtworkPath,
ChannelNameEncoded = WebUtility.UrlEncode(channel.Name)
};
var scriptObject = new ScriptObject();
scriptObject.Import(data);
templateContext.PushGlobal(scriptObject);
string result = await template.RenderAsync(templateContext);
MarkupMinificationResult minified = minifier.Minify(result);
await xml.WriteRawAsync(minified.MinifiedContent);
}
await xml.FlushAsync();
string tempFile = Path.GetTempFileName();
await File.WriteAllBytesAsync(tempFile, ms.ToArray(), cancellationToken);
string targetFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, "channels.xml");
File.Move(tempFile, targetFile, true);
}
private static async IAsyncEnumerable<ChannelResult> GetChannels(TvContext dbContext)
{
const string QUERY = @"select C.Number, C.Name, C.Categories, A.Path as ArtworkPath
from Channel C
left outer join Artwork A on C.Id = A.ChannelId and A.ArtworkKind = 2
where C.Id in (select ChannelId from Playout) and C.ActiveMode = 0
order by CAST(C.Number as double)";
// TODO: this needs to be fixed for sqlite/mariadb
await using var reader = (DbDataReader)await dbContext.Connection.ExecuteReaderAsync(QUERY);
Func<DbDataReader, ChannelResult> rowParser = reader.GetRowParser<ChannelResult>();
while (await reader.ReadAsync())
{
yield return rowParser(reader);
}
while (await reader.NextResultAsync())
{
}
}
private static List<string> GetCategories(string categories) =>
(categories ?? string.Empty).Split(',')
.Map(s => s.Trim())
.Filter(s => !string.IsNullOrWhiteSpace(s))
.Distinct()
.ToList();
// ReSharper disable once ClassNeverInstantiated.Local
private sealed record ChannelResult(string Number, string Name, string Categories, string ArtworkPath);
}
@@ -1,20 +1,28 @@
using ErsatzTV.Core;
using ErsatzTV.Application.Artworks;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels;
public record UpdateChannel
(
public record UpdateChannel(
int ChannelId,
string Name,
string Number,
string Group,
string Categories,
int FFmpegProfileId,
string Logo,
ArtworkContentTypeModel Logo,
ChannelStreamSelectorMode StreamSelectorMode,
string StreamSelector,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
ChannelProgressMode ProgressMode,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode SubtitleMode) : IRequest<Either<BaseError, ChannelViewModel>>;
ChannelSubtitleMode SubtitleMode,
ChannelMusicVideoCreditsMode MusicVideoCreditsMode,
string MusicVideoCreditsTemplate,
ChannelSongVideoMode SongVideoMode,
ChannelActiveMode ActiveMode) : IRequest<Either<BaseError, ChannelViewModel>>;
@@ -1,9 +1,9 @@
using System.Globalization;
using System.Text.RegularExpressions;
using System.Text.RegularExpressions;
using System.Threading.Channels;
using ErsatzTV.Application.Subtitles;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
@@ -12,26 +12,19 @@ using Channel = ErsatzTV.Core.Domain.Channel;
namespace ErsatzTV.Application.Channels;
public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseError, ChannelViewModel>>
public class UpdateChannelHandler(
ChannelWriter<IBackgroundServiceRequest> workerChannel,
IDbContextFactory<TvContext> dbContextFactory,
ISearchTargets searchTargets)
: IRequestHandler<UpdateChannel, Either<BaseError, ChannelViewModel>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ChannelWriter<ISubtitleWorkerRequest> _ffmpegWorkerChannel;
public UpdateChannelHandler(
ChannelWriter<ISubtitleWorkerRequest> ffmpegWorkerChannel,
IDbContextFactory<TvContext> dbContextFactory)
{
_ffmpegWorkerChannel = ffmpegWorkerChannel;
_dbContextFactory = dbContextFactory;
}
public async Task<Either<BaseError, ChannelViewModel>> Handle(
UpdateChannel request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Channel> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request));
return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request));
}
private async Task<ChannelViewModel> ApplyUpdateRequest(TvContext dbContext, Channel c, UpdateChannel update)
@@ -41,40 +34,72 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
c.Group = update.Group;
c.Categories = update.Categories;
c.FFmpegProfileId = update.FFmpegProfileId;
c.StreamSelectorMode = update.StreamSelectorMode;
c.StreamSelector = update.StreamSelector;
c.PreferredAudioLanguageCode = update.PreferredAudioLanguageCode;
c.PreferredAudioTitle = update.PreferredAudioTitle;
c.PreferredSubtitleLanguageCode = update.PreferredSubtitleLanguageCode;
c.SubtitleMode = update.SubtitleMode;
c.Artwork ??= new List<Artwork>();
c.MusicVideoCreditsMode = update.MusicVideoCreditsMode;
c.MusicVideoCreditsTemplate = update.MusicVideoCreditsTemplate;
c.SongVideoMode = update.SongVideoMode;
c.ActiveMode = update.ActiveMode;
c.Artwork ??= [];
if (!string.IsNullOrWhiteSpace(update.Logo))
if (!string.IsNullOrWhiteSpace(update.Logo?.Path))
{
Option<Artwork> maybeLogo =
Optional(c.Artwork).Flatten().FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo);
string logo = update.Logo.Path;
if (logo.StartsWith("iptv/logos/", StringComparison.Ordinal))
{
logo = logo.Replace("iptv/logos/", string.Empty);
}
maybeLogo.Match(
artwork =>
Option<Artwork> maybeLogo = c.Artwork.Where(a => a.ArtworkKind == ArtworkKind.Logo).HeadOrNone();
foreach (Artwork artwork in maybeLogo)
{
artwork.Path = logo;
artwork.OriginalContentType = !string.IsNullOrEmpty(update.Logo.ContentType)
? update.Logo.ContentType
: null;
artwork.DateUpdated = DateTime.UtcNow;
}
if (maybeLogo.IsNone)
{
var artwork = new Artwork
{
artwork.Path = update.Logo;
artwork.DateUpdated = DateTime.UtcNow;
},
() =>
{
var artwork = new Artwork
{
Path = update.Logo,
DateAdded = DateTime.UtcNow,
DateUpdated = DateTime.UtcNow,
ArtworkKind = ArtworkKind.Logo
};
c.Artwork.Add(artwork);
});
Path = logo,
OriginalContentType = !string.IsNullOrEmpty(update.Logo.ContentType)
? update.Logo.ContentType
: null,
DateAdded = DateTime.UtcNow,
DateUpdated = DateTime.UtcNow,
ArtworkKind = ArtworkKind.Logo
};
c.Artwork.Add(artwork);
}
}
else
{
await dbContext.Entry(c)
.Collection(channel => channel.Artwork)
.LoadAsync();
foreach (Artwork artwork in c.Artwork.Where(x => x.ArtworkKind is ArtworkKind.Logo).ToList())
{
c.Artwork.Remove(artwork);
dbContext.Artwork.Remove(artwork);
}
}
c.ProgressMode = update.ProgressMode;
c.StreamingMode = update.StreamingMode;
c.WatermarkId = update.WatermarkId;
c.FallbackFillerId = update.FallbackFillerId;
await dbContext.SaveChangesAsync();
searchTargets.SearchTargetsChanged();
if (c.SubtitleMode != ChannelSubtitleMode.None)
{
Option<Playout> maybePlayout = await dbContext.Playouts
@@ -82,18 +107,19 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
foreach (Playout playout in maybePlayout)
{
await _ffmpegWorkerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
await workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id));
}
}
await workerChannel.WriteAsync(new RefreshChannelList());
return ProjectToViewModel(c);
}
private async Task<Validation<BaseError, Channel>> Validate(TvContext dbContext, UpdateChannel request) =>
private static async Task<Validation<BaseError, Channel>> Validate(TvContext dbContext, UpdateChannel request) =>
(await ChannelMustExist(dbContext, request), ValidateName(request),
await ValidateNumber(dbContext, request),
ValidatePreferredAudioLanguage(request))
.Apply((channelToUpdate, _, _, _) => channelToUpdate);
await ValidateNumber(dbContext, request))
.Apply((channelToUpdate, _, _) => channelToUpdate);
private static Task<Validation<BaseError, Channel>> ChannelMustExist(
TvContext dbContext,
@@ -128,11 +154,4 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
return BaseError.New("Channel number must be unique");
}
private static Validation<BaseError, string> ValidatePreferredAudioLanguage(UpdateChannel updateChannel) =>
Optional(updateChannel.PreferredAudioLanguageCode ?? string.Empty)
.Filter(
lc => string.IsNullOrWhiteSpace(lc) || CultureInfo.GetCultures(CultureTypes.NeutralCultures).Any(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lc, StringComparison.OrdinalIgnoreCase)))
.ToValidation<BaseError>("Preferred audio language code is invalid");
}
+34 -6
View File
@@ -1,4 +1,5 @@
using ErsatzTV.Core.Api.Channels;
using ErsatzTV.Application.Artworks;
using ErsatzTV.Core.Api.Channels;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Channels;
@@ -14,13 +15,21 @@ internal static class Mapper
channel.Categories,
channel.FFmpegProfileId,
GetLogo(channel),
channel.StreamSelectorMode,
channel.StreamSelector,
channel.PreferredAudioLanguageCode,
channel.PreferredAudioTitle,
channel.ProgressMode,
channel.StreamingMode,
channel.WatermarkId,
channel.FallbackFillerId,
channel.Playouts?.Count ?? 0,
channel.PreferredSubtitleLanguageCode,
channel.SubtitleMode);
channel.SubtitleMode,
channel.MusicVideoCreditsMode,
channel.MusicVideoCreditsTemplate,
channel.SongVideoMode,
channel.ActiveMode);
internal static ChannelResponseModel ProjectToResponseModel(Channel channel) =>
new(
@@ -31,9 +40,27 @@ internal static class Mapper
channel.PreferredAudioLanguageCode,
GetStreamingMode(channel));
private static string GetLogo(Channel channel) =>
Optional(channel.Artwork.FirstOrDefault(a => a.ArtworkKind == ArtworkKind.Logo))
.Match(a => a.Path, string.Empty);
internal static ResolutionViewModel ProjectToViewModel(Resolution resolution) =>
new(resolution.Height, resolution.Width);
internal static ResolutionAndBitrateViewModel ProjectToViewModel(Resolution resolution, int bitrate) =>
new(resolution.Height, resolution.Width, bitrate);
private static ArtworkContentTypeModel GetLogo(Channel channel)
{
Option<Artwork> maybeArtwork = channel.Artwork
.Where(a => a.ArtworkKind == ArtworkKind.Logo)
.HeadOrNone();
foreach (Artwork artwork in maybeArtwork)
{
return artwork.IsExternalUrl()
? new ArtworkContentTypeModel(artwork.Path, string.Empty)
: new ArtworkContentTypeModel($"iptv/logos/{artwork.Path}", artwork.OriginalContentType);
}
return ArtworkContentTypeModel.None;
}
private static string GetStreamingMode(Channel channel) =>
channel.StreamingMode switch
@@ -42,6 +69,7 @@ internal static class Mapper
StreamingMode.TransportStreamHybrid => "MPEG-TS",
StreamingMode.HttpLiveStreamingDirect => "HLS Direct",
StreamingMode.HttpLiveStreamingSegmenter => "HLS Segmenter",
_ => throw new ArgumentOutOfRangeException()
StreamingMode.HttpLiveStreamingSegmenterV2 => "HLS Segmenter V2",
_ => throw new ArgumentOutOfRangeException(nameof(channel))
};
}
@@ -3,13 +3,10 @@ using static ErsatzTV.Application.Channels.Mapper;
namespace ErsatzTV.Application.Channels;
public class GetChannelByIdHandler : IRequestHandler<GetChannelById, Option<ChannelViewModel>>
public class GetChannelByIdHandler(IChannelRepository channelRepository)
: IRequestHandler<GetChannelById, Option<ChannelViewModel>>
{
private readonly IChannelRepository _channelRepository;
public GetChannelByIdHandler(IChannelRepository channelRepository) => _channelRepository = channelRepository;
public Task<Option<ChannelViewModel>> Handle(GetChannelById request, CancellationToken cancellationToken) =>
_channelRepository.Get(request.Id)
channelRepository.GetChannel(request.Id)
.MapT(ProjectToViewModel);
}
@@ -86,10 +86,20 @@ public class GetChannelFramerateHandler : IRequestHandler<GetChannelFramerate, O
return result;
}
_logger.LogInformation(
"All content on channel {ChannelNumber} has the same frame rate of {FrameRate}; will not normalize",
request.ChannelNumber,
distinct[0]);
if (distinct.Count != 0)
{
_logger.LogInformation(
"All content on channel {ChannelNumber} has the same frame rate of {FrameRate}; will not normalize",
request.ChannelNumber,
distinct[0]);
}
else
{
_logger.LogInformation(
"No content on channel {ChannelNumber} has frame rate information; will not normalize",
request.ChannelNumber);
}
return None;
}
@@ -1,5 +1,7 @@
using ErsatzTV.Core.Iptv;
using ErsatzTV.Core;
using ErsatzTV.Core.Iptv;
namespace ErsatzTV.Application.Channels;
public record GetChannelGuide(string Scheme, string Host) : IRequest<ChannelGuide>;
public record GetChannelGuide(string Scheme, string Host, string BaseUrl, string AccessToken)
: IRequest<Either<BaseError, ChannelGuide>>;
@@ -1,21 +1,87 @@
using ErsatzTV.Core.Interfaces.Repositories;
using System.Collections.Immutable;
using System.Text;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Iptv;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.IO;
namespace ErsatzTV.Application.Channels;
public class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, ChannelGuide>
public class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, Either<BaseError, ChannelGuide>>
{
private readonly IChannelRepository _channelRepository;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ILocalFileSystem _localFileSystem;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
public GetChannelGuideHandler(IChannelRepository channelRepository, RecyclableMemoryStreamManager recyclableMemoryStreamManager)
public GetChannelGuideHandler(
IDbContextFactory<TvContext> dbContextFactory,
RecyclableMemoryStreamManager recyclableMemoryStreamManager,
ILocalFileSystem localFileSystem)
{
_channelRepository = channelRepository;
_dbContextFactory = dbContextFactory;
_recyclableMemoryStreamManager = recyclableMemoryStreamManager;
_localFileSystem = localFileSystem;
}
public Task<ChannelGuide> Handle(GetChannelGuide request, CancellationToken cancellationToken) =>
_channelRepository.GetAllForGuide()
.Map(channels => new ChannelGuide(_recyclableMemoryStreamManager, request.Scheme, request.Host, channels));
public async Task<Either<BaseError, ChannelGuide>> Handle(
GetChannelGuide request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
var inactiveChannelNumbers = dbContext.Channels
.Where(c => c.ActiveMode != ChannelActiveMode.Active)
.Select(c => c.Number)
.AsEnumerable()
.Select(n => $"{n}.xml")
.ToImmutableHashSet();
string channelsFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, "channels.xml");
if (!_localFileSystem.FileExists(channelsFile))
{
return BaseError.New($"Required file {channelsFile} is missing");
}
long mtime = File.GetLastWriteTime(channelsFile).Ticks;
var accessTokenUri = $"?v={mtime}";
if (!string.IsNullOrWhiteSpace(request.AccessToken))
{
accessTokenUri += $"&amp;access_token={request.AccessToken}";
}
string channelsFragment = await File.ReadAllTextAsync(channelsFile, Encoding.UTF8, cancellationToken);
// TODO: is regex faster?
channelsFragment = channelsFragment
.Replace("{RequestBase}", $"{request.Scheme}://{request.Host}{request.BaseUrl}")
.Replace("{AccessTokenUri}", accessTokenUri);
var channelDataFragments = new Dictionary<string, string>();
foreach (string fileName in _localFileSystem.ListFiles(FileSystemLayout.ChannelGuideCacheFolder))
{
if (fileName.Contains("channels"))
{
continue;
}
if (inactiveChannelNumbers.Contains(Path.GetFileName(fileName)))
{
continue;
}
string channelDataFragment = await File.ReadAllTextAsync(fileName, Encoding.UTF8, cancellationToken);
channelDataFragment = channelDataFragment
.Replace("{RequestBase}", $"{request.Scheme}://{request.Host}{request.BaseUrl}")
.Replace("{AccessTokenUri}", accessTokenUri);
channelDataFragments.Add(Path.GetFileNameWithoutExtension(fileName), channelDataFragment);
}
return new ChannelGuide(_recyclableMemoryStreamManager, channelsFragment, channelDataFragments);
}
}
@@ -1,4 +1,5 @@
using ErsatzTV.Core.Hdhr;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Hdhr;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Channels;
@@ -11,5 +12,6 @@ public class GetChannelLineupHandler : IRequestHandler<GetChannelLineup, List<Li
public Task<List<LineupItem>> Handle(GetChannelLineup request, CancellationToken cancellationToken) =>
_channelRepository.GetAll()
.Map(channels => channels.Map(c => new LineupItem(request.Scheme, request.Host, c)).ToList());
.Map(channels => channels.Where(c => c.ActiveMode is ChannelActiveMode.Active)
.Map(c => new LineupItem(request.Scheme, request.Host, c)).ToList());
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record GetChannelNameByPlayoutId(int PlayoutId) : IRequest<Option<string>>;
@@ -0,0 +1,22 @@
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Channels;
public class GetChannelNameByPlayoutIdHandler : IRequestHandler<GetChannelNameByPlayoutId, Option<string>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
public GetChannelNameByPlayoutIdHandler(IDbContextFactory<TvContext> dbContextFactory) =>
_dbContextFactory = dbContextFactory;
public async Task<Option<string>> Handle(GetChannelNameByPlayoutId request, CancellationToken cancellationToken)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
return await dbContext.Playouts
.Include(p => p.Channel)
.SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId)
.MapT(p => p.Channel.Name);
}
}
@@ -1,5 +1,11 @@
using ErsatzTV.Core.Iptv;
using ErsatzTV.Core.Iptv;
namespace ErsatzTV.Application.Channels;
public record GetChannelPlaylist(string Scheme, string Host, string Mode) : IRequest<ChannelPlaylist>;
public record GetChannelPlaylist(
string Scheme,
string Host,
string BaseUrl,
string Mode,
string UserAgent,
string AccessToken) : IRequest<ChannelPlaylist>;
@@ -1,4 +1,4 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Iptv;
@@ -14,19 +14,34 @@ public class GetChannelPlaylistHandler : IRequestHandler<GetChannelPlaylist, Cha
public Task<ChannelPlaylist> Handle(GetChannelPlaylist request, CancellationToken cancellationToken) =>
_channelRepository.GetAll()
.Map(channels => EnsureMode(channels, request.Mode))
.Map(channels => new ChannelPlaylist(request.Scheme, request.Host, channels));
.Map(channels => new ChannelPlaylist(
request.Scheme,
request.Host,
request.BaseUrl,
channels,
request.UserAgent,
request.AccessToken));
private static List<Channel> EnsureMode(IEnumerable<Channel> channels, string mode)
{
var result = new List<Channel>();
foreach (Channel channel in channels)
{
if (channel.ActiveMode is not ChannelActiveMode.Active)
{
continue;
}
switch (mode.ToLowerInvariant())
{
case "segmenter":
channel.StreamingMode = StreamingMode.HttpLiveStreamingSegmenter;
result.Add(channel);
break;
case "segmenter-v2":
channel.StreamingMode = StreamingMode.HttpLiveStreamingSegmenterV2;
result.Add(channel);
break;
case "hls-direct":
channel.StreamingMode = StreamingMode.HttpLiveStreamingDirect;
result.Add(channel);
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record GetChannelResolutionAndBitrate(string ChannelNumber) : IRequest<Option<ResolutionAndBitrateViewModel>>;
@@ -0,0 +1,27 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Channels;
public class GetChannelResolutionAndBitrateHandler(IDbContextFactory<TvContext> dbContextFactory)
: IRequestHandler<GetChannelResolutionAndBitrate, Option<ResolutionAndBitrateViewModel>>
{
public async Task<Option<ResolutionAndBitrateViewModel>> Handle(
GetChannelResolutionAndBitrate request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
Option<Channel> maybeChannel = await dbContext.Channels
.AsNoTracking()
.Include(c => c.FFmpegProfile)
.ThenInclude(ff => ff.Resolution)
.SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber);
return maybeChannel.Map(c => Mapper.ProjectToViewModel(
c.FFmpegProfile.Resolution,
(int)((c.FFmpegProfile.VideoBitrate * 1000 + c.FFmpegProfile.AudioBitrate * 1000) * 1.2)));
}
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record GetChannelStreamSelectors : IRequest<List<string>>;
@@ -0,0 +1,14 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.Metadata;
namespace ErsatzTV.Application.Channels;
public class GetChannelStreamSelectorsHandler(ILocalFileSystem localFileSystem)
: IRequestHandler<GetChannelStreamSelectors, List<string>>
{
public Task<List<string>> Handle(GetChannelStreamSelectors request, CancellationToken cancellationToken) =>
localFileSystem.ListFiles(FileSystemLayout.ChannelStreamSelectorsFolder)
.Map(Path.GetFileName)
.ToList()
.AsTask();
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record ResolutionAndBitrateViewModel(int Height, int Width, int Bitrate);
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Channels;
public record ResolutionViewModel(int Height, int Width);
@@ -0,0 +1,14 @@
using System.Net;
using Scriban;
using Scriban.Parsing;
namespace ErsatzTV.Application.Channels;
public class XmlTemplateContext : TemplateContext
{
public override TemplateContext Write(SourceSpan span, object textAsObject)
=> base.Write(span, textAsObject is string text ? WebUtility.HtmlEncode(text) : textAsObject);
public override ValueTask<TemplateContext> WriteAsync(SourceSpan span, object textAsObject)
=> base.WriteAsync(span, textAsObject is string text ? WebUtility.HtmlEncode(text) : textAsObject);
}
@@ -2,4 +2,4 @@
namespace ErsatzTV.Application.Configuration;
public record SaveConfigElementByKey(ConfigElementKey Key, string Value) : IRequest<Unit>;
public record SaveConfigElementByKey(ConfigElementKey Key, string Value) : IRequest;
@@ -2,16 +2,13 @@
namespace ErsatzTV.Application.Configuration;
public class SaveConfigElementByKeyHandler : IRequestHandler<SaveConfigElementByKey, Unit>
public class SaveConfigElementByKeyHandler : IRequestHandler<SaveConfigElementByKey>
{
private readonly IConfigElementRepository _configElementRepository;
public SaveConfigElementByKeyHandler(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
public async Task<Unit> Handle(SaveConfigElementByKey request, CancellationToken cancellationToken)
{
public async Task Handle(SaveConfigElementByKey request, CancellationToken cancellationToken) =>
await _configElementRepository.Upsert(request.Key, request.Value);
return Unit.Default;
}
}
@@ -16,16 +16,15 @@ public class UpdateLibraryRefreshIntervalHandler :
UpdateLibraryRefreshInterval request,
CancellationToken cancellationToken) =>
Validate(request)
.MapT(
_ => _configElementRepository.Upsert(
ConfigElementKey.LibraryRefreshInterval,
request.LibraryRefreshInterval))
.MapT(_ => _configElementRepository.Upsert(
ConfigElementKey.LibraryRefreshInterval,
request.LibraryRefreshInterval))
.Bind(v => v.ToEitherAsync());
private static Task<Validation<BaseError, Unit>> Validate(UpdateLibraryRefreshInterval request) =>
Optional(request.LibraryRefreshInterval)
.Where(lri => lri > 0)
.Where(lri => lri is >= 0 and < 1_000_000)
.Map(_ => Unit.Default)
.ToValidation<BaseError>("Tuner count must be greater than zero")
.ToValidation<BaseError>("Library refresh interval must be zero or greated")
.AsTask();
}
@@ -0,0 +1,5 @@
using ErsatzTV.Core;
namespace ErsatzTV.Application.Configuration;
public record UpdateLoggingSettings(LoggingSettingsViewModel LoggingSettings) : IRequest<Either<BaseError, Unit>>;
@@ -0,0 +1,51 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Configuration;
public class UpdateLoggingSettingsHandler : IRequestHandler<UpdateLoggingSettings, Either<BaseError, Unit>>
{
private readonly IConfigElementRepository _configElementRepository;
private readonly LoggingLevelSwitches _loggingLevelSwitches;
public UpdateLoggingSettingsHandler(
LoggingLevelSwitches loggingLevelSwitches,
IConfigElementRepository configElementRepository)
{
_loggingLevelSwitches = loggingLevelSwitches;
_configElementRepository = configElementRepository;
}
public async Task<Either<BaseError, Unit>> Handle(
UpdateLoggingSettings request,
CancellationToken cancellationToken) => await ApplyUpdate(request.LoggingSettings);
private async Task<Unit> ApplyUpdate(LoggingSettingsViewModel loggingSettings)
{
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, loggingSettings.DefaultMinimumLogLevel);
_loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = loggingSettings.DefaultMinimumLogLevel;
await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelScanning,
loggingSettings.ScanningMinimumLogLevel);
_loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = loggingSettings.ScanningMinimumLogLevel;
await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelScheduling,
loggingSettings.SchedulingMinimumLogLevel);
_loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = loggingSettings.SchedulingMinimumLogLevel;
await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelStreaming,
loggingSettings.StreamingMinimumLogLevel);
_loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = loggingSettings.StreamingMinimumLogLevel;
await _configElementRepository.Upsert(
ConfigElementKey.MinimumLogLevelHttp,
loggingSettings.HttpMinimumLogLevel);
_loggingLevelSwitches.HttpLevelSwitch.MinimumLevel = loggingSettings.HttpMinimumLogLevel;
return Unit.Default;
}
}
@@ -1,4 +1,5 @@
using System.Threading.Channels;
using System.Globalization;
using System.Threading.Channels;
using ErsatzTV.Application.Playouts;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
@@ -45,7 +46,8 @@ public class UpdatePlayoutSettingsHandler : IRequestHandler<UpdatePlayoutSetting
List<Playout> playouts = await dbContext.Playouts
.Include(p => p.Channel)
.ToListAsync();
foreach (int playoutId in playouts.OrderBy(p => decimal.Parse(p.Channel.Number)).Map(p => p.Id))
foreach (int playoutId in playouts.OrderBy(p => decimal.Parse(p.Channel.Number, CultureInfo.InvariantCulture))
.Map(p => p.Id))
{
await _workerChannel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Continue));
}
@@ -0,0 +1,5 @@
using ErsatzTV.Core;
namespace ErsatzTV.Application.Configuration;
public record UpdateXmltvSettings(XmltvSettingsViewModel XmltvSettings) : IRequest<Either<BaseError, Unit>>;
@@ -0,0 +1,49 @@
using System.Threading.Channels;
using ErsatzTV.Application.Channels;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Configuration;
public class UpdateXmltvSettingsHandler(
IDbContextFactory<TvContext> dbContextFactory,
IConfigElementRepository configElementRepository,
ChannelWriter<IBackgroundServiceRequest> workerChannel)
: IRequestHandler<UpdateXmltvSettings, Either<BaseError, Unit>>
{
public async Task<Either<BaseError, Unit>> Handle(
UpdateXmltvSettings request,
CancellationToken cancellationToken)
{
int playoutDaysToBuild =
await configElementRepository
.GetValue<int>(ConfigElementKey.PlayoutDaysToBuild)
.IfNoneAsync(2);
if (playoutDaysToBuild < request.XmltvSettings.DaysToBuild)
{
return BaseError.New(
$"XMLTV days to build ({request.XmltvSettings.DaysToBuild}) cannot be greater than Playout days to build ({playoutDaysToBuild})");
}
return await ApplyUpdate(request.XmltvSettings);
}
private async Task<Unit> ApplyUpdate(XmltvSettingsViewModel xmltvSettings)
{
await configElementRepository.Upsert(ConfigElementKey.XmltvTimeZone, xmltvSettings.TimeZone);
await configElementRepository.Upsert(ConfigElementKey.XmltvDaysToBuild, xmltvSettings.DaysToBuild);
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync();
foreach (string channelNumber in await dbContext.Channels.Map(c => c.Number).ToListAsync())
{
await workerChannel.WriteAsync(new RefreshChannelData(channelNumber));
}
return Unit.Default;
}
}
@@ -0,0 +1,12 @@
using Serilog.Events;
namespace ErsatzTV.Application.Configuration;
public class LoggingSettingsViewModel
{
public LogEventLevel DefaultMinimumLogLevel { get; set; }
public LogEventLevel ScanningMinimumLogLevel { get; set; }
public LogEventLevel SchedulingMinimumLogLevel { get; set; }
public LogEventLevel StreamingMinimumLogLevel { get; set; }
public LogEventLevel HttpMinimumLogLevel { get; set; }
}
@@ -13,5 +13,5 @@ public class GetConfigElementByKeyHandler : IRequestHandler<GetConfigElementByKe
public Task<Option<ConfigElementViewModel>> Handle(
GetConfigElementByKey request,
CancellationToken cancellationToken) =>
_configElementRepository.Get(request.Key).MapT(ProjectToViewModel);
_configElementRepository.GetConfigElement(request.Key).MapT(ProjectToViewModel);
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Configuration;
public record GetLoggingSettings : IRequest<LoggingSettingsViewModel>;
@@ -0,0 +1,40 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using Serilog.Events;
namespace ErsatzTV.Application.Configuration;
public class GetLoggingSettingsHandler : IRequestHandler<GetLoggingSettings, LoggingSettingsViewModel>
{
private readonly IConfigElementRepository _configElementRepository;
public GetLoggingSettingsHandler(IConfigElementRepository configElementRepository) =>
_configElementRepository = configElementRepository;
public async Task<LoggingSettingsViewModel> Handle(GetLoggingSettings request, CancellationToken cancellationToken)
{
Option<LogEventLevel> maybeDefaultLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
Option<LogEventLevel> maybeScanningLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScanning);
Option<LogEventLevel> maybeSchedulingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScheduling);
Option<LogEventLevel> maybeStreamingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelStreaming);
Option<LogEventLevel> maybeHttpLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelHttp);
return new LoggingSettingsViewModel
{
DefaultMinimumLogLevel = await maybeDefaultLevel.IfNoneAsync(LogEventLevel.Information),
ScanningMinimumLogLevel = await maybeScanningLevel.IfNoneAsync(LogEventLevel.Information),
SchedulingMinimumLogLevel = await maybeSchedulingLevel.IfNoneAsync(LogEventLevel.Information),
StreamingMinimumLogLevel = await maybeStreamingLevel.IfNoneAsync(LogEventLevel.Information),
HttpMinimumLogLevel = await maybeHttpLevel.IfNoneAsync(LogEventLevel.Information)
};
}
}
@@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Configuration;
public record GetXmltvSettings : IRequest<XmltvSettingsViewModel>;
@@ -0,0 +1,22 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Configuration;
public class GetXmltvSettingsHandler(IConfigElementRepository configElementRepository)
: IRequestHandler<GetXmltvSettings, XmltvSettingsViewModel>
{
public async Task<XmltvSettingsViewModel> Handle(GetXmltvSettings request, CancellationToken cancellationToken)
{
Option<int> daysToBuild = await configElementRepository.GetValue<int>(ConfigElementKey.XmltvDaysToBuild);
Option<XmltvTimeZone> maybeTimeZone =
await configElementRepository.GetValue<XmltvTimeZone>(ConfigElementKey.XmltvTimeZone);
return new XmltvSettingsViewModel
{
DaysToBuild = await daysToBuild.IfNoneAsync(2),
TimeZone = await maybeTimeZone.IfNoneAsync(XmltvTimeZone.Local)
};
}
}
@@ -0,0 +1,7 @@
namespace ErsatzTV.Application.Configuration;
public class XmltvSettingsViewModel
{
public int DaysToBuild { get; set; }
public XmltvTimeZone TimeZone { get; set; }
}
@@ -0,0 +1,7 @@
namespace ErsatzTV.Application.Configuration;
public enum XmltvTimeZone
{
Local = 0,
Utc = 1
}
@@ -0,0 +1,83 @@
using System.Globalization;
using System.Threading.Channels;
using ErsatzTV.Application.Libraries;
using ErsatzTV.Core;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.FFmpeg.Runtime;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Emby;
public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler<SynchronizeEmbyCollections>,
IRequestHandler<SynchronizeEmbyCollections, Either<BaseError, Unit>>
{
public CallEmbyCollectionScannerHandler(
IDbContextFactory<TvContext> dbContextFactory,
IConfigElementRepository configElementRepository,
ChannelWriter<ISearchIndexBackgroundServiceRequest> channel,
IMediator mediator,
IRuntimeInfo runtimeInfo) : base(dbContextFactory, configElementRepository, channel, mediator, runtimeInfo)
{
}
public async Task<Either<BaseError, Unit>>
Handle(SynchronizeEmbyCollections request, CancellationToken cancellationToken)
{
Validation<BaseError, string> validation = await Validate(request);
return await validation.Match(
scanner => PerformScan(scanner, request, cancellationToken),
error =>
{
foreach (ScanIsNotRequired scanIsNotRequired in error.OfType<ScanIsNotRequired>())
{
return Task.FromResult<Either<BaseError, Unit>>(scanIsNotRequired);
}
return Task.FromResult<Either<BaseError, Unit>>(error.Join());
});
}
protected override async Task<DateTimeOffset> GetLastScan(TvContext dbContext, SynchronizeEmbyCollections request)
{
DateTime minDateTime = await dbContext.EmbyMediaSources
.SelectOneAsync(l => l.Id, l => l.Id == request.EmbyMediaSourceId)
.Match(l => l.LastCollectionsScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc);
return new DateTimeOffset(minDateTime, TimeSpan.Zero);
}
protected override bool ScanIsRequired(
DateTimeOffset lastScan,
int libraryRefreshInterval,
SynchronizeEmbyCollections request)
{
if (lastScan == SystemTime.MaxValueUtc)
{
return false;
}
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
}
private async Task<Either<BaseError, Unit>> PerformScan(
string scanner,
SynchronizeEmbyCollections request,
CancellationToken cancellationToken)
{
var arguments = new List<string>
{
"scan-emby-collections", request.EmbyMediaSourceId.ToString(CultureInfo.InvariantCulture)
};
if (request.ForceScan)
{
arguments.Add("--force");
}
return await base.PerformScan(scanner, arguments, cancellationToken).MapT(_ => Unit.Default);
}
}
@@ -0,0 +1,102 @@
using System.Globalization;
using System.Threading.Channels;
using ErsatzTV.Application.Libraries;
using ErsatzTV.Core;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.FFmpeg.Runtime;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.Emby;
public class CallEmbyLibraryScannerHandler : CallLibraryScannerHandler<ISynchronizeEmbyLibraryById>,
IRequestHandler<ForceSynchronizeEmbyLibraryById, Either<BaseError, string>>,
IRequestHandler<SynchronizeEmbyLibraryByIdIfNeeded, Either<BaseError, string>>
{
public CallEmbyLibraryScannerHandler(
IDbContextFactory<TvContext> dbContextFactory,
IConfigElementRepository configElementRepository,
ChannelWriter<ISearchIndexBackgroundServiceRequest> channel,
IMediator mediator,
IRuntimeInfo runtimeInfo)
: base(dbContextFactory, configElementRepository, channel, mediator, runtimeInfo)
{
}
Task<Either<BaseError, string>> IRequestHandler<ForceSynchronizeEmbyLibraryById, Either<BaseError, string>>.Handle(
ForceSynchronizeEmbyLibraryById request,
CancellationToken cancellationToken) => Handle(request, cancellationToken);
Task<Either<BaseError, string>> IRequestHandler<SynchronizeEmbyLibraryByIdIfNeeded, Either<BaseError, string>>.
Handle(
SynchronizeEmbyLibraryByIdIfNeeded request,
CancellationToken cancellationToken) => Handle(request, cancellationToken);
private async Task<Either<BaseError, string>> Handle(
ISynchronizeEmbyLibraryById request,
CancellationToken cancellationToken)
{
Validation<BaseError, string> validation = await Validate(request);
return await validation.Match(
scanner => PerformScan(scanner, request, cancellationToken),
error =>
{
foreach (ScanIsNotRequired scanIsNotRequired in error.OfType<ScanIsNotRequired>())
{
return Task.FromResult<Either<BaseError, string>>(scanIsNotRequired);
}
return Task.FromResult<Either<BaseError, string>>(error.Join());
});
}
private async Task<Either<BaseError, string>> PerformScan(
string scanner,
ISynchronizeEmbyLibraryById request,
CancellationToken cancellationToken)
{
var arguments = new List<string>
{
"scan-emby", request.EmbyLibraryId.ToString(CultureInfo.InvariantCulture)
};
if (request.ForceScan)
{
arguments.Add("--force");
}
if (request.DeepScan)
{
arguments.Add("--deep");
}
return await base.PerformScan(scanner, arguments, cancellationToken);
}
protected override async Task<DateTimeOffset> GetLastScan(
TvContext dbContext,
ISynchronizeEmbyLibraryById request)
{
DateTime minDateTime = await dbContext.EmbyLibraries
.SelectOneAsync(l => l.Id, l => l.Id == request.EmbyLibraryId)
.Match(l => l.LastScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc);
return new DateTimeOffset(minDateTime, TimeSpan.Zero);
}
protected override bool ScanIsRequired(
DateTimeOffset lastScan,
int libraryRefreshInterval,
ISynchronizeEmbyLibraryById request)
{
if (lastScan == SystemTime.MaxValueUtc)
{
return false;
}
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
}
}
@@ -52,5 +52,5 @@ public class SaveEmbySecretsHandler : IRequestHandler<SaveEmbySecrets, Either<Ba
return Unit.Default;
}
private record Parameters(EmbySecrets Secrets, EmbyServerInformation ServerInformation);
private sealed record Parameters(EmbySecrets Secrets, EmbyServerInformation ServerInformation);
}
@@ -1,6 +1,6 @@
using ErsatzTV.Core;
using ErsatzTV.Core;
namespace ErsatzTV.Application.Emby;
public record SynchronizeEmbyCollections(int EmbyMediaSourceId) : IRequest<Either<BaseError, Unit>>,
IEmbyBackgroundServiceRequest;
public record SynchronizeEmbyCollections(int EmbyMediaSourceId, bool ForceScan) : IRequest<Either<BaseError, Unit>>,
IScannerBackgroundServiceRequest;
@@ -1,73 +0,0 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Emby;
using ErsatzTV.Core.Interfaces.Emby;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Emby;
public class SynchronizeEmbyCollectionsHandler : IRequestHandler<SynchronizeEmbyCollections, Either<BaseError, Unit>>
{
private readonly IEmbySecretStore _embySecretStore;
private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly IEmbyCollectionScanner _scanner;
public SynchronizeEmbyCollectionsHandler(
IMediaSourceRepository mediaSourceRepository,
IEmbySecretStore embySecretStore,
IEmbyCollectionScanner scanner)
{
_mediaSourceRepository = mediaSourceRepository;
_embySecretStore = embySecretStore;
_scanner = scanner;
}
public async Task<Either<BaseError, Unit>> Handle(
SynchronizeEmbyCollections request,
CancellationToken cancellationToken)
{
Validation<BaseError, ConnectionParameters> validation = await Validate(request);
return await validation.Match(
SynchronizeCollections,
error => Task.FromResult<Either<BaseError, Unit>>(error.Join()));
}
private Task<Validation<BaseError, ConnectionParameters>> Validate(SynchronizeEmbyCollections request) =>
MediaSourceMustExist(request)
.BindT(MediaSourceMustHaveActiveConnection)
.BindT(MediaSourceMustHaveApiKey);
private Task<Validation<BaseError, EmbyMediaSource>> MediaSourceMustExist(
SynchronizeEmbyCollections request) =>
_mediaSourceRepository.GetEmby(request.EmbyMediaSourceId)
.Map(o => o.ToValidation<BaseError>("Emby media source does not exist."));
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection(
EmbyMediaSource embyMediaSource)
{
Option<EmbyConnection> maybeConnection = embyMediaSource.Connections.HeadOrNone();
return maybeConnection.Map(connection => new ConnectionParameters(connection))
.ToValidation<BaseError>("Emby media source requires an active connection");
}
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveApiKey(
ConnectionParameters connectionParameters)
{
EmbySecrets secrets = await _embySecretStore.ReadSecrets();
return Optional(secrets.Address == connectionParameters.ActiveConnection.Address)
.Where(match => match)
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey })
.ToValidation<BaseError>("Emby media source requires an api key");
}
private async Task<Either<BaseError, Unit>> SynchronizeCollections(ConnectionParameters connectionParameters) =>
await _scanner.ScanCollections(
connectionParameters.ActiveConnection.Address,
connectionParameters.ApiKey);
private record ConnectionParameters(EmbyConnection ActiveConnection)
{
public string ApiKey { get; set; }
}
}
@@ -3,4 +3,4 @@
namespace ErsatzTV.Application.Emby;
public record SynchronizeEmbyLibraries(int EmbyMediaSourceId) : IRequest<Either<BaseError, Unit>>,
IEmbyBackgroundServiceRequest;
IScannerBackgroundServiceRequest;
@@ -92,7 +92,7 @@ public class SynchronizeEmbyLibrariesHandler : IRequestHandler<SynchronizeEmbyLi
toAdd,
toRemove,
toUpdate);
if (ids.Any())
if (ids.Count != 0)
{
await _searchIndex.RemoveItems(ids);
_searchIndex.Commit();
@@ -102,9 +102,7 @@ public class SynchronizeEmbyLibrariesHandler : IRequestHandler<SynchronizeEmbyLi
return Unit.Default;
}
private record ConnectionParameters(
EmbyMediaSource EmbyMediaSource,
EmbyConnection ActiveConnection)
private sealed record ConnectionParameters(EmbyMediaSource EmbyMediaSource, EmbyConnection ActiveConnection)
{
public string ApiKey { get; set; }
}
@@ -2,19 +2,20 @@
namespace ErsatzTV.Application.Emby;
public interface ISynchronizeEmbyLibraryById : IRequest<Either<BaseError, string>>,
IEmbyBackgroundServiceRequest
public interface ISynchronizeEmbyLibraryById : IRequest<Either<BaseError, string>>, IScannerBackgroundServiceRequest
{
int EmbyLibraryId { get; }
bool ForceScan { get; }
bool DeepScan { get; }
}
public record SynchronizeEmbyLibraryByIdIfNeeded(int EmbyLibraryId) : ISynchronizeEmbyLibraryById
{
public bool ForceScan => false;
public bool DeepScan => false;
}
public record ForceSynchronizeEmbyLibraryById(int EmbyLibraryId) : ISynchronizeEmbyLibraryById
public record ForceSynchronizeEmbyLibraryById(int EmbyLibraryId, bool DeepScan) : ISynchronizeEmbyLibraryById
{
public bool ForceScan => true;
}
@@ -1,207 +0,0 @@
using System.Threading.Channels;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Emby;
using ErsatzTV.Core.Interfaces.Emby;
using ErsatzTV.Core.Interfaces.Locking;
using ErsatzTV.Core.Interfaces.Repositories;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.Application.Emby;
public class SynchronizeEmbyLibraryByIdHandler :
IRequestHandler<ForceSynchronizeEmbyLibraryById, Either<BaseError, string>>,
IRequestHandler<SynchronizeEmbyLibraryByIdIfNeeded, Either<BaseError, string>>
{
private readonly IConfigElementRepository _configElementRepository;
private readonly IEmbyMovieLibraryScanner _embyMovieLibraryScanner;
private readonly IEmbySecretStore _embySecretStore;
private readonly IEmbyTelevisionLibraryScanner _embyTelevisionLibraryScanner;
private readonly ChannelWriter<IEmbyBackgroundServiceRequest> _embyWorkerChannel;
private readonly IEntityLocker _entityLocker;
private readonly ILibraryRepository _libraryRepository;
private readonly ILogger<SynchronizeEmbyLibraryByIdHandler> _logger;
private readonly IMediaSourceRepository _mediaSourceRepository;
public SynchronizeEmbyLibraryByIdHandler(
IMediaSourceRepository mediaSourceRepository,
IEmbySecretStore embySecretStore,
IEmbyMovieLibraryScanner embyMovieLibraryScanner,
IEmbyTelevisionLibraryScanner embyTelevisionLibraryScanner,
ILibraryRepository libraryRepository,
IEntityLocker entityLocker,
IConfigElementRepository configElementRepository,
ChannelWriter<IEmbyBackgroundServiceRequest> embyWorkerChannel,
ILogger<SynchronizeEmbyLibraryByIdHandler> logger)
{
_mediaSourceRepository = mediaSourceRepository;
_embySecretStore = embySecretStore;
_embyMovieLibraryScanner = embyMovieLibraryScanner;
_embyTelevisionLibraryScanner = embyTelevisionLibraryScanner;
_libraryRepository = libraryRepository;
_entityLocker = entityLocker;
_configElementRepository = configElementRepository;
_embyWorkerChannel = embyWorkerChannel;
_logger = logger;
}
public Task<Either<BaseError, string>> Handle(
ForceSynchronizeEmbyLibraryById request,
CancellationToken cancellationToken) => HandleImpl(request, cancellationToken);
public Task<Either<BaseError, string>> Handle(
SynchronizeEmbyLibraryByIdIfNeeded request,
CancellationToken cancellationToken) => HandleImpl(request, cancellationToken);
private async Task<Either<BaseError, string>>
HandleImpl(ISynchronizeEmbyLibraryById request, CancellationToken cancellationToken)
{
Validation<BaseError, RequestParameters> validation = await Validate(request);
return await validation.Match(
parameters => Synchronize(parameters, cancellationToken),
error => Task.FromResult<Either<BaseError, string>>(error.Join()));
}
private async Task<Either<BaseError, string>> Synchronize(
RequestParameters parameters,
CancellationToken cancellationToken)
{
try
{
var lastScan = new DateTimeOffset(parameters.Library.LastScan ?? SystemTime.MinValueUtc, TimeSpan.Zero);
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(parameters.LibraryRefreshInterval);
if (parameters.ForceScan || nextScan < DateTimeOffset.Now)
{
Either<BaseError, Unit> result = parameters.Library.MediaKind switch
{
LibraryMediaKind.Movies =>
await _embyMovieLibraryScanner.ScanLibrary(
parameters.ConnectionParameters.ActiveConnection.Address,
parameters.ConnectionParameters.ApiKey,
parameters.Library,
parameters.FFmpegPath,
parameters.FFprobePath,
cancellationToken),
LibraryMediaKind.Shows =>
await _embyTelevisionLibraryScanner.ScanLibrary(
parameters.ConnectionParameters.ActiveConnection.Address,
parameters.ConnectionParameters.ApiKey,
parameters.Library,
parameters.FFmpegPath,
parameters.FFprobePath,
cancellationToken),
_ => Unit.Default
};
if (result.IsRight)
{
parameters.Library.LastScan = DateTime.UtcNow;
await _libraryRepository.UpdateLastScan(parameters.Library);
await _embyWorkerChannel.WriteAsync(
new SynchronizeEmbyCollections(parameters.Library.MediaSourceId),
cancellationToken);
}
return result.Map(_ => parameters.Library.Name);
}
else
{
_logger.LogDebug("Skipping unforced scan of emby media library {Name}", parameters.Library.Name);
}
return parameters.Library.Name;
}
finally
{
_entityLocker.UnlockLibrary(parameters.Library.Id);
}
}
private async Task<Validation<BaseError, RequestParameters>> Validate(
ISynchronizeEmbyLibraryById request) =>
(await ValidateConnection(request), await EmbyLibraryMustExist(request),
await ValidateLibraryRefreshInterval(), await ValidateFFmpegPath(), await ValidateFFprobePath())
.Apply(
(connectionParameters, embyLibrary, libraryRefreshInterval, ffmpegPath, ffprobePath) =>
new RequestParameters(
connectionParameters,
embyLibrary,
request.ForceScan,
libraryRefreshInterval,
ffmpegPath,
ffprobePath
));
private Task<Validation<BaseError, ConnectionParameters>> ValidateConnection(
ISynchronizeEmbyLibraryById request) =>
EmbyMediaSourceMustExist(request)
.BindT(MediaSourceMustHaveActiveConnection)
.BindT(MediaSourceMustHaveApiKey);
private Task<Validation<BaseError, EmbyMediaSource>> EmbyMediaSourceMustExist(
ISynchronizeEmbyLibraryById request) =>
_mediaSourceRepository.GetEmbyByLibraryId(request.EmbyLibraryId)
.Map(
v => v.ToValidation<BaseError>(
$"Emby media source for library {request.EmbyLibraryId} does not exist."));
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection(
EmbyMediaSource embyMediaSource)
{
Option<EmbyConnection> maybeConnection = embyMediaSource.Connections.HeadOrNone();
return maybeConnection.Map(connection => new ConnectionParameters(embyMediaSource, connection))
.ToValidation<BaseError>("Emby media source requires an active connection");
}
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveApiKey(
ConnectionParameters connectionParameters)
{
EmbySecrets secrets = await _embySecretStore.ReadSecrets();
return Optional(secrets.Address == connectionParameters.ActiveConnection.Address)
.Where(match => match)
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey })
.ToValidation<BaseError>("Emby media source requires an api key");
}
private Task<Validation<BaseError, EmbyLibrary>> EmbyLibraryMustExist(
ISynchronizeEmbyLibraryById request) =>
_mediaSourceRepository.GetEmbyLibrary(request.EmbyLibraryId)
.Map(v => v.ToValidation<BaseError>($"Emby library {request.EmbyLibraryId} does not exist."));
private Task<Validation<BaseError, int>> ValidateLibraryRefreshInterval() =>
_configElementRepository.GetValue<int>(ConfigElementKey.LibraryRefreshInterval)
.FilterT(lri => lri > 0)
.Map(lri => lri.ToValidation<BaseError>("Library refresh interval is invalid"));
private Task<Validation<BaseError, string>> ValidateFFmpegPath() =>
_configElementRepository.GetValue<string>(ConfigElementKey.FFmpegPath)
.FilterT(File.Exists)
.Map(
ffmpegPath =>
ffmpegPath.ToValidation<BaseError>("FFmpeg path does not exist on the file system"));
private Task<Validation<BaseError, string>> ValidateFFprobePath() =>
_configElementRepository.GetValue<string>(ConfigElementKey.FFprobePath)
.FilterT(File.Exists)
.Map(
ffprobePath =>
ffprobePath.ToValidation<BaseError>("FFprobe path does not exist on the file system"));
private record RequestParameters(
ConnectionParameters ConnectionParameters,
EmbyLibrary Library,
bool ForceScan,
int LibraryRefreshInterval,
string FFmpegPath,
string FFprobePath);
private record ConnectionParameters(
EmbyMediaSource EmbyMediaSource,
EmbyConnection ActiveConnection)
{
public string ApiKey { get; set; }
}
}
@@ -8,15 +8,15 @@ namespace ErsatzTV.Application.Emby;
public class SynchronizeEmbyMediaSourcesHandler : IRequestHandler<SynchronizeEmbyMediaSources,
Either<BaseError, List<EmbyMediaSource>>>
{
private readonly ChannelWriter<IEmbyBackgroundServiceRequest> _channel;
private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
public SynchronizeEmbyMediaSourcesHandler(
IMediaSourceRepository mediaSourceRepository,
ChannelWriter<IEmbyBackgroundServiceRequest> channel)
ChannelWriter<IScannerBackgroundServiceRequest> scannerWorkerChannel)
{
_mediaSourceRepository = mediaSourceRepository;
_channel = channel;
_scannerWorkerChannel = scannerWorkerChannel;
}
public async Task<Either<BaseError, List<EmbyMediaSource>>> Handle(
@@ -26,8 +26,7 @@ public class SynchronizeEmbyMediaSourcesHandler : IRequestHandler<SynchronizeEmb
List<EmbyMediaSource> mediaSources = await _mediaSourceRepository.GetAllEmby();
foreach (EmbyMediaSource mediaSource in mediaSources)
{
// await _channel.WriteAsync(new SynchronizeEmbyAdminUserId(mediaSource.Id), cancellationToken);
await _channel.WriteAsync(new SynchronizeEmbyLibraries(mediaSource.Id), cancellationToken);
await _scannerWorkerChannel.WriteAsync(new SynchronizeEmbyLibraries(mediaSource.Id), cancellationToken);
}
return mediaSources;
@@ -2,7 +2,6 @@
namespace ErsatzTV.Application.Emby;
public record UpdateEmbyLibraryPreferences
(List<EmbyLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
public record UpdateEmbyLibraryPreferences(List<EmbyLibraryPreference> Preferences) : IRequest<Either<BaseError, Unit>>;
public record EmbyLibraryPreference(int Id, bool ShouldSyncItems);
@@ -6,7 +6,7 @@ namespace ErsatzTV.Application.Emby;
public class
UpdateEmbyLibraryPreferencesHandler : IRequestHandler<UpdateEmbyLibraryPreferences,
Either<BaseError, Unit>>
Either<BaseError, Unit>>
{
private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly ISearchIndex _searchIndex;
@@ -43,7 +43,6 @@ public class UpdateEmbyPathReplacementsHandler : IRequestHandler<UpdateEmbyPathR
private Task<Validation<BaseError, EmbyMediaSource>> EmbyMediaSourceMustExist(
UpdateEmbyPathReplacements request) =>
_mediaSourceRepository.GetEmby(request.EmbyMediaSourceId)
.Map(
v => v.ToValidation<BaseError>(
$"Emby media source {request.EmbyMediaSourceId} does not exist."));
.Map(v => v.ToValidation<BaseError>(
$"Emby media source {request.EmbyMediaSourceId} does not exist."));
}
@@ -1,3 +1,3 @@
namespace ErsatzTV.Application.Emby;
public record EmbyConnectionParametersViewModel(string Address);
public record EmbyConnectionParametersViewModel(string Address, string ApiKey);
@@ -4,9 +4,9 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Application.Emby;
public record EmbyLibraryViewModel(
int Id,
string Name,
LibraryMediaKind MediaKind,
bool ShouldSyncItems,
int MediaSourceId)
: LibraryViewModel("Emby", Id, Name, MediaKind, MediaSourceId);
int Id,
string Name,
LibraryMediaKind MediaKind,
bool ShouldSyncItems,
int MediaSourceId)
: LibraryViewModel("Emby", Id, Name, MediaKind, MediaSourceId, string.Empty);
@@ -1,5 +1,7 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Emby;
using ErsatzTV.Core.Interfaces.Emby;
using ErsatzTV.Core.Interfaces.Repositories;
using Microsoft.Extensions.Caching.Memory;
@@ -8,15 +10,18 @@ namespace ErsatzTV.Application.Emby;
public class GetEmbyConnectionParametersHandler : IRequestHandler<GetEmbyConnectionParameters,
Either<BaseError, EmbyConnectionParametersViewModel>>
{
private readonly IEmbySecretStore _embySecretStore;
private readonly IMediaSourceRepository _mediaSourceRepository;
private readonly IMemoryCache _memoryCache;
public GetEmbyConnectionParametersHandler(
IMemoryCache memoryCache,
IMediaSourceRepository mediaSourceRepository)
IMediaSourceRepository mediaSourceRepository,
IEmbySecretStore embySecretStore)
{
_memoryCache = memoryCache;
_mediaSourceRepository = mediaSourceRepository;
_embySecretStore = embySecretStore;
}
public async Task<Either<BaseError, EmbyConnectionParametersViewModel>> Handle(
@@ -30,7 +35,7 @@ public class GetEmbyConnectionParametersHandler : IRequestHandler<GetEmbyConnect
Either<BaseError, EmbyConnectionParametersViewModel> maybeParameters =
await Validate()
.MapT(cp => new EmbyConnectionParametersViewModel(cp.ActiveConnection.Address))
.MapT(cp => new EmbyConnectionParametersViewModel(cp.ActiveConnection.Address, cp.ApiKey))
.Map(v => v.ToEither<EmbyConnectionParametersViewModel>());
return maybeParameters.Match(
@@ -44,13 +49,13 @@ public class GetEmbyConnectionParametersHandler : IRequestHandler<GetEmbyConnect
private Task<Validation<BaseError, ConnectionParameters>> Validate() =>
EmbyMediaSourceMustExist()
.BindT(MediaSourceMustHaveActiveConnection);
.BindT(MediaSourceMustHaveActiveConnection)
.BindT(MediaSourceMustHaveApiKey);
private Task<Validation<BaseError, EmbyMediaSource>> EmbyMediaSourceMustExist() =>
_mediaSourceRepository.GetAllEmby().Map(list => list.HeadOrNone())
.Map(
v => v.ToValidation<BaseError>(
"Emby media source does not exist."));
.Map(v => v.ToValidation<BaseError>(
"Emby media source does not exist."));
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection(
EmbyMediaSource embyMediaSource)
@@ -60,7 +65,20 @@ public class GetEmbyConnectionParametersHandler : IRequestHandler<GetEmbyConnect
.ToValidation<BaseError>("Emby media source requires an active connection");
}
private record ConnectionParameters(
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveApiKey(
ConnectionParameters connectionParameters)
{
EmbySecrets secrets = await _embySecretStore.ReadSecrets();
return Optional(secrets.Address == connectionParameters.ActiveConnection.Address)
.Where(match => match)
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey })
.ToValidation<BaseError>("Emby media source requires an api key");
}
private sealed record ConnectionParameters(
EmbyMediaSource EmbyMediaSource,
EmbyConnection ActiveConnection);
EmbyConnection ActiveConnection)
{
public string ApiKey { get; set; }
}
}
@@ -1,4 +1,3 @@
namespace ErsatzTV.Application.Emby;
public record GetEmbyPathReplacementsBySourceId
(int EmbyMediaSourceId) : IRequest<List<EmbyPathReplacementViewModel>>;
public record GetEmbyPathReplacementsBySourceId(int EmbyMediaSourceId) : IRequest<List<EmbyPathReplacementViewModel>>;
@@ -1,28 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<NoWarn>VSTHRD200</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bugsnag" Version="3.0.1" />
<PackageReference Include="CliWrap" Version="3.4.4" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MediatR" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.2.32">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Winista.MimeDetect" Version="1.0.1" />
<PackageReference Include="Bugsnag" Version="4.0.0" />
<PackageReference Include="CliWrap" Version="3.9.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MediatR" Version="[12.5.0]" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" />
<PackageReference Include="WebMarkupMin.Core" Version="2.19.0" />
<PackageReference Include="Winista.MimeDetect" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ErsatzTV.Core\ErsatzTV.Core.csproj" />
<ProjectReference Include="..\ErsatzTV.Infrastructure\ErsatzTV.Infrastructure.csproj" />
<ProjectReference Include="..\ErsatzTV.Core\ErsatzTV.Core.csproj" />
<ProjectReference Include="..\ErsatzTV.Infrastructure\ErsatzTV.Infrastructure.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=artists_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=artworks_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=channels_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=channels_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=configuration_005Ccommands/@EntryIndexedValue">True</s:Boolean>
@@ -33,12 +34,18 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=plex_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=programschedules_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=programschedules_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=resolutions_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=resolutions_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=scheduling_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=scheduling_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=search_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=search_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=streaming_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=streaming_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=subtitles_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=television_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=templates_005Cqueries/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=troubleshooting_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=validators/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=watermarks_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=watermarks_005Cqueries/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -2,5 +2,4 @@
namespace ErsatzTV.Application.FFmpegProfiles;
public record CopyFFmpegProfile
(int FFmpegProfileId, string Name) : IRequest<Either<BaseError, FFmpegProfileViewModel>>;
public record CopyFFmpegProfile(int FFmpegProfileId, string Name) : IRequest<Either<BaseError, FFmpegProfileViewModel>>;
@@ -1,5 +1,7 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using static ErsatzTV.Application.FFmpegProfiles.Mapper;
namespace ErsatzTV.Application.FFmpegProfiles;
@@ -8,9 +10,13 @@ public class
CopyFFmpegProfileHandler : IRequestHandler<CopyFFmpegProfile, Either<BaseError, FFmpegProfileViewModel>>
{
private readonly IFFmpegProfileRepository _ffmpegProfileRepository;
private readonly ISearchTargets _searchTargets;
public CopyFFmpegProfileHandler(IFFmpegProfileRepository ffmpegProfileRepository) =>
public CopyFFmpegProfileHandler(IFFmpegProfileRepository ffmpegProfileRepository, ISearchTargets searchTargets)
{
_ffmpegProfileRepository = ffmpegProfileRepository;
_searchTargets = searchTargets;
}
public Task<Either<BaseError, FFmpegProfileViewModel>> Handle(
CopyFFmpegProfile request,
@@ -19,14 +25,17 @@ public class
.MapT(PerformCopy)
.Bind(v => v.ToEitherAsync());
private Task<FFmpegProfileViewModel> PerformCopy(CopyFFmpegProfile request) =>
_ffmpegProfileRepository.Copy(request.FFmpegProfileId, request.Name)
.Map(ProjectToViewModel);
private async Task<FFmpegProfileViewModel> PerformCopy(CopyFFmpegProfile request)
{
FFmpegProfile copy = await _ffmpegProfileRepository.Copy(request.FFmpegProfileId, request.Name);
_searchTargets.SearchTargetsChanged();
return ProjectToViewModel(copy);
}
private Task<Validation<BaseError, CopyFFmpegProfile>> Validate(CopyFFmpegProfile request) =>
private static Task<Validation<BaseError, CopyFFmpegProfile>> Validate(CopyFFmpegProfile request) =>
ValidateName(request).AsTask().MapT(_ => request);
private Validation<BaseError, string> ValidateName(CopyFFmpegProfile request) =>
private static Validation<BaseError, string> ValidateName(CopyFFmpegProfile request) =>
request.NotEmpty(x => x.Name)
.Bind(_ => request.NotLongerThan(50)(x => x.Name));
}
@@ -8,16 +8,24 @@ public record CreateFFmpegProfile(
string Name,
int ThreadCount,
HardwareAccelerationKind HardwareAcceleration,
string VaapiDisplay,
VaapiDriver VaapiDriver,
string VaapiDevice,
int? QsvExtraHardwareFrames,
int ResolutionId,
ScalingBehavior ScalingBehavior,
FFmpegProfileVideoFormat VideoFormat,
string VideoProfile,
string VideoPreset,
bool AllowBFrames,
FFmpegProfileBitDepth BitDepth,
int VideoBitrate,
int VideoBufferSize,
FFmpegProfileTonemapAlgorithm TonemapAlgorithm,
FFmpegProfileAudioFormat AudioFormat,
int AudioBitrate,
int AudioBufferSize,
bool NormalizeLoudness,
NormalizeLoudnessMode NormalizeLoudnessMode,
int AudioChannels,
int AudioSampleRate,
bool NormalizeFramerate,
@@ -1,5 +1,6 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
@@ -10,9 +11,13 @@ public class CreateFFmpegProfileHandler :
IRequestHandler<CreateFFmpegProfile, Either<BaseError, CreateFFmpegProfileResult>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ISearchTargets _searchTargets;
public CreateFFmpegProfileHandler(IDbContextFactory<TvContext> dbContextFactory) =>
public CreateFFmpegProfileHandler(IDbContextFactory<TvContext> dbContextFactory, ISearchTargets searchTargets)
{
_dbContextFactory = dbContextFactory;
_searchTargets = searchTargets;
}
public async Task<Either<BaseError, CreateFFmpegProfileResult>> Handle(
CreateFFmpegProfile request,
@@ -20,43 +25,50 @@ public class CreateFFmpegProfileHandler :
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, FFmpegProfile> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, profile => PersistFFmpegProfile(dbContext, profile));
return await validation.Apply(profile => PersistFFmpegProfile(dbContext, profile));
}
private static async Task<CreateFFmpegProfileResult> PersistFFmpegProfile(
private async Task<CreateFFmpegProfileResult> PersistFFmpegProfile(
TvContext dbContext,
FFmpegProfile ffmpegProfile)
{
await dbContext.FFmpegProfiles.AddAsync(ffmpegProfile);
await dbContext.SaveChangesAsync();
_searchTargets.SearchTargetsChanged();
return new CreateFFmpegProfileResult(ffmpegProfile.Id);
}
private async Task<Validation<BaseError, FFmpegProfile>> Validate(
private static async Task<Validation<BaseError, FFmpegProfile>> Validate(
TvContext dbContext,
CreateFFmpegProfile request) =>
(ValidateName(request), ValidateThreadCount(request), await ResolutionMustExist(dbContext, request))
.Apply(
(name, threadCount, resolutionId) => new FFmpegProfile
{
Name = name,
ThreadCount = threadCount,
HardwareAcceleration = request.HardwareAcceleration,
VaapiDriver = request.VaapiDriver,
VaapiDevice = request.VaapiDevice,
ResolutionId = resolutionId,
VideoFormat = request.VideoFormat,
VideoBitrate = request.VideoBitrate,
VideoBufferSize = request.VideoBufferSize,
AudioFormat = request.AudioFormat,
AudioBitrate = request.AudioBitrate,
AudioBufferSize = request.AudioBufferSize,
NormalizeLoudness = request.NormalizeLoudness,
AudioChannels = request.AudioChannels,
AudioSampleRate = request.AudioSampleRate,
NormalizeFramerate = request.NormalizeFramerate,
DeinterlaceVideo = request.DeinterlaceVideo
});
.Apply((name, threadCount, resolutionId) => new FFmpegProfile
{
Name = name,
ThreadCount = threadCount,
HardwareAcceleration = request.HardwareAcceleration,
VaapiDriver = request.VaapiDriver,
VaapiDevice = request.VaapiDevice,
QsvExtraHardwareFrames = request.QsvExtraHardwareFrames,
ResolutionId = resolutionId,
ScalingBehavior = request.ScalingBehavior,
VideoFormat = request.VideoFormat,
VideoProfile = request.VideoProfile,
VideoPreset = request.VideoPreset,
AllowBFrames = request.AllowBFrames,
BitDepth = request.BitDepth,
VideoBitrate = request.VideoBitrate,
VideoBufferSize = request.VideoBufferSize,
TonemapAlgorithm = request.TonemapAlgorithm,
AudioFormat = request.AudioFormat,
AudioBitrate = request.AudioBitrate,
AudioBufferSize = request.AudioBufferSize,
NormalizeLoudnessMode = request.NormalizeLoudnessMode,
AudioChannels = request.AudioChannels,
AudioSampleRate = request.AudioSampleRate,
NormalizeFramerate = request.NormalizeFramerate,
DeinterlaceVideo = request.DeinterlaceVideo
});
private static Validation<BaseError, string> ValidateName(CreateFFmpegProfile createFFmpegProfile) =>
createFFmpegProfile.NotEmpty(x => x.Name)
@@ -1,5 +1,6 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Infrastructure.Extensions;
using Microsoft.EntityFrameworkCore;
@@ -9,23 +10,28 @@ namespace ErsatzTV.Application.FFmpegProfiles;
public class DeleteFFmpegProfileHandler : IRequestHandler<DeleteFFmpegProfile, Either<BaseError, Unit>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly ISearchTargets _searchTargets;
public DeleteFFmpegProfileHandler(IDbContextFactory<TvContext> dbContextFactory) =>
public DeleteFFmpegProfileHandler(IDbContextFactory<TvContext> dbContextFactory, ISearchTargets searchTargets)
{
_dbContextFactory = dbContextFactory;
_searchTargets = searchTargets;
}
public async Task<Either<BaseError, Unit>> Handle(
DeleteFFmpegProfile request,
CancellationToken cancellationToken)
{
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, FFmpegProfile> validation = await FFmpegProfileMustExist(dbContext, request);
return await LanguageExtensions.Apply(validation, p => DoDeletion(dbContext, p));
return await validation.Apply(p => DoDeletion(dbContext, p));
}
private static async Task<Unit> DoDeletion(TvContext dbContext, FFmpegProfile ffmpegProfile)
private async Task<Unit> DoDeletion(TvContext dbContext, FFmpegProfile ffmpegProfile)
{
dbContext.FFmpegProfiles.Remove(ffmpegProfile);
await dbContext.SaveChangesAsync();
_searchTargets.SearchTargetsChanged();
return Unit.Default;
}

Some files were not shown because too many files have changed in this diff Show More