에어소닉을 사용해본 사람이라면 알겠지만
에어소닉에는 플레이리스트를 정렬할 수 있는 방법이 하나밖에 없다.
바로 손으로 일일히 하나하나 정리하는 것이다.

 

그냥 앨범별, 폴더별, 아티스트별 이렇게 간단하게 정리해줬으면 좋겠는데 기능도 없고
그렇다고 하나하나 정리하기에는 내 플레이리스트에 있는 노래가 너무 많아졌다...
그래서 플레이리스트와 별표된 노래를 정렬할 수 있는 방법을 만들려고 한다.

SERVER='http://localhost/airsonic'
CLIENT='CLI'
USERNAME='admin'
PASSWORD='admin'
SALT="$(openssl rand -hex 20)"
TOKEN="$(echo -n "${PASSWORD}${SALT}" | md5sum | awk '{ print $1 }')"
echo ${SALT}
echo ${TOKEN}
curl "${SERVER}/rest/getPlaylists?u=${USERNAME}&t=${TOKEN}&s=${SALT}&v=1.15.0&c=${CLIENT}"

위의 명령어를 사용하면 간단하게 API를 날리 수 있따.

우선 플레이리스트의 목록을 가져왔다.

플레이리스트의 id를 알아야 플레이리스트의 노래목록을 가져올 수 있기 때문이다.

<?xml version="1.0" encoding="UTF-8"?>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.15.0">
   <playlists>
      <playlist id="4" name="aa" comment="" owner="admin" public="false" songCount="148" duration="38279" created="2021-04-24T16:32:28.549Z" changed="2021-07-08T23:39:25.343Z" coverArt="pl-4"/>
      <playlist id="7" name="aaa" owner="admin" public="false" songCount="20" duration="5462" created="2021-06-21T08:10:09.487Z" changed="2021-07-08T23:39:30.937Z" coverArt="pl-7"/>
      <playlist id="6" name="아이카츠" owner="admin" public="false" songCount="113" duration="29959" created="2021-05-20T01:30:47.781Z" changed="2021-05-20T01:31:05.752Z" coverArt="pl-6"/>
      <playlist id="5" name="프리파라" owner="admin" public="false" songCount="133" duration="29424" created="2021-05-12T00:12:54.650Z" changed="2021-07-02T04:50:00.105Z" coverArt="pl-5"/>
   </playlists>
</subsonic-response>

 

그렇게 가져온 플레이리스트 ID로 플레이리스트를 가져오면 이런 결과가 나온다.

 

curl "${SERVER}/rest/getPlaylist?u=${USERNAME}&t=${TOKEN}&s=${SALT}&v=1.0.0&c=${CLIENT}&id=7"

<?xml version="1.0" encoding="UTF-8"?>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.15.0">
   <playlist id="7" name="aaa" owner="admin" public="false" songCount="20" duration="5462" created="2021-06-21T08:10:09.487Z" changed="2021-07-08T23:39:30.937Z" coverArt="pl-7">
      <entry id="211" parent="198" isDir="false" title="HAPPY면 LUCKY (솔라미♡스마일)" album="한국어" artist="프리파라" coverArt="198" size="4746327" contentType="audio/mpeg" suffix="mp3" duration="286" bitRate="64" path="프리파라/한국어/HAPPY면 LUCKY (솔라미♡스마일).mp3" isVideo="false" playCount="60" created="2021-02-02T15:36:38.077Z" starred="2021-04-28T23:45:30.352Z" albumId="2731" artistId="913" type="music"/>
      <entry id="11658" parent="11612" isDir="false" title="Lu La La☆Lumière" album="キラキラ☆プリキュアアラモード ボーカルアルバム 2 苺坂物語" artist="駒形友梨" track="1" year="2017" genre="Anime" coverArt="11612" size="11564299" contentType="audio/mpeg" suffix="mp3" duration="285" bitRate="320" path="프리큐어/[프리큐어] 키라키라 프리큐어 아라모드/キラキラ☆プリキュアアラモード ボーカルアルバム 2 苺坂物語/Lu La La☆Lumière.mp3" isVideo="false" playCount="59" created="2018-05-26T08:03:40.000Z" starred="2021-05-12T23:20:30.094Z" albumId="2622" type="music"/>
      <entry id="11278" parent="11254" isDir="false" title="La♪La♪La♪ Suite Precure♪ ~∞UNLIMITED∞ ver.~" album="Suite Precure♪ 2nd OP&amp;ED Theme Single" artist="Mayu Kudo" track="1" year="2011" genre="Soundtrack" coverArt="11254" size="8303807" contentType="audio/mpeg" suffix="mp3" duration="206" bitRate="320" path="프리큐어/[프리큐어] 스위트 프리큐어/Suite Precure♪ 2nd OP&amp;ED Theme Single/La♪La♪La♪ Suite Precure♪ ~∞UNLIMITED∞ ver.~.mp3" isVideo="false" playCount="44" created="2018-05-26T10:15:02.000Z" starred="2021-05-12T23:27:39.529Z" albumId="2574" artistId="900" type="music"/>
   </playlist>
</subsonic-response>

 

계획은 간단한데

우선 설정값으로 이런 값을 받는다.

