Jellyfin Plugin Development
Jellyfin 으로 music media server 를 구축하려고 하는데, plugin 생태계가 생각보다 부족함. 그래서 원하는 것을 직접 만들 수 밖에 없는데, 이에 대한 문서가 마땅히 존재하지 않음. 필요한 자료들을 여기에 정리함.
우선 Jellyfin 은 C# 기반의 프레임워크라서 언어는 C#, web service 로서 ASP 를 사용해야함. 웹도 모르고 C# 도 무지렁이라서 읽는 대상은 아무것도 모르는 사람임.
C# programming
nullable type
간단하게 말하면 Type? 의 형태로 사용하는데 이렇게 만든 변수는 null 을 가질 수 있음.
int a? = null;
int b = null; // error
심화로 null instance 검사 ,boxing/unboxing 등이 있으니 아래 문서를 참고 MS offical docs
static const
A const object is always static…
Elapsed time 측정
C# system library 로서 제공됨. StopWatch class
exception
c++ 과 크게 다를 바 없음.
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
rethrow
catch(){
throw
}
프로젝트 구성 Jellyfin Template
jellyfin template 이라는 프로젝트가 존재함. 여기서 프로젝트를 그대로 가져와서 사용하면 된다.
현 프로젝트에 대해 linter 적용
$ dotnet format
현 프로젝트 빌드
$ dotnet build
현 프로젝트를 빌드하고 배포에 필요한 dependecy 파일들 모두 생성.
$ dotnet publish
package dependency 추가
$ dotnet add package PACKAGE_NAME
ASP.NET Core
Dependency Injection
Software engineering 에서 loosely coupled program 을 만들기 위해 고안된 개념임. 어떤 object 나 function 이 어떤 서비스 ( interface) 를 요청하게 되고 여기서 client 가 됨. 그러면 injector 가 만들어서 보내주는 형태. 즉, 어떤 object 나 function 은 구체적으로 요청하는 object 를 어떻게 만들지 알 필요가 없음.
MS 공식문서를 차근차근 보면 어떻게 dependency 를 제거할 수 있는지 예제를 통해 이해할 수 있음.
ASP.NET web api design
design docs for beginners tutorials for beginners
ControllerBase
ASP web api 문서
Controller 는 url 에 따른 처리기다.
아래처럼 정의하게 되면,
Route 에 기술된 url( [controller] 는 해당 class 에서 Controller 를 제외한 문자열을 의미함) 에 대한 처리를
이 클래스에서 담당하게 된다.
즉, https://localhost/weatherforcast/
에 대한 요청을 처리하게 된다.
method 에서 HttpGet("forecast")
으로 등록했기 때문에 https://localhost/weatherforcast/forecast
url
에 대한 http get response 를 주게 된다.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("forecast")]
public async Task<IActionResult> forecast()
{
...
}
}
##
Selenium
Selelnium 은 browser 를 프로그래밍으로 제어할 수 있게 해주는 library다. 예를 들어, Selenium 을 이용하면 어떤 url 에 접속했을 때 특정 attribute 을 가진 버튼을 찾아서 클릭하는 것이 가능해진다. 이런 이점 때문에 실제로 web , browser test 툴로서도 사용되고 있다.
driver 는 browser 를 제어하게 해주는 layer 로 해당 browser vender 가 제공한다. 예를 들어, google-chrome 의 경우에는 chromedriver 라는 파일을 제공하고, firefox 는 gecko driver 를 제공한다.
Raspberry pi
raspberry pi 에서는 chrome 이 설치되지 않는다. firefox 는 esr 만 package 관리자가 제공해준다.
Install FireFox ESR
firefox 는 RR 과 ESR 로 나뉘는데, RR 은 Rapid Rease 로 4주에 한번씩 major update 가 발생함. 반면에, ESR 은 Extended Support Release 의 약자로 42주에 한번씩 major update 가, minor update 는 4주에 한번씩 발생하기 때문에 굉장히 보수적인 릴리즈라고 할수 있음.
$ apt install firefox-esr
Install gecko driver
gecko driver github에서 aarch64 로 빌드된 최신 asset 을 다운받는다.
그리고 압축을 해제하면 단일 바이너리 geckodriver
가 존재하고, 적절한 위치에 옮겨둔다.
어차피 이 위치는 Selenium code 에서 직접 지정해줄 예정이다.
Usage code
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
...
var options = new FirefoxOptions();
options.AddArgument("--headless");
options.AddArgument("--disable-gpu");
options.AddArgument("window-size=1920x1080");
options.AddArgument("--start-maximized");
FirefoxProfile profile = new FirefoxProfile();
const string download_dir = "/music";
profile.SetPreference("browser.download.folderList",2); //Use for the default download directory the last folder specified for a download
profile.SetPreference("browser.download.dir", download_dir);
// profile.SetPreference("browser.download.manager.showWhenStarting", false);
profile.SetPreference("browser.helperApps.neverAsk.saveToDisk", "*/*");
options.Profile = profile;
var driver = new FirefoxDriver("/jellyfin/geckodriver", options);
옵션이 꽤나 많은데, –headless 는 browser 창을 띄우지 않겠다는 의미다. 가능한 옵션들은 해당 binary 의 옵션이기 때문에 firefox –help 를 해서 가능한게 있는지 확인하라. disable-gpu 는 사실 chrome 의 옵션이라 이런식으로 작성하면 모르는 option 이라는 warning 이 발생한다. FirefoxProfile 은 실제 firefox browser 에서 preference 를 수정하겠다는 의미다. 이는 직접 브라우저에서도 설정할 수 있으며, 따라서 key 는 browser 에서 찾아오면 된다. 혹은 mozilla docs서 확인
Preference browser.download.manager.showWhenStarting may not be overridden
라는 에러가 나오면,
이미 설정되어있는 값인지를 확인해봐라. 애초에 frozen 인 것은 디자인상 그렇게 되어야하는
속성이기 때문이다.
Deployment
아래처럼 빌드하게 되면 dependency package 까지 함께 제공되기 때문에 배포하기에 좋다. 특히 Selenium WebDriver 를 의존하고 있는데, 아래 커맨드를 수행하게 되면 WebDriver.Dll 도 함께 나오며, jellyfin plugin directory 에 함께 넣기만 하면 되서 편리함. 시스템에 설치할 필요가 없음.
$ dotnet publish
jellyfin config plugins 에 본인의 디렉토리를 생성하고 생성된 프로젝트의 dll 과 WebDriver 를 함께 넣기만 하면 된다.