1. 유저 ID

2. 유저 패스워드

3. 플레이리스트 이름

4. 새로 만들 플레이리스트 이름

5. 정렬 기준(최대 3개정도)

 

정렬기준은 아래와 같다.

 

id
album
artist
year
genre
size
path
created
starred

getPlaylist에 들어있는 값들을 이용해서 정렬할것이다

 

 

그 후에 그냥 플레이리스트를 만든 후

정렬된 리스트를 순서대로 플레이리스트에 넣어주면 끝이다.

 

아니 이런 간단한 기능을 지금까지 아무도 안만들어 주다니

진짜 너무한것 같다.

 

그래서 한번 간단하게 만들어봤는데 일단 동작 자체는 잘 되는걸 확인했다.

 

https://github.com/AhatLi/SonicClassifier

 

AhatLi/SonicClassifier

Sort playlist for Airsonic, subsonic . Contribute to AhatLi/SonicClassifier development by creating an account on GitHub.

github.com

 

근데 막상 써보니 star 목록에 대해서도 정렬을 해야될것 같다.

이것도 플레이리스트 정렬 방법이랑 똑같이 하면 되긴 하지만

사용하는 API가 달라서 결국 기능 추가는 해야한다...

어렵지는 않지만 귀찮은 작업인데 나중에 시간날때 추가해야겠다.

 

그리고 또 써보니까 GUI로 되어있으면 상당히 편하지 않을까 하는 생각도 들었다.

go언어용 GUI 라이브러리가 몇가지 있던데 사용방법을 확인해서

나중에는 GUI로 돌아가도록 수정해야겠다.

예전에 핸드폰에 용량이 너무 부족해서

클라우드 스토리지를 이것저것 알아본적이 있었다.

 

네이버 클라우드, 구글 클라우드, 원드라이브 다 알아보았는데 내 마음에 드는것은 없었다.

굳이 꼽자면 네이버 클라우드쪽이 가장 쓰기 좋았지만

안그래도 돈 나가는것도 많은데 매달 돈을 쓰면서까지 사용해야할 정도는 아니기도 하고...

 

그래서 무료 오픈소스 클라우드 서버프로그램인 파이디오(Pydio) 등도 설치하고 다 해보았지만

생각보다 무겁고 내 빈약한 스마트폰 환경에서는 사용하기가 어려웠다.

 

그러다가 이미지 전용 클라우드 서버같은건 없을까 싶어서 찾아보다가  그냥 내가 만들기로 했다.

내가 진짜로 원하는 기능은 정말 간단했는데

 

그냥 이미지파일을 업로드 다운로드가 가능하고 

목록의 이미지는 썸네일을 표시 해서 표시하는데 필요한 용량은 줄이고

이미지 썸네일을 누르면 원본 이미지를 표시해서 양옆 스와이프로 다음 이전 이미지로 이동할 수 있는 정도?

 

이정도만 있으면 딱 좋겠다고 생각했다.

 

일단 모바일에서 사용이 가능해야되는데 앱으로 만들기는 어려우니까

간단하게 html과 자바스크립트 그리고 golang을 사용해서 만들었다.

 

일단 제작 컨셉은 다른 오픈소스 클라우드 프로그램들을 사용하다보니

설치나 사용방법 복잡하다는 것을 느껴서

다운받고 실행파일만 눌러서 딱 실행만 하면 바로 쉽게 사용할 수 있는 프로그램이다.

 

프로그램 외형은 컨셉이 아니라 그냥 내가 디자인 센스가 없어서 저렇게 됐다.

 

github.com/AhatLi/ahat-simple-image-gallery

 

AhatLi/ahat-simple-image-gallery

A very simple image gallery server developed with go. Suitable for use on mobile. - AhatLi/ahat-simple-image-gallery

github.com

서버 프로그램을 실행시킨 후 브라우저로 서버의 주소에 접속하고

로그인을 하면 위와같은 화면이 나온다.

 

로그인 기능은 처음에는 만들 생각이 없었는데

서버 로그를 살펴보던 중에 자꾸 이상한 IP에 요청이 계속 들어와서

그냥 로그인 기능을 추가해주었다.

 

사용하면서 또 필요하다고 느낀 기능으로는 

인증서를 이용한 SSL 기능과 이미지 업로드 기능

그리고 새폴더 같은거 만드는기능?

 

버그도 좀 있는것 같은데 그것도 확인해봐야겠다.

 

 

###Ahat Simple Gallary

```
모바일에서 사용하기 적합한 간단한 이미지 갤러리 서버입니다.

Ahat Simple Gallary를 실행시키고 
모바일 웹브라우저 혹은 데스크탑 웹 브라우저로 접속하여
이미지 갤러리 기능을 사용할 수 있습니다.
현재까지 구현된 기능은 아래와 같습니다.
```

```
 - 이미지를 썸네일화 하여 갤러리 목록에 표시
 - 썸네일을 클릭할 경우 원본 이미지를 표시
 - 이미지를 좌 우로 드래그하여 이전/다음 이미지 표시
 - 이미지를 선택 후 이미지 경로 이동 기능
 - 이미지를 선택 후 이미지 삭제 기능
 - 페이지 접속 시 로그인을 하여야 접속 가능
 - 이미지의 이름/크기/날짜별 정렬 기능
```

```
컴파일 방법
 go get github.com/disintegration/imaging
 go get gopkg.in/ini.v1
 go get github.com/gorilla/securecookie
 go build .\ImageCloud.go .\Utils.go .\login.go
```

```
사용방법 1. 윈도우 환경
1) images 파일에 이용한 이미지 파일을 구성한다.
2) ImageCloud.exe 파일을 실행한다.
2-1) 로그파일을 남기고 싶을 경우 CMD 창을 열고 바이너리 위치로 이동해 
     ` ImageCloud.exe >> ImageCloud.log ` 
     형식으로 실행한다.
3) 프로그램이 실행된 후 모바일 브라우저 혹은 브라우저로 접속하여 사용한다.
ex) http://127.0.0.1:9090/
```

 

 

아직 개선할 점이 많이 있지만

내 서버컴퓨터에 이미지를 넣어두고 외부에서 접속해서 보거나 가져가는 등의 작업은 가능하다.

 

나중에 기능을 추가해야겠다.

 

인터넷을 보니까 커피찌꺼기로 식물 비료를 만드는 방법이 있어서 한번 만들어봤다.

마침 집에서 탈취용으로 사용하던 잘마른 커피찌꺼기가 있어서 그걸 사용했다.

 

커피찌꺼기를 한달정도 발효시킨 후에 식물에게 주어야 한다고 하는데

그대로 사용하면 커피찌꺼기가 나중에 발효되면서 가스가 발생하고 그 가스때문에

식물에게 안좋은 영향을 줄 수 있다는것 같다.

 

하지만 커피찌꺼기를 흙에 섞는게 아니라 화분 위쪽에 뿌리기만 하는거면

굳이 발효시킬 필요가 없지 않을까 싶기도 하다.

 

어쨌든 전문가들이 발효시켜서 쓰라고 하니까 나는 한번 발효를 시켜보기로 했다.

 

커피찌꺼기를 발효시킬 통에 담았다.

뚜껑이 있고 밀폐되는 용기가 좋다고 한다.

 

마른 과일껍질을 섞어서 만드는 사람도 있길래

나도 귀찮아서 안치웠더니 내 책상위에서 바싹 말라있는 귤껍질을 부숴서 섞어줬다.

 

 

 

여기에 요구르트를 섞어주었다.

 

요구르트를 섞으니까 생각보다 양이 많아져서 좀 더 큰 용기로 옮겨주었다.

 

 

균이 번식할 수 있도록 설탕을 섞어주라고 하는데

설탕이 안보여서 그냥 올리고당을 뿌려줬다.

 

 

다 섞고나니 브라우니 반죽처럼 되었다.

 

 

 

이제 따뜻한곳에서 한달동안 발효시키면 된다.

 

엘로나를 플레이 하던 도중 King's Heart 라는 아이템을 얻었다.

이 아이템은 적에게 낮은 확률로 드랍되는 아이템으로 

플레이어의 펫을 진화시킬 수 있는 아이템 중에 하나다.

 

마침 새로운 동료를 하나 추가하고싶던 참이라 어떤 동료를 추가할까 고민하다가

King's Heart 를 사용하여 Ghost Princess로 진화가 가능한 

Ghost를 동료로 만들었다.

 

Ghost Princess의 모습

하지만 내가 가지고있는 최고의 아이템을 착용시켜줬는데도 불구하고

Ghost Princess의 공격력은 생각보다 강하지 않았다.

 

어째서인지 그런지 전투 로그를 살펴보았는데

Ghost Princess가 내가 준 무기로 공격을 하는게 아니라

썩은 손으로 손대었다와 무서운 손으로 손대었다 라는 로그가 남았다.

 

그래서 엘로나 위키를 살펴보니

망령공주의 공격이 Touch of Weakness, Touch of Fear 그리고 Martial Arts 로구성되어 있다는 것을 알았다.

그러나 현재 내 동료인 망령공주는 마셜아츠가 발동되지 않고있었고

내가 무기를 착용시켜준 것이 원인 것으로 생각된다. 

아무래도 NPC 고스트가 맨손으로 등장하기 때문에 마셜아츠라고 기록된 것으로 생각된다.

 

만약 Touch of Weakness, Touch of Fear 기술의 공격력이 맨손전투의 영향을 받는다면 

무기를 빼고 맨손전투 스킬을 올려줬겠는데 그런것도 아닌것같고 의지 스탯의 영향을 받는다고 한다.

그래도 혹시 몰라서 테스트를 해보기로 했다.

 

망령공주의 격투스킬은 현재 16.296이다.

만약 무기를 착용한 상태에서 망령공주의 맨손격투 스킬이 올라간다면 

Touch of Weakness, Touch of Fear 기술은 격투스킬의 영향을 받는다는것이 된다.

 

낮은 레벨의 던전을 클리어 한 후 망령공주의 스킬레벨을 확인해보았다.

장검 스킬 같은경우는 레벨이나 올라간 반면에 격투 스킬은 사실상 오르지 않았다.

 

Touch of Weakness, Touch of Fear는 격투와는 정말로 아무런 관계도 없는 스킬이었다.

 

망령공주를 앞으로 어떻게 육성할지는 좀 더 생각을 해보아야겠다.

'생활 > 게임' 카테고리의 다른 글

엘로나 거미 플레이  (0) 2020.12.24

 

우선 Airsonic에 대해서 설명하자면

Airsonic은 Subsonic에서 파생된 프로그램으로 Subsonic은 간단히 말해 미디어 서버 프로그램이다.

Subsonic은 서버내의 음악파일을 외부 클라이언트로 공유하여 들을 수 있도록 다양한 기능을 제공하는 서버 프로그램으로 버전 6 이전까지는 무료였다가 이후에는 유료로 변경되었다고 한다.

Airsonic은 Subsonic의 유료화에 반발하여 분리된 프로젝트로 Subsonic의 기능과 API 등을 가지고도 무료로 사용할 수 있는 서버 프로그램이다.

때문에 Subsonic과 연동되는 클라이언트 프로그램은 Airsonic에도 연결할 수 있다.

https://github.com/airsonic/airsonic

 

airsonic/airsonic

:satellite: :cloud: :notes:Airsonic, a Free and Open Source community driven media server (fork of Subsonic and Libresonic) - airsonic/airsonic

github.com

Subsonic은 클라이언트에서 사용할 수 있는 다양한 API를 제공하는데

http://www.subsonic.org/pages/api.jsp

 

Subsonic

maxBitRate No (Since 1.13.0) The maximum bit rate (in Kbps) for the user. Audio streams of higher bit rates are automatically downsampled to this bit rate. Legal values: 0 (no limit), 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320.

www.subsonic.org

Airsonic 또한 Subsonic의 API를 대부분 사용할 수 있다.

그 중에서도 이번에 수정해볼 API는 이것이다.

가사를 얻어오는 API인데

이전 포스트에서도 설명했지만 저 API를 실행시키면

Airsonic은 파라미터로 보내진 artist, title 정보를 이용해서

String url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=" + artist + "&song=" + song; String xml = executeGetRequest(url); lyrics = parseSearchResult(xml);

이런 위의 URL을 통해 가사를 가져온다.

즉 저 사이트에 가사 정보가 없으면 가사를 가져오지 못한다는 뜻이다.

하지만 Airsonic은 오픈소스 프로젝트이므로 내가 코드를 수정해서 사용하는것이 가능하다.

이번에는 저 코드를 수정해서 Airsonic이 getLyric API를 요청받았을 때

저 URL이 아닌 로컬 환경에서 가사를 가져오도록 수정할 예정이다.

우선 Airsonic을 컴파일할 환경을 구성해야 한다.

환경을 구성하는 방법은 아래 사이트에서 설명해주고 있다.

https://airsonic.github.io/docs/install/source/

 

Airsonic

Airsonic, a Free and Open Source community driven media server, providing ubiquitous access to your music.

airsonic.github.io

계획은 단순한데

Airsonic에서 제공하는 getLyrics API를 실행하면

Airsonic이 자신의 musicDirectory에서 title 파라미터값을 이용해 파일을 검색해 가사 파일을 찾아내는 것이다.

만약 가사파일을 찾으면 가사파일을 읽어서 API를 요청한 클라이언트한테 보내주면 된다.

Airsonic 서버가 로컬 가사파일을 읽어서 클라이언트에게 가사를 전송하도록 수정할 것이다.

나는 윈도우 환경에서 MVN, JDK 버전등을 맞추고 컴파일을 시도해 보았더니

윈도우 환경에서는 컴파일 할 수 없다는 메세지가 출력되어서 Virtualbox 가상머신에 MX 리눅스를 설치했다.

이후 설명 페이지에 나와있는대로 환경을 구성하고 소스코드를 수정하지 않은 상태에서 컴파일하여 .war 파일을 생성하여 테스트 해보았다.

tomcat 9버전대에 올려보니 정상동작하는것을 확인할 수 있었다.

참고로 컴파일에는 한 20분정도 시간이 걸렸는데 실제 컴파일하는데는 5분이면 되는것 같은데

Airsonic 컴파일을 할 때 실행되는 기능 테스트 동작하는데에만 한 15분이 넘게 걸리는 것 같다.

어쨋든 컴파일이 된것을 확인하고 나서는 코드를 수정해 주었다.

참고로 코드 규칙이 정말 까다롭게 되어있는데

탭문자쓰면 탭문자 쓰지 말라고 에러나고 띄어쓰기 한개때문에 에러나고 if , for 문 앞뒤로 한칸씩 띄어쓰라고 에러나고 대괄호 내려썼다고 에러나고 == 쓰지말고 equals 쓰라고 에러나고

얼마나 깐깐하게 만들었는지 알 수 있었다.

그렇게 코드를 수정하고 리눅스 환경에서 Airsonic에 getLyrics API를 날려보니

 

SERVER='http://127.0.0.1:8080/airsonic'
CLIENT='CLI'
USERNAME='admin'
PASSWORD='admin'
SALT="$(openssl rand -hex 20)"
TOKEN="$(echo -n "${PASSWORD}${SALT}" | md5sum | awk '{ print $1 }')"
echo ${SALT}
echo ${TOKEN}
curl "${SERVER}/rest/getLyrics.view?u=${USERNAME}&t=${TOKEN}&s=${SALT}&v=1.15.0&c=${CLIENT}&artist=&title=Anemone"

 

가사가 정상적으로 출력되었다.

안드로이드 클라이언트에서도 가사가 정상적으로 출력되는것을 확인했다.

다만 lrc 파일 특유의 시간표시가 남아있는데

저 시간정보를 이용하려면 서버 프로그램이 아닌 클라이언트 프로그램을 수정해야만 한다.

Subsonic API는 가사정보가 LRC 형식이 아닌 그냥 단순 텍스트로 전송되기때문에

저 시간정보를 이용할 수 있는 클라이언트가 없다.

만약 저걸 이용하려면 클라이언트 기능까지 직접 만들어야된다는건데...

일단은 저 시간정보를 빼고 깔끔하게 보이는 텍스트 가사로 출력하는것에 만족해야겠다.

시간정보를 제거한 가사 출력

이번에 추가한 코드는 아래와 같다.

 

 @RequestMapping("/getLyrics")
    public void getLyrics(HttpServletRequest request, HttpServletResponse response) {
        request = wrapRequest(request);
        String artist = request.getParameter("artist");
        String title = request.getParameter("title");

        Lyrics result = new Lyrics();
        result.setArtist(artist);
        result.setTitle(title);
        result.setContent(getContentMain(title));

        Response res = createResponse();
        res.setLyrics(result);
        jaxbWriter.writeResponse(request, response, res);
    }

    public String getContentMain(String title) {
        for (org.airsonic.player.domain.MusicFolder folder: settingsService.getAllMusicFolders()) {
            File[] fileList = folder.getPath().listFiles();
            for (int i = 0; i < fileList.length; i++) {
                File file = fileList[i];
                if (file.isFile() && file.getName().contains(title) && file.getName().toLowerCase().contains(".lrc")) {
                    String str = "";
                    try {
                        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"));
                        br.readLine();
                        br.readLine();
                        br.readLine();
                        String line = br.readLine();
                        while (str != null) {
                            str += line.substring(10) + "\n";
                            line = br.readLine();
                        }
                        br.close();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    return str;
                } else if (file.isDirectory()) {
                    String r = getContentSub(file, title);
                    if (!r.isEmpty()) {
                        return r;
                    }
                }
            }
        }
        return "";
    }

    public String getContentSub(File subdir, String title) {
        File[] fileList = subdir.listFiles();
        for (int i = 0; i < fileList.length; i++) {
            File file = fileList[i];
            if (file.isFile() && file.getName().contains(title) && file.getName().toLowerCase().contains(".lrc")) {
                String str = "";
                try {
                    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"));
                    br.readLine();
                    br.readLine();
                    br.readLine();
                    String line = br.readLine();
                    while (line != null) {
                        str += line.substring(10) + "\n";
                        line = br.readLine();
                    }
                    br.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return str;
            } else if (file.isDirectory()) {
                String r = getContentSub(file, title);
                if (!r.isEmpty()) {
                    return r;
                }
            }
        }
        return "";
    }

 

getLyrics 함수를 수정하고 getContentMain, getContentSub 함수를 추가해주었다.

새벽 늦게까지 했지만 기능을 완성하니 뿌듯하고 기분이 좋다.

컴파일 시간이 짧았으면 좀 더 빨리 끝났을것 같은데 테스트를 스킵하는 명령이 있는지 찾아볼걸 그랬다 싶다.

아무튼 이로써 무료 음악 스트리밍 서버를 이용하면서 노래의 가사까지 볼 수 있게 되었다.

집에 음악 스트리밍 서버를 구축하기 위해서 내가 기존에 조사했던 자료이다.

각 프로그램의 전체 기능을 비교한 것이 아닌 음악을 재생 기능만을 비교한 것이다.

대부분이 음악 재생기능이 메인이 아닌 영화 등의 동영상 매체를 메인으로 하다보니 음악재생에 있어서는 부족한 부분이 한두가지씩 있어서 어떤 프로그램을 사용해야 좋을지 한눈에 보기 위해서 정리했던 자료이다.

나는 외부에서 음악을 듣기 위해서 Jellyfin 서버를 설치했는데 막상 사용해보니 조금 문제점이 있었다.

일단 첫번째로 Jellyfin은 가사 출력 기능이 없다.

나는 가사를 출력하기 위해서 음악파일에 대응되는 lrc 파일들을 많이 만들어놨는데 Jellyfin은 온라인 가사 출력기능도, 로컬 가사 출력기능도 없었다.

두번째로는 기본적으로 동영상이 메인이기 때문인지 다음에 재생될 음악 등을 미리 다운로드 하는 기능이 없다.

나같은 경우는 전철 1호선을 타고 갈때 온수 ~ 구로 사이에서 이상하게 LTE 속도가 느려지는 점이 있어서 음악을 미리 다운로드해서 캐시에 넣어놓는 기능이 없으면 다음 파일이 바로 재생되지 않는 문제점이 있었다.

결국 가사 출력이 가능하면서 미리 다운로드하는 기능이 있는 서버 + 클라인트 프로그램을 찾고있었는데

찾기가 생각보다 쉽지 않았다.

그래서 계속 찾아보다보니 Subsonic에 getLrics 라는 기능이 있는것을 알 수 있었다.

딱 봐도 가사를 얻어오는 기능인것 같긴 한데 어떤 동작을 해서 가사를 얻어오는지 알 수가 없었다.

Subsonic과 연결되는 안드로이드 클라이언트를 가능한 한 확인해보았더니

Ultrasonic과 Subsonic에 가사를 받아오는 기능이 있는것을 확인했고 다른 클라이언트 어플리케이션에서는 기능을 찾지 못했다.

Ultrasonic; Subsonic용 안드로이드 클라이언트 어플리케이션

subsonic; Subsonic용 안드로이드 클라이언트 어플리케이션,. 공식 클라이언트인것 같다.

그런데 가사를 받아오는 기능을 사용해도 전혀 가사를 받아올 수 없었다.

그 이유를 알기 위해 Airsonic 코드가 올라와있는 Github에서 관련 코드를 찾아보았다.

https://github.com/airsonic/airsonic

 

airsonic/airsonic

:satellite: :cloud: :notes:Airsonic, a Free and Open Source community driven media server (fork of Subsonic and Libresonic) - airsonic/airsonic

github.com

그렇게 찾은 코드가 글쎄

 

public LyricsInfo getLyrics(String artist, String song) {
        LyricsInfo lyrics = new LyricsInfo();
        try {

            artist = StringUtil.urlEncode(artist);
            song = StringUtil.urlEncode(song);

            String url = "http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=" + artist + "&song=" + song;
            String xml = executeGetRequest(url);
            lyrics = parseSearchResult(xml);

        } catch (HttpResponseException x) {
            LOG.warn("Failed to get lyrics for song '{}'. Request failed: {}", song, x.toString());
            if (x.getStatusCode() == 503) {
                lyrics.setTryLater(true);
            }
        } catch (SocketException | ConnectTimeoutException x) {
            LOG.warn("Failed to get lyrics for song '{}': {}", song, x.toString());
            lyrics.setTryLater(true);
        } catch (Exception x) {
            LOG.warn("Failed to get lyrics for song '" + song + "'.", x);
        }
        return lyrics;
    }

 

 

이렇게 되어있었다.

Subsonic 종류의 미디어 서버 프로그램은 코드상에 api.chartlyrics.com 라는 URL을 하드코딩으로 박아넣고

저 사이트에서 가사를 받아오고 있었던 것이다.

그러니 당연히 가사를 찾을수가 없었던 것이다.

하지만 코드가 저렇게 간단하게 되어있으니 수정도 쉬울것이라는 생각이 들었다.

이 다음에는 Airsonic의 코드를 수정해서

로컬환경의 lrc 파일을 읽어 클라이언트로 전송하도록 동작을 바꿔보도록 하겠습니다.

2020. 8. 9. 16:50

 

이번에 핸드폰을 새로 구매했는데 돈을 좀 아껴보겠다고 64GB짜리 작은 용량의 스마트폰을 구매하고 마이크로 SD 카드 128GB 짜리를 하나 주문했다.

그런데 핸드폰 배송이 완료되고 확인해보니 이럴수가 내가 구입한 스마트폰은 마이크로 SD카드를 사용할 수 없는 스마트폰이었던 것이엇따.

그동안 스마트폰에 넣고다니던 음악파일들을 들고다닐 수 없게된 나는 이 참담한 심정을 조금이나마 위로받고싶어 인터넷 커뮤니티에 토로하였으나 돌아온 답변은

 

 

 

요즘 음악 다 스트리밍으로 듣지 어던 찐따가 음악파일 들고다니냐ㅋㅋㅋ 하는 이야기 뿐이었다.

여러가지 고민을 했지만 결국 집에서 사용중인 홈서버용 컴퓨터에 미디어 서버를 설치해서 사용하기로 결정했다.

그 후로 FTP 서버를 깔아서 안드로이드용 FTP 플레이어도 설치해보고 여러가지 서버 프로그램 등도 설치해서 비교해보았는데 그중에 가장 괜찮은게 Jellyfin 미디어 서버였다.

결국 Jellyfin을 설치하였는데 내가 본 Jellyfin의 장점은 우선

1. 설치가 정말 쉬었다.

airsonic을 설치할때는 도커를 깔아서 버추얼박스용 가상머신을 만들어서 네트워크세팅해주고 컨테이너를 다운받고 런한다음에 포트열어주고 외부폴더 연결해주는 등 복잡한 작업을 해주었는데, Jellyfin은 전혀 그런게 없다.

그냥 다운받은다음에 설치해주면 끝이었다.

2. 동영상 파일도 올리고 재생이 가능

subsonic, airsonic처럼 음악 서버가 아니라 미디어서버이다 보니 영화나 애니메이션, 드라마 등의 파일도 넣고 스트리밍으로 재생을 할 수가 있었다.

3. 인터페이스와 디자인이 깔끔하고 각종 설정이 가능

웹페이지와 안드로이드 앱등을 살펴보았을 때, 그동안 설치해본 airsonic이나 FTP 서버와 연결하기 위해 설치한 foobar2000 안드로이드 앱 등과 비교하면 정말 아름답게 보였다.

또한 유저를 추가하여 특정 라이브러리에만 접근 권한을 주거나 접속로그를 보거나 하는 등의 상용 프로그램 못지 않은 기능을 가지고 있다.

 

관리 페이지도 깔끔하다

5. 개발이 활발하다.

Jellyfin 웹페이와 git 페이지등에 찾아가보니 최근에도 개선작업 등이 활발하게 이루어기는것을 볼 수 있었다.

나같은경우도 github 이슈페이지에 안드로이드앱에 캐시기능이 없다고 넣어달라고 글을 올렸더니 2분만에 답장이와서 중복된 이슈가 있다고 내 글을 close 시켜버렸다.

5. 오픈소스 무료 프로젝트다.

무료보다 좋은건 없다.

이런 장점이 있다는것을 느낄 수 있었고

단점도 몇가지 느낄 수 있었는데 단점으로는

1. 파일 탐색 속도가 느리다.

라이브러리를 추가하면 실제로 추가되는데에 생각보다 오랜 시간이 걸렸다. 아무래도 그냥 추가가 되는게 아니라 파일에과 관련된 메타데이터들을 만들고 찾지 못한 메타데이터 등은 인터넷에서 정보를 직접 수집한다음에 집어넣는 그런 방식인것 같다.

이는 라이브러리 추가를 할 때 그런 메타데이터를 얻어올지 말지 등을 설정해 줄 수 있는 것으로 보인다.

2. 안드로이드 앱에 캐시 기능

두가지 안드로이드 앱에서 음악파일을 실행해 봤다. Jellyfin 안드로이드앱과 Gelli 앱인데 Jellyfin 앱에는 캐시기능이 없는것처럼 보이고, Gelli 앱에는 설정에서 캐시 부분을 확인 할 수 있었다.

FTP를 이용하는 음악 플레이어로 테스트를 해봤을 때 음악을 재생하면 그 다음에 재생될 재생목록의 파일 몇가지를 미리 다운로드 하는 등의 나름 편의성있는 기능이 있는것을 보았다.

그런데 이 두가지 앱은 일단 그런 기능도 없었고 캐시가 동작하는지 확인하기 위해

음악1을 재생한 후에 음악2를 재생하고 네트워크를 종료한다음 음악1이 재생되는지에 대해 테스트를 해보았는데 음악1은 재생되지 않았고 심지어는 서버에서 로그아웃까지 된것을 확인할 수 있었다.

Jellyfin앱은 캐시기능이 없는것같으니 그럴 수 있겠지만 Gelli앱은 이런 기능이 되어야하는것 아닌가 싶은데 내가 설정을 따로 해주어야 하는 부분이 있을수도 있을것같다.

Jellyfin 서버는 아래 링크에서 다운받을 수가 있다.

https://jellyfin.org/

 

Jellyfin: The Free Software Media System

Software freedom is important. Jellyfin is Free Software, licensed under the GNU GPL. You can use it, study it, modify it, build it, and distribute it for free, as long as your changes are licensed the same way. The project is community-built, relying enti

jellyfin.org

또한 각종 기능에 대한 설명을 아래 페이지에서 볼 수 있다.

https://jellyfin.org/docs/general/quick-start.html

 

Quick Start | Documentation - Jellyfin Project

 

jellyfin.org

 

이렇게 홈서버나 NAS 등에 Jellyfin 서버를 설치하고 공유기 포트포워딩에서 홈서버 8096 포트에 접속할 수 있도록 설정해 준다음 DDNS 설정을 해주거나 IP로 접속하면 외부에서도 인터넷을 통해서 미디어 서버에 접속을 할 수 있다.

이제 스마트폰 데이터만 걱정 없으면 어디서든 내가 소유한 음악파일을 재생할 수 있게 되었다.

 

참고로 Jellyfin 라이브러리에에 음악을 폴더별로 넣었을때 음악들이 "앨범" 이라는 형태로 들어가는것을 확인할 수 있다.

그런데 확인해보니 이 앨범이라는건 실제 음악의 앨범은 아니고

음악파일이 들어있는 폴더와 그 안에 들어있는 음악파일들이 앨범이라는 형태로 표시되는 것이었다.

앨범의 이름이 그 폴더의 가장 첫 음악파일의 이름으로 되어있었기 때문에 헷갈리게 되는것이다.

앨범의 이름은 아래와 같은 방법으로 바꿀 수 있다.

1. 앨범에서 마우스 오른쪽을 버튼을 누른다.

2. 메타데이터 편집으로 들어가서 제목을 수정한다.

다만 이렇게 수정하고 바로 변경되는것이 아니라 일정 시간이 흐른 후에 변경이 된다.

 

 

 

2020. 7. 10. 2:06

 

이전에 영상 보간 프로그램으로 드미트리를 사용하다가 네트워크 어댑터 추가로 키가 만료되고

무료 영상 보간 플레이어인 Splash를 사용해 보았는데

아무래도 Splash는 일부 코덱을 지원하지 않는데다가 일부 자막형식을 지원하지 않아서 불편한 점이 있었다.

그래서 또다시 방법을 찾아보던 도중 MPV와 SVP 4를 사용하여 영상 보간을 하는 방법을 적은 글을 찾을 수 있었다.

https://quasarzone.com/bbs/qb_free/views/541538

 

프레임 보간 플레이어 필요하신 분들 mpv 한번 써보세요

AMD를 아주 좋아하는데, 이번에 노트북을 구입하며 플루이드 모션을 대체할 방법을 찾아봤습니다.드미트리 렌더…

quasarzone.com

그런데 이 당시에는 SVP 4의 기능을 무료로 사용할 수 있던 시절이었고

현재 SVP는 유료에 30일을 무료로 사용할 수 있다.

MPV를 설치해 보았더니 플레이어 설정까지도 전부 텍스트로 작성해서 실행해야 하는 등의 불편함이 있었다.

 

그러다가 MPV와 같은 갈래에서 파생된 Mplayer 라는 또다른 플레이어를

이용하여 제작된 SMPlayer 라는 플레이어를 발견했다.

기능도 괜찮고 SVP 4와 연동도 간편했다.

 

인터페이스는 정말 깔끔하다.

SMPlayer를 설치하고 SVP 4는 30일동안 무료로 사용할 수 있기 때문에 둘 다 설치해서 연동했더니

Splash처럼 자막의 종류를 가리거나 기능이 동작하지 않는 문제점도 없고

인터페이스나 마우스 버튼 등의 동작을 바꾸는 기능도 있어서 사용하기 좋게 셋팅을 할 수 있었다.

아래는 설치 및 연동 과정입니다.

우선 SMPlayer 64bit버전을 설치한다.

 

https://www.smplayer.info/

 

SMPlayer - Free media player for your PC

SMPlayer is a free media player than can play virtually all audio and video formats. It can even play and download Youtube videos. Other interesting features: find and download subtitles, thumbnail generator, resume playback.

www.smplayer.info

만약 기본 스킨이 마음에 들지 않는다면 다음 방법으로 플레이어 모습을 바꿀 수 있다.

 

그리고 나서 SVP 4를 설치해주었다.

윈도우에서는 SVP 4를 20달러를 내고 구매하여 사용할 수 있지만 설치하면 30일 동안은 무료로 사용이 가능하다.

리눅스에서는 무료라고 하니 나중에 리눅스에서도 설치해보고 싶다.

 

https://www.svp-team.com/get/

 

Get – SVP – SmoothVideo Project

In case the payment was rejected or blocked by your bank, please try the PayPal option instead! We accept Visa and MasterCard bank cards via CloudPayments processing service, please find more information on security and privacy below.

www.svp-team.com

 

설치가 완료되면 이런 창이 뜨는데 나중에 구매를 하겠지만 일단 평가판을 사용하기로 하겠다.

지금 등록하기! 를 선택하면 평가판 기간이 30일 남았다는 메세지가 뜬다.

이제 SMPlayer에서 SVP를 사용해보겠습니다.

사실 너무 간단한데 아래 페이지를 그대로 따라하면 됩니다.

 

https://www.svp-team.com/wiki/SVP:SMPlayer

 

SVP:SMPlayer - SmoothVideo Project (SVP) - frame doubling interpolation

Setting up SMPlayer (Windows, Linux) Installation - Windows Install [VPS_64] mpv video player package via SVP's "Additional programs and features" Install 64-bit version of SMPlayer, Open Preferences, On the General tab set Multimedia engine to Other and b

www.svp-team.com

우선 환경설정으로 들어가서 일반 메뉴의 멀티미디어 엔진의 기타를 선택한다.

 

그러면 파일을 선택하는 다이얼로그가 뜨는데 SVP를 설치한 경로의 MPV 실행파일을 선택한다.

 

내 경우는 기본경로로 설치를 했기 때문에

C:\Program Files (x86)\SVP 4\mpv64

경로에 설치되었다.

다음은 고급 탭으로 이동해서 아래 옵션을 입력해준다.

 

--hr-seek-framedrop=no

확인 버튼을 누르고 나서 영상을 재생해 보면

 

SVP 아이콘이 뜨면서 영상 보간이 동작하는것을 확인할 수 있다.

추가로 SMPlayer에서 OSD를 출력하려면

이 메뉴를 선택하면 된다

영상의 현재 초당 프레임과 각종 정보등을 표시할 수 있다.

 

 

 

 

 

 

 

또한 SMPlayer에서 유튜브 영상을 볼 수 있는데

 

이렇게 주소 메뉴를 클릭한 후 유튜브 주소를 입력하면 재생이 된다.

또는 메뉴에 있는 유튜브 브라우저를 누르고 설치한 뒤

 

 

 

이렇게 검색하여 동영상을 누르면 유튜브 영상이 재생되게 된다.

이렇게 유튜브 영상을 SMPlayer + SVP 환경에서 실행하면

 

이렇게 유튜브 영상또한 보간이 되어 높은 프레임으로 유튜브 영상을 볼 수 있다.

이렇게 볼 일이 있을지는 모르겠지만...

이렇게 해서 동영상을 보간하여 높은 프레임의 영상으로 만들어 부드러운 동영상을 감상할 수 있도록 하였습니다.

+ Recent posts