XMonad

XMonad + mvp + MPD = Custom

Rina Kawakita 2024. 5. 12. 18:37

XMonad + mpv  + MPD = Custom

 

- 다이나믹 윈도우 매니저 : 타일링 + 플로팅 을 모두 지원하는 WM

  * XMonad : Virtual 2 Screen , [Circle, ThreeColum, Tiled] Layout, NSP(=NamedScratchPad)

  * Apps : rxvt-unicode terminal, qutebrowser, mopidy, nsxiv, dunst, tmux, ncmpcpp, cava, tvheadend, mpv

  

😀 ~/.xmonad.hs : NSP 설정사용하는 미니멀 설정값

import XMonad
import qualified XMonad.StackSet as W   --https://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-StackSet.html#t:RationalRect
import qualified Data.Map        as M
import XMonad.Util.NamedScratchpad      --http://xmonad.org/xmonad-docs/xmonad-contrib/XMonad-Util-NamedScratchpad.html
import XMonad.ManageHook                --https://hackage.haskell.org/package/xmonad-0.15/docs/XMonad-ManageHook.html
import XMonad.Hooks.ManageHelpers


-- Named ScratchPads
myScratchPads :: [NamedScratchpad]
myScratchPads = [ 
                  NS "livetv"   spawnLivetv findLivetv manageLivetv   -- and a 4th
                ]
    where
        role = stringProperty "WM_WINDOW_ROLE"
        --- (중략) ---

        spawnPlayer  = "mpv --x11-name=livetv  --no-resume-playback https://www.youtube.com/@3mtissue"     -- launch mpv for Youtube 3분휴지
        --spawnLivetv  = "mpv --x11-name=livetv --profile=tv --playlist-start=8 --no-resume-playback TVChannels/my_tvheadend.m3u"     -- launch mpv for livetv mpv
        findLivetv   = resource  =? "livetv" <&&> className =? "mpv"                          -- its window will be named "livetv" (see above)
        manageLivetv = customFloating $ W.RationalRect l t w h                                                      -- and I'd like it* using the geometry below
            where
            -- reusing these variables is ok since they're confined to their own 
            -- where clauses 
            h = 0.24                                                 -- height, 24%
            w = 1                                                    -- width,  100%
            t = (1 - h) - (16 / 1080)                      -- bottom right edge: bottom xmobar height=16, y resoulution=1080
            l = 1 - w                                                -- right, left/right


----------------------------------------------------------------------------}}}
-- Key Binding                                                              {{{
-------------------------------------------------------------------------------
-- FOR MULTIMEDIA KEYS RUN:
-- xev | grep -A2 --line-buffered '^KeyRelease' | sed -n '/keycode /s/^.*keycode \([0-9]*\).* (.*, \(.*\)).*$/\1 \2/p'
-- Then look in /usr/include/X11/XF86keysym.h and look for the name and code.
--
-- Graphics.X11 keysym definitions: https://wiki.haskell.org/Xmonad/Key_codes
-- /usr/include/X11/keysymdef.h

-- Define keys to add
keysToAdd x =
    [
    -- rofi lanucher
    (modMask x, xK_p), spawn "rofi -show drun -show-icons")
    -- Close focused window
    ,((modMask x, xK_c), kill)

    -- Named Scratchpad key
    -- http://xmonad.org/xmonad-docs/xmonad-contrib/XMonad-Util-NamedScratchpad.html
    ((modMask x, xK_F12), scratchTerm   )
    , ((modMask x, xK_F9 ), scratchLivetv )
    ]
    where
        -- this simply means "find the scratchpad in myScratchPads that is 
        -- named terminal and launch it"
        scratchTerm   = namedScratchpadAction myScratchPads "terminal"
        scratchLivetv = namedScratchpadAction myScratchPads "livetv"

-- Define keys to remove
keysToRemove x = 
    [
    -- Unused dmenu binding
      (modMask x,               xK_p)
    -- Unused gmrun binding
    , (modMask x .|. shiftMask, xK_p)
    -- Unused close window binding
    , (modMask x .|. shiftMask, xK_c)
    -- Unused xmonad recompile, restart logout binding
    , (modMask x,               xK_q)
    , (modMask x .|. shiftMask, xK_q)
    ]

-- Delete the keys combinations we want to remove
strippedKeys x = foldr M.delete (keys def x) (keysToRemove x)
-- Compose all my new key combinations.
myKeys x       = M.union (strippedKeys x) (M.fromList  (keysToAdd x  ))


-- Define the workspace on application has to go to
myManageHook :: ManageHook
myManageHook = (composeAll . concat $
    [
        --[ className =? "mpv"                                --> doCenterFloat ]   --doFloat
        [ className =? "mpv" <&&> resource =? "livetv"  --> doSideFloat(SE) ]
        --, [ className =? "mpv" <&&> resource =? "livetv"  --> doF (W.shift (myWorkspaces !! 4)) ]
    ])  <+> namedScratchpadManageHook myScratchPads
        <+> manageHook def


-- Main
main = do
  xmonad $ def {
    keys = myKeys
    , manageHook = myManageHook
  }

 

- Web Surfing : vivaldi + mpv

 

- chrome web store : 'Improve YouTube!' 🎧 (for YouTube & Videos)


 

0. 전통적인 타일링 : tmux(-> ranger + btop), qutebrowser, nsxiv, tmux(--> ncmpcpp + cava), mpv

 

 

 

 


 

1. 타일링 : "여백과 선의 미학"

  1-1 : 1px : qutebrowser,  tmux(--> ranger + btop) , nsxiv(spootify cover), tmux(--> spt + spt )

 

  1-2 : 6px : qutebrowser, PostgreSQL, ranger(widh ueberzugpp), fzf, vim, dunst, mopidy, tmux(--> ncmpcpp + cava), mpv(with tvheadend)


 

2.  AV 리눅스(r/unixporn) : Audio/Video 최적화 미니멀 데스크탑

   (그러나 AV는 Audio/Video 인 동시에 Adult Video )

 2-1. AV : 좌측 livetv | movie | drama | image | jav (mpv) + 우측 mopidy | mpd (nsxiv)

 

 

 2-2. 가. Audio : Music Sever(mpd or spotify), Music Client(ncmpcpp or ncspot), Coverart(nsxiv | dunst)

  - 1-key Music : 영상링크 :  https://drive.google.com/file/d/1V2_gehHm7AkTXiB72TI27cSRFk59WjDz/view

 

 

 2-3. Video : Jav(mpv anywhere)

  - 1 command Jav : 영상 링크 : https://drive.google.com/file/d/1y6Xl7FwYoCw-kgw8kggScN_rm_X4yQFq/view

 

 2-3. Image :  Image Viewer = ucollage(dependency : ueberzugpp) widh ranger(or lf) on tmux.  

  - 터미널 이미지 뷰어 : 영상 링크 : https://drive.google.com/file/d/1nEqSxb8PG5MSZg0u4UWRXRY1qwipfMaf/view?usp=sharing


 

3. Editor : XMonad WM --> XMonad 1 key NamedScratchPad : Uxvt Terminal to Tmux multiplxer(with fzf) --> Astro Nvim editer(widh plugins)  --> toggleterm plugin + Ranger plugin(=Rnvimr)     ||    mpv

    - 영상 링크 1 : https://drive.google.com/file/d/1qhBQOMQR9lh6saaHrW052fmMSjGJncWk/view

    - 영상 링크 2 : https://drive.google.com/file/d/1M-_DqR7vSEHPKdbOdGSBZdSk_eS4YLMr/view

 


 

4.  XMonad + MPV Custom

 

 

 

   - Mpv custom : 영상 링크 : https://drive.google.com/file/d/1K0OMPsvMwXpmZ9yt0GeQQInl4k6NL5YY/view

   - XMonad Tiling 1 (Music Video) : 영상 링크 : https://drive.google.com/file/d/13RxXnJihRTAdpMTQOtVLyaBYGdkuXT_j/view?usp=drive_link

   - XMonad Tiling 2 (XMonad Custom Layout) : 영상 링크 : https://drive.google.com/file/d/18bfQdp75np2PkwH0AMMkN8QVSEsjjzvb/view?usp=drive_link

 


Audio Custom


 

1. 음원이나 스티리밍 소스가 많아 통합이 필요하다.

   mopidy(음악서버) + iris(mopidy 웹서버) + (coverart 커스텀), + 플러그인 커스텀 :  음악서버 + 웹클라이언트 + 확장 플러그인

 

 

2. 로컬 음원파일들이 많다.

  mpd(응악서버) + ncmpcpp + (mpc 키바인딩,) + (coverat 커스텀) + (coverart notification) + (fzf + mpc + fmpc)

 

- CLI 리눅스 유저의 거의 표준 구성 : qutebrowser + ranger + ncmpcpp(width mpd or mopidy) + mpv

 

- ranger + mpc 키바인딩(with mpd or mopidy) + youtube music video

 

- ncmpcpp(with mpd) + coverart noti

 

- mpd(or mopidy) + ncmpcpp + coverart 커스텀

 

- mpd(or mopidy) + fzf + mpc + coverart noti

 

 

 - youtube-viewer(or pipe-viewer)

$ pipe-viewer --yt-dlp --ytdl-cmd="yt-dlp" --resolution=1080p --prefer-av1 --hfr --wget-dl -uv @ai-enhancedkpop1975

 

 

 - ytfzf

$ ytfzf -t -T mpv blackpink 8k

 

 

- mpv(or mplayer) with ranger, cue, mpris, playerctl

 



2-1.  Music with mpv, etc

  - 음악용 : 1분 30초 : 영상링크: : https://drive.google.com/file/d/1zignWJEjezTV5iy2ESi1HK5xwgjJhx1Z/view?usp=sharing

  가. ranger + mpv ,  나. cue

  ( mpv 는 console 상에 실행 또는 X로 실행 선택 가능,  mpv 출력은  auto profile 이용, 우측 상단에  geometry로 고정), 

 

  (( playlist 관련 lua 확장 플러그인 ))

A. autoload.lua : (위 영상의) 00:00:38:050 지점부터,  

   https://github.com/mpv-player/mpv/blob/master/TOOLS/lua/autoload.lua

   ranger 실행하고, "018 NewJeans - Attenstion.flac"  커서 상태에서 "l" 키 누르면 mpv 실행되면서 자동으로 "[autoload] Adding ....."  뜨면서 플레이리스트를 만들어 준다. 

   lua 파일 , lua 환경 설정 파일 위치 ( "~~" 는 mpv 환경설정 폴더를 의미, 보통은 "~/.config/mpv/"

  | ~~/scripts/autoload.lua

  | ~~/script-opts/autoload.conf

disabled=no
images=no
videos=yes
audio=yes
ignore_hidden=yes

 

 

B. 음원 fanart 우측 상단 고정 : 00:00:39.150 지점,, 우측 상단 "꼬양이" fanart 고정

  | ~~/mpv.conf

아래 "geometry=14%"

(저는 음원 fanart 14% 로 사이즈를 지정하고,  듀얼모니터를 사용하는지라 별도의 xmonad 윈도우 매니저에서 X위에 Screen 구분하여, 상대적인 우측 상단의 코너에 박는 루틴이 따로 설정되어 있습니다.)

 일반 윈도우 매니저인 경우 : 아래 geometry  옵션을 참조하여 정확한 절대 위치로 고정

--geometry=<[W[xH]][+-x+-y][/WS]>, --geometry=<x:y>

### Extension Specific #########################################################
[extension.mp3]
no-config
hwdec=no
vf-clr
x11-name=music
ontop
border=no
geometry=14%
target-prim=auto
target-trc=srgb
video-output-levels=full
display-tags=album,artist,title,lyrics-kor,lyrics-eng
term-osd-bar=yes
osd-status-msg='${time-pos}-${=time-pos} / ${duration} (${percent-pos}%)\n${osd-ass-cc/0}{\\1c&H725761&}{\\b1} ${osd-sym-cc}${osd-ass-cc/0}{\\1c&H725761&}{\\b1}\t${media-title}\n ${osd-ass-cc/0}{\\1c&HA3A3A3&}{\\b1}{\\fs10}\n${osd-ass-cc/0}{\\1c&HA3A3A3&}{\\b1}  File Size: ${osd-ass-cc/0}{\\1c&HFFFFFF&}{\\b1}${file-size}\n${osd-ass-cc/0}{\\1c&HA3A3A3&}{\\b1}  Format: ${osd-ass-cc/0}{\\1c&HFFFFFF&}{\\b1}${file-format}{?audio-codec-name:${audio-codec-name}}\n${osd-ass-cc/0}{\\1c&HA3A3A3&}{\\b1}\n  Audio Codec: ${osd-ass-cc/0}{\\1c&H725761&}{\\b1}${?audio-codec:${audio-codec}}\n${osd-ass-cc/0}{\\1c&HA3A3A3&}{\\b1}  Audio-bitrate: ${osd-ass-cc/0}{\\1c&H725761&}{\\b1}${?audio-bitrate:${audio-bitrate}} ${?audio-params/channels:(${audio-params/channels})}\n\n${osd-ass-cc/0}{\\1c&H573E57&}{\\b1}  Output Driver: ${?current-ao: AO [${current-ao}]}{\\1c&H573E57&}{\\b1} {osd-ass-cc/0}{\\1c&H009aa7c0&}{\\b1}{\\an7}\n\n  Mpv version: ${mpv-version}\n  FFmpeg version: ${ffmpeg-version}'
osd-msg2='${osd-ass-cc/0}{\\r}{\\pos(100,10)}{\\1c&H573e78&}{\\b1}{\\fs10}\n ${filtered-metadata/title}   by ${filtered-metadata/artist} ( ${filtered-metadata/album})${osd-ass-cc/0}{\\1c&H009aa7c0&}{\\b1}{\\fs6}\n\n ${?filtered-metadata/lyrics-kor:${filtered-metadata/lyrics-kor}}${?filtered-metadata/lyrics-eng:${filtered-metadata/lyrics-eng}}'

[extension.flac]
profile=extension.mp3

[extension.m4a]
#no-video
# equal to --vid=no, If video is disabled, mpv will try to download the audio only if media is streamed 
# with youtube-dl, because it saves bandwidth. This is done by setting the ytdl_format to "bestaudio/best"
# in the ytdl_hook.lua script.
display-tags=album,artist,title,lyrics-kor,lyrics-eng   # display-tags=*
term-osd-bar=yes
profile=extension.mp3

 

   

C. minimal GUI : UOSC 내장 플레이리스트 뷰어 - 00:00:46.200  지점부터

   https://github.com/tomasklaen/uosc

   UOSC lua 플러그린 설치하면 Playlist Control Viewer 같이 설치 된다.

   "f" 키 눌러 풀스크린 화면으로 전환하고,  "키패드 0번" 키 누르면 플레이리스트가 뜬다.

   (디폴트는 "p" 키. 다른 lua  화장과 겹쳐서  "keypad 0" 번키로 변경 사용중.)

   | ~~/script/uosc.lua

   | ~~/script/uosc_shared 폴더

   | ~~/script-opts/uosc.conf

   ** UOSC 우크릭 메뉴 수정, UOSC 단축키 수정  | ~~/input.conf

 

 

D. Mpv-Playlistmanager : 00:00:49.100 지점부터

    https://github.com/jonniek/mpv-playlistmanager

    (위 UOSC 내장 플레이리스트 뷰어는 콘솔 상의 단축키로는 안 먹고, X 출력상의 화면에 커서가 있을때만 단축키가 먹는다. 그리고 마우스 크릭 메뉴가 따로 있다.

    D번은,,, curosr 가 콘솔상에 있든, X의 출력 화면상에 있든 단축키가 2다 먹는다다. 가장 빠르고 가볍다.)

   | ~~/script/playlistmanager.lua

   | ~~/script-opt/playlistmanager.conf

    아래 적절한 값으로 외형을 꾸미고 편집 가능

#### ------- Mpv-Playlistmanager configuration ------- ####

#### ------- FUNCTIONAL ------- ####

#json format for replacing, check .lua for explanation
#example json=[{"ext":{"all":true},"rules":[{"_":" "}]},{"ext":{"mp4":true,"mkv":true},"rules":[{"^(.+)%..+$":"%1"},{"%s*[%[%(].-[%]%)]%s*":""},{"(%w)%.(%w)":"%1 %2"}]},{"protocol":{"http":true,"https":true},"rules":[{"^%a+://w*%.?":""}]}]
filename_replace=[{"ext":{"all":true},"rules":[{"_":" "}]},{"ext":{"mp4":true,"mkv":true},"rules":[{"^(.+)%..+$":"%1"},{"%s*[%[%(].-[%]%)]%s*":""},{"(%w)%.(%w)":"%1 %2"}]},{"protocol":{"http":true,"https":true},"rules":[{"^%a+://w*%.?":""}]}]
#empty for no replace
#filename_replace=

#filetypes to search from directory
#loadfiles_filetypes=["mkv","avi","mp4","ogv","webm","rmvb","flv","wmv","mpeg","mpg","m4v","3gp","mp3","wav","ogv","flac","m4a","wma","jpg","gif","png","jpeg","webp"]
loadfiles_filetypes=["mkv","avi","mp4","ogv","webm","rmvb","flv","wmv","mpeg","mpg","m4v","3gp","mp3","wav","ogv","flac","m4a","wma"]

#loadfiles at startup if there is 0 or 1 items in playlist, if 0 uses worḱing dir for files
#requires --idle=yes or --idle=once if 0 files in playlist
#loadfiles_on_start=no
loadfiles_on_start=yes

#sort playlist on mpv start
sortplaylist_on_start=no
#sortplaylist_on_start=yes

#sort playlist when any files are added to playlist after initial load
sortplaylist_on_file_add=no
#sortplaylist_on_file_add=yes

#yes: use alphanumerical sort comparison(nonpadded numbers in order), no: use normal lua string comparison
alphanumsort=yes

#linux=yes, windows=no
linux_over_windows=yes

#navigation keybindings force override only while playlist is visible
dynamic_binds=yes
#dynamic_binds=no

#path where you want to save playlists, notice trailing \ or /. Do not use shortcuts like ~ or $HOME
playlist_savepath=/home/nietz/Documents/

#2 shows playlist, 1 shows current file(filename strip applied), 0 shows nothing
show_playlist_on_fileload=0

#sync cursor when file is loaded from outside reasons(file-ending, playlist-next shortcut etc.)
sync_cursor_on_load=yes

#playlist open key will toggle visibility instead of refresh
open_toggles=yes

#allow the playlist cursor to loop from end to start and vice versa
loop_cursor=yes


#### ------- VISUAL ------- ####

#prefer to display titles over filenames, sorting will still use filename to stay pure
#Playing: file header, will always prefer media title
#prefer_titles=no
prefer_titles=yes

#playlist timeout on inactivity, with high value on this open_toggles is good to be yes
#playlist_display_timeout=10
playlist_display_timeout=5

#amount of entries to show before slicing. Optimal value depends on font/video size etc.
showamount=16

#font size scales by window, if no then needs larger font and padding sizes
scale_playlist_by_window=yes
#scale_playlist_by_window=no
#playlist ass style overrides
#example {\fnUbuntu\fs10\b0\bord1} equals: font=Ubuntu, size=10, bold=no, border=1
#read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags
#no values defaults to OSD settings in mpv.conf
style_ass_tags={\fnSpoqaHanSansJP\i0\shad0.5\fs10\bord1\c&Hffffff>&\3a&H00&\fry20\frx20\frz15}

#paddings for top left corner
#text_padding_x=10
text_padding_x=155
#text_padding_y=30
text_padding_y=30

#set title of window with stripped name
set_title_stripped=no
title_prefix=
title_suffix= - mpv

#slice long filenames, and how many chars to show
slice_longfilenames=no
slice_longfilenames_amount=70

#Playing header. One newline will be added after the string.
#%mediatitle or %filename = title or name of playing file
#%pos = position of playing file
#%cursor = position of navigation
#%plen = playlist lenght
#%N = newline
playlist_header=Playing: %mediatitle%N%NPlaylist - %cursor/%plen

#playlist display signs, prefix is before filename, and suffix after
#currently playing file 
#playing_str_prefix=▷ - 
playing_str_prefix=●● - 
playing_str_suffix=

#cursor is ontop of playing file
#playing_and_cursor_str_prefix=▶ - 
playing_and_cursor_str_prefix=●● - 
playing_and_cursor_str_suffix= - ●●

#cursor file prefix and suffix
#cursor_str_prefix=● - 
cursor_str_prefix=   
cursor_str_suffix=   

#non cursor file prefix and suffix
non_cursor_str_prefix=○ - 
non_cursor_str_suffix=

#when you select a file
#cursor_str_selected_prefix=● = 
cursor_str_selected_prefix=  = 
cursor_str_selected_suffix=

#when currently playing file is selected
#playing_str_selected_prefix=▶ = 
playing_str_selected_prefix= ●● - 
playing_str_selected_suffix=

#top and bottom if playlist entries are sliced off from display
playlist_sliced_prefix=...
playlist_sliced_suffix=...

#show file playlistnumber before filename ex 01 - ▷ - file.mkv
#show_prefix_filenumber=no
show_prefix_filenumber=yes
#show playlistnumber before other prefixes
show_prefix_filenumber_first=yes
#prefix and suffix will be before and after the raw playlistnumber
prefix_filenumber_prefix=
prefix_filenumber_suffix= -

 

 

 

E. mpv-gallery-view : 00:01:09.600 지점부터

   https://github.com/occivink/mpv-gallery-view

 "f" 키로 전체화면으로 전환한 후 "g" 키,  "~/.cache/thumbnails/mpv-gallery" 폴더에..  플레이리스트의 영상 파일들 또는 음원 파일들의 썸네일 이미지를 최조 한번 생성한다.

 "c" 키 :  해당 영상 파일의 50 구간의 타임스탬프 쎔네일 이미지를 생성한다. 이하 cache 폴더에 해당 영상 파일 최초 한번 

   | ~~/script/gallery-thumbgen.lua

   | ~~/script/contact-sheet.lua

   | ~~script-modules/gallery.lua

  | ~~/script-opt/gallery_worker.conf

  | ~~/script-opt/playlist_view.conf

  | ~~/script-opt/contact_sheet.conf

 

** 위 3가지 정도의 playlist viewer 플러그인 이면 충분.

   저는 음원 파일들은 "E 플러그임" 을 사용하고,  영상파일/TV채널 일때 "D 플러그인을 " 사용하는게 좋더라구요. 영상파일들이 많으면 최조 썸네일 생성에 시간도 좀 걸리기도 하구요. 

 

 

***  확장 플러그인 호출 단축키 :   lua 확장 플러그인들이 엄청 많이 설치되어 있으면 일일히 다 뜯어서, 서로 단축키 겹치지 않게 설정하는게 중요.

(저는 설치/설정보다.  단축키 겹치지 않게 변경해서 키바인딩하는게 더 시간이 많이 걸리더라구요.

현재. window manager의 단축키,  각종 프로그램들의 단축키,  mpv 의 단축키 등등 서로 겹치지 않게 설정하는데 정말 많은 시간을 투자한 것 같아요,  현재 1000여개 이상의 단축키 사용중.. 천자문인가???? )

  | ~~/input.conf

ctrl+p script-message playlistmanager sort startover
p script-message playlistmanager show playlist toggle
i script-binding jumptofile

g script-message contact-sheet-close; script-message playlist-view-toggle
c script-message playlist-view-close; script-message contact-sheet-toggle

KP0 script-binding uosc/playlist

 


 

3. Audio Custom Script

   ※ 1.  mpc + coverart 커스텀. 1-key 단축키

🔎 설치

$ sudo pacman -S kitty mpd mpc ncmpcpp ffmpeg nsxiv tmux fzf rofi
$ yay -S cava dunst-git



🔎 mpd 설정 : 생략


😀 ~/.local/bin/music 

#!/bin/bash

session="Music"
cover="/home/[user]/Music/cover_ori.png"

#mpv --profile=tv -x11-name=livetv  ~/TVChannels/my_tvheadend.m3u & >/dev/null 2>&1

if ! pidof mpd >/dev/null; then mpd; fi
[[ ! -f $cover ]] && ffmpeg -f lavfi -i color=c=red:s=320x240:d=10 -vf "drawtext=fontsize=30:box=1:boxborderw=5:boxcolor=white@0.25:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:text='Clien Linuxer'" $cover;

tmux -2 has-session -t $session
if [ $? -eq 0 ]; then
    tmux -2 attach -t $session
    /usr/bin/kitty --class=music-tmux --hold sh -c "tmux attach-session -t Music" & >/dev/null 2>&1
    /usr/bin/nsxiv -b -N nsxiv-mpd $cover & >/dev/null 2>&1
    exit 0
fi

tmux new-session -d -s $session
tmux set-option status off

tmux send-keys -t "$session:1" '/usr/bin/ncmpcpp' C-m
tmux split-window -t "$session:1"  -v -p 30 -d '/usr/bin/cava'

tmux new-window -t "$session:2"
tmux send-keys -t "$session:2" '~/.local/bin/notification music 2>/dev/null' C-m

tmux select-window -t "$session:1"
#tmux attach-session -t $session

/usr/bin/kitty --class=music-tmux --hold sh -c "tmux attach-session -t Music" & >/dev/null 2>&1
/usr/bin/nsxiv -b -N nsxiv-mpd $cover & >/dev/null 2>&1




😀 ~/.local/bin/notification

#!/bin/bash

MUSIC_DIR="$HOME/Music/"
ICON="$MUSIC_DIR/cover.png"
ICON2="$MUSIC_DIR/cover_ori.png"

#spotify_app="ncspot"       # spotify, ncspot
spotify_app="spotify"       # spotify, ncspot
spotify_cover="$HOME/Music/cover_spotify.jpg"

# mpd or mopidy
function music
{
    while true; do
    mpc idleloop |
    while read update; do
        if [ $update == "player" ]; then
            ffmpeg -i "$MUSIC_DIR/`mpc current -f '%file%'`" -vf scale=100:-1 \
                -codec png -y $ICON >/dev/null 2>&1;
            ffmpeg -i "$MUSIC_DIR/`mpc current -f '%file%'`" -vf scale=480:-1 \
                -codec png -y $ICON2 >/dev/null 2>&1;

            # Get fields from mpc, split by tabs.
            IFS=$'\t' read album artist title \
              <<< "$(mpc --format="%album%\t%artist%\t%title%")"

            notify-send --urgency=normal --expire-time=5000 --app-name=mpd \
              --icon=$ICON "   $title" "  by  $artist"
        elif [ $update == "mixer" ]; then
            # Get volume
            vol=`mpc | tail -n1 | awk '{print $2}'`
            notify-send --urgency=low --expire-time=1000 --app-name=mpd \
              --icon=$ICON " Volume: $vol" "Mixer Changed"
        fi
        done
    done
}

# spotify
function music_spotify
{
    if pidof ${spotify_app} >/dev/null; then
        old_cover=$(playerctl -p ${spotify_app} metadata mpris:artUrl)
        wget ${old_cover} -O ${spotify_cover} & >/dev/null 2>&1;
        while true; do
            new_cover=$(playerctl -p ${spotify_app} metadata mpris:artUrl)
            if [[ ${old_cover} != ${new_cover} ]]; then
                wget ${new_cover} -O ${spotify_cover} & >/dev/null 2>&1;
            fi
            old_cover=${new_cover}
        done
    else
        :
        #ffmpeg -f lavfi -i color=c=red:s=320x240:d=10 -vf "drawtext=fontsize=30:box=1:boxborderw=5:boxcolor=white@0.25:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:text='Clien Linuxer'" -frames:v 1 -y ${spotify_cover};
    fi
}

case $1 in
    music)      music   ;;
    music_s)    music_spotify   ;;
    *)          exit 1  ;;
esac



😀 ~/.zshrc (or ~/.bashrc)

# fzf {{{
## mpd
fmpc() {
  local song_position
  song_position=$(mpc -f "%position%) %artist% - %title%" playlist | \
    fzf-tmux --header="♬ MPD Playlist Queue" --query="$1" --reverse --select-1 --exit-0 | \
    sed -n 's/^\([0-9]\+\)).*/\1/p') || return 1
  [ -n "$song_position" ] && mpc -q play $song_position
}
$ source ~/.zshrc



😀 ~/.config/cava/config

# The number of bars. 0 (default) sets it to auto (fil upp console).
# width of bras and space between bars in number of characters. Default witdth 3 and space 1.
bars = 0 
bar_width = 1
bar_spacing = 1



😀 ~/.config/dunst/dunstrc

[MPD]
    appname = mpd
    format = "  Mopidy\n\n  %s\n   %b"
    #new_icon = /home/nietz/Music/recover.png 
    timeout = 5



🔎 nsxiv 이미지 뷰어의 backgound 색상 변경
😀 ~/.Xresources

!! 아래 색상값은 kitty 터미널의 Dracula 테마의 "background" 색상임
*background:                    #282a36
$ xrdb -merge .~/.Xresources


 이후. 실행되는 nsxiv 백그라운드 색상은 위의 값으로 나타남.



🔎🔎 Music 실행 방법

🔎 실행 1 :  런처에서 "music" 엔터 

🔎 실행 2 :  또는, 런처에서 "kitty -e music" 엔터

🔎 실행 3 :  또는, 터미널에서  "music" 엔터   (3가지 방법 중에서 택 1)

(저의 경우 런쳐는 "rofi")

1.  drun : "mod4(=윈도우키) + o" ---> "rofi -show drun -show-icons"

2.  run : "mod4(=윈도우키) + shift + o" ---> "rofi -show run -modi 'run'"             <--  이것



🔎 실행 4: ~/.local/bin/music  파일을 WM이나 DE에서 제공하는 단축키 세팅밥법을 통해  키바인딩 , 실행 1과 같은 결과

(정리:  "윈도우키 + shift + o + music + Enter" 하면 실행 됨. 또는 1-key 단축키 바인딩 실행)



🔎 우측 화면에뜨는 "nsxiv" 의 coverart,  "kitty" 터미널의 tmux(ncmpcpp + cava) 윈도우 창은

1. WM/DM 에서 제공하는 룰을 통해 자동 배치 룰을 작성해서 화면 배치, 

2. 또는 2개의 각각의 윈도우 창을 실행 할때, --geometry  옵션을 통해 수동으로 고정

3. 또는 마우스 드래그에서 위치를 수동으로 고정.

 

 


Video Custom


 

1. mpv install and setting

 

🔎 설치  

$ sudo pacman -S mpv lua python vapoursynth kitty

 

 (vapoursynth 프레임 보간 할려면, $ yay -S mpv-vapoursynth   이걸로 대체 설치)

🔎 REPL mpv-live-filters :  https://github.com/hdb/mpv-live-filters


😀 ~/.local/bin/tv

#/usr/bin/sh

main ()
{
    mpv $HOME/TVChannels/my_tvheadend.m3u --profile=tv --x11-name=tv --playlist-start=21
}

main

 

  ( "--profile=tv" 는 아래 블락 참조,  "--x11-name=tv" 는 xmonad wm 설정에서 사용할 $ xprop 값 )


😀 ~/.conofig/mpv/mpv.conf
    위 영상에서 처럼 filter 체인과 custom shader 구성을 수동으로 단축키나 커맨드로 하나 하나씩 직접 입력해도 되지만. 매번 그리하기엔 귀찮으니깐.. 위 영상에서 사용한 오디오/비디오 필터 순서대로, 그리고 커스텀 세이더를 적용해서  [tv] profile 을 구성하면 해당 필터들이 아래 이미지처럼 자동 실행된다. 커스텀 세이더를 사용할 꺼이기 때문에 mpv 내장 스케일링 알고리즘 옵션들은 가능한 다 꺼버린다.
    ( tv profile :   제 하드웨어 사양에선 CPU: 15%,  GPU:: 25% 정도 먹네요 )

[tv]
#vo=gpu-next
#gpu-api=vulkan
#hwdec=vulkan
#gpu-context=x11vk
vo=gpu-next         #vo=gpu
hwdec=nvdec-copy
no-resume-playback
cache=auto
x11-name=tv
#demuxer-readahead-secs=20
af-clr
vf-clr
glsl-shaders-clr
af="lavfi=[loudnorm=I=-14:TP=-3:LRA=4]"
scale=bilinear
cscale=bilinear
dscale=bilinear
scale-antiring=0
cscale-antiring=0
dither-depth=no
correct-downscaling=no
sigmoid-upscaling=no
deband=no
vf=lavfi=graph="bwdif=mode=send_frame:parity=auto:deint=all"    #deint=interlaced
vf-add=lavfi=graph="unsharp,atadenoise"
vf-add="vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow_b4c8.vpy:buffered-frames=4:concurrent-frames=8"
# Kriigbl Chroma Up/Down Scaling, then SSimDownscaler L2 pass 1
glsl-shaders="~~/shaders/krigbl.glsl:~~/shaders/ssimds.glsl"
screen=1
fs-screen=current

 

 

 



2. (번외. 쉬어가는 쉼터) 초보자용 :  kitty 터미널 미니멀 설정, 색상 테마 설정 등등

$ kitten theme


치면,, 테마 선택 윈도우가 뜬다. 영상에서 처럼 사용하고 싶은 테마를 선택 한 후,  1. Enter    2. m 키(Modify)  를 누르면 테마 색상 값이 변경 됩다. 이후. 다음 kiity 실행 해 보변. 변경된 색상으로 나온다

kitty 재시동 안하고, 바로 테마 파일이나 kitty 환경 설정 파일 반영할려면,   

- Reload Kitty Terminal: 단축키 : "ctrl  + shift + F5" 키를 누르면 즉각 반영된다.

| 테마 파일 저장 위치 :  ~/.config/kitty/current-theme.conf

| kitty 환경 설정 파일 : ~/.config/kitty/kitty.conf  (kitty 터미널창에서 "ctrl + shift + F2" 키를 누르면 해당 환경 설정 파일이 바로 열립니다.


😀 ~/.config/kitty/kitty.conf

# BEGIN_KITTY_THEME
# Dracula
include current-theme.conf
# END_KITTY_THEME

font_family      IosevkaTerm Nerd Font Mono
bold_font       auto
italic_font      auto
bold_italic_font auto

font_size 11.0

window_margin_width 16

confirm_os_window_close 0

 



3. 부록

▌MPV + Vapoursynth_Nvdia_NVOF + Lua+ Nvdia Shader 환경 구축하기 (윈도우 OS 기준,  Linux  같은 mpv 설정 파일로 동작함)


※ Custom_mpv 설치/설정  한방팩: 3분카레. 마우스 5번 크릭하면 완료. 아래 링크에 언급된 실시간 보간을 사용할 수 있다(rife 제외, 의미없어서 뺏다)

 




1. 설치: Window OS 기준 (리눅스 생략, Window 10 에서 설치 동작 확인 완료.)

mpv_custom.7z : https://drive.google.com/drive/folders/11HDjNzo6zkMeJvfeMFui3X6GuHM1mYjS?usp=sharing


1-1. 위 링크에서 mpv_custom.7z 압축 파일을 받아서 아무 폴더에 푼다. portable mpv 설치임. 압축 해제 암호: "URE-091"

압축을 풀면. "mpv\Installer" 폴더가 있음. 걸로 들어가서. 5개 파일 설치한다. 반드시 순서대로. 관리자 권한으로 실행(더블크릭한다) 설치

(mpv.exe 파일을 환경변수 path 에 등록하면 파워셀에서 실행할 경우가 있을때 유용하다)

"mpv\Vapoursynth\plugins" 폴더에 들어가면 vapousynth dll 플러그인이 여러갠 있다. -- dll 파일 전부다 복사해서 "c:\program files(x86)\vapoursnth\plugins" 폴더에다 붙여 넣으면 작동함

하나 빠드렸네요. yt-dlp.exe(구: youtubd-dl.exe) 파일을 mpv 폴더에다 넣어 주세요. mpv.exe. 파일 있는 곳


- lua 설치 파일 제외하곤, 당시 세팅/작성일자 기준(2023-04-14 일자): 전부 최신 최신 버젼의 64bit(x64)설치파일/Vapoursynth Plugin 들입니다. (mpv 는 mpv_Update 파일을 관리자 실행하면 바로 최신버젼으로 업데이트 가능할 거에요. 업데이트 추천안함.버전 올라가서 mpv 옵션값 바뀌부분 잇으면 설정값 다시 수정해줘야함)



2. 설정: 이미 다 되어 있다

mpv.conf : mpv 환경설정 파일

input.conf : mpv 단축키/우크릭 메뉴 설정 파일

 

3. mpv 데모/사용법 : 윈도우 탐색기에서 음악 파일이나, 영상 파일을 더블 크릭하면 mpv 로 재생된다

  - 영상 링크 : https://drive.google.com/file/d/1LKSBb7enkeCK1uTUdBf2Qm-EAeqcYy9j/view

     | 필터 작동 확인 유무 : "I" 키 + "1" 번키

     | Shaders 작동 확인 유무 : "I" 키 + "2" 번키

     | 현재 설정된 단축키 목록 보기 : "I" + "4"



- 야동 재생에 필수 단축키 세팅

   | 1, 2, 3, 4, 5: 5가지 종류 보간 필터 토글(1키고 다시 1누르면 꺼짐), 모든 적용된 비디오 필터 제거(초기화) : 6번키 or H

   | 8번 : fulllscreen 토글(전제화면 모드 켜기/끄기) | 9번 : 자막 단위 역방향 이동, 0번 키: 자막 단위 정방향 이동

   | F10, F1, F2 ~~~ F9 : 영상의 첫분분, 10%, 20% ~~~ 90% 구간 이동

   | 스킵 재생(Seeking) : 화살표 4개(+10, -5, +60, -60 초), PGUP(+10분), PGDWN(-10분)

   | 현재 적용된 모든 Shader 제거: h 키


모든 필터나, 세이더는 중첩해서 여러개 사용 할 수 있다. 실시간 화면 캡쳐 인코딩 하면서, 무거운 CPU 필터 2개 썯더니, 영상이 버벅인다. ㅠ.ㅠ. 평상시 2개는 문제 없이 실시가 재생됨(프레임 드랍, 프레임 스킵 없음)



- FHD 1080p 모니터 기준

   480p, 720p 소스를 재생하면, Upscaler 가 작동하고,

   4K,2160p 이상의 소스를 재생하면, Downscaler 가 작동한다. 1080p 소스를 fullscreen 재생이 아닌. 72% 정도의 윈도우 창 모드 크기로 재생해도 Downscaler 가 작동한다.


   Nvida 카드인 경우 : NVScaler (업스케일러) 세이더, NVSharpen 세이더(1080p 모니터에 1080p 소스를 fullscreen 으로 재생시 작동)

   AMD 카드인 경우 : SharpenCAS 세이더, AMD FidelityFX Super Resolutioin 세이더 사용


   나머지 비디오 재생 옵션들 설명 생략, mpv.conf 파일 열어보면 잡다한 세팅이 많음.

   [야동 480p], [야동 4K-60fps] 등등의 프로파일(=일종의 프리셋) 설정 해 놓았음

 
(2018 년에) SVPflow plugins 페이지 : https://www.svp-team.com/wiki/Manual:SVPflow 에 들어 갔을땐. svpflow1.2.so[dll] 플러그인을 다이렉트 다운받을 수가 잇었는데. 지금 들어 가보니. 페이지 옵션값 설명은 그대로 인데. 전제 SVP4 프로그램 다운페이지로 연결됨 ㅠ.ㅠ

SVP4 평가판 깔아서. svpflow1.dll, 등등 파일 추출 해서, 적용 해 놓았음

윈도우 OS 에서 SVP4 깔아 봤는데. 영상 재생하다가 좀 지나니깐 빨간색 Border가 생김,


우크릭 메뉴 Auto Corp(단축키 "C" 로 활당함) 또는 input.conf 파일의 2 3 4 5 보간 필터값에 아래 화면의 "Crop" 필터값 적용해서, 동서남북 새빨간 Border 4px 씩 날렷음




4. 4K 소스 8K 업스케일 재생시 CPU/GPU 사용율 체크

  - 소스 : CAWD-266.mp4 | 20 G 정도(?) | 29.970 fps 파일

  - 60 fps 변환 재생, 8K 업스케일 재생, NVScaler 세이더 적용, 화면 캡쳐 인코딩 빼면 GPU 90% 선에서 잘 돌아감

   * 캡쳐 영상 : 3840p x 1080p | 60 fps | 좌측이 59.940 fps 보간 재생, 우측이 29.970 fps 원본 프레임 레이트 재생
     영상 링크 : https://drive.google.com/file/d/1GnNRxrmkIkexdMauP_gtnr6_1hMMgqXb/view
                      (웹 재생은 1080p, 다운받으면, 8K 해상도 영상)



   4K 원본 프레임 (누르면 이미지가 커집니다.)


    8K 업스케일링 된 프레임 : NVidia Scaler Shader



[ mpv 가 재생이 잘 안될 때, 환경 설정 값 최소치 설정 ]

가. mpv.conf: 환경 설정 파일 설정 예시

# this is required for Vapoursynth to "catch" the mpv
input-ipc-server=mpvpipe

# hardware video decoder
hwdec=d3d11va-copy
#hwdec=dxva-copy
hwdec-codecs=all
#hwdec-codecs="h264,hevc,vp8,vp9,av1,mpeg2video"

# may fix audio desync in come cases
hr-seek-framedrop=no

vf="vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow.vpy,lavfi=[gradfun=strength=3.5:radius=16]"

profile=opengl
# high quality video output, require rather fast video card
#profile=opengl-hq

opengl-backend=angle

# "ReClock" replacement
#video-sync=display-resample
#video-sync-max-video-change=5



나.. input.conf: mpv 단축키 키바인딩 설정 예시

1 vf toggle "vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow.vpy,lavfi=[gradfun=strength=3.5:radius=16]"




※※ 야동 감상 최적화 세팅값

※ "야동-HD" Auto-Profile(프리셋)

   | CPU: Ryzen 2700X | GPU: Nvidia GTX 1660 Super 기준 아래 Profile 은 Idle 대비 CPU 50%, GPU 45% 정도의 점유율은 먹는다. 프레임 드롭/프레임 스킵/A-V Desyc 가 발생하지 않는 셋팅값

   | mpv.conf

# Auto Profile : Video file, 720p ~ 1080p,  29.970fps ~ 59 fps source, "블라블라/Jav/블라블라" 폴더
[야동-HD]
#profile-desc=cond:get('width', -math.huge) <= 1920 and get('container-fps', 0) <=59
profile-cond=width >=720 and width <= 1920 and fps <=59 and require 'mp.utils'.join_path(working_directory, path):match('/jav/') ~= nil and duration >= 600
af-clr
vf-clr
glsl-shaders-clr
input-ipc-server=/tmp/mpvsocket
hr-seek-framedrop=no
vo=gpu,tct
gpu-api=auto
gpu-context=auto
hwdec=cuda-copy
hwdec-codecs="h264,hevc,vp8,vp9,av1,mpeg2video"     # vc1(wmv) 코덱, 녹색 스크린, 하드웨어 디코딩 에러남.
profile=gpu-hq
scale=ewa_lanczossharp  # mpv 내장 스케일러 : gpu 사용량 높음
cscale=ewa_lanczossoft
dscale=mitchell
cscale=ewa_lanczos
scaler-resizes-only
scale-antiring=0.7
cscale-antiring=0.7
dither-depth=auto
correct-downscaling=yes
sigmoid-upscaling=yes
deband=no      #deband=yes      # 아래 f3kdb vapursynth deband 로 대체함
tscale=sphinx
tscale-blur=0.6991556596428412
tscale-radius=1.05              #lower (e.g. 0.955) = sharper; higher (e.g. 1.005) = smoother
tscale-clamp=0.0
override-display-fps=60
video-sync=display-resample
af="lavfi=[loudnorm=I=-14:TP=-3:LRA=4]"
af-add='dynaudnorm=g=5:f=250:r=0.9:p=0.5'
vf="vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow_b4c8.vpy:buffered-frames=4:concurrent-frames=8,vapoursynth=~~/vapoursynth/flash3kyuu.vpy"
glsl-shader="~~/shaders/krigbl.glsl"     # Chroma Up/Down Scaling, KrigBilateral Downscaling Y pass 1
glsl-shader="~~/shaders/NVSharpen.glsl"    # NVIDIA Image Scaling v1.0.2 - Sharpen
glsl-shader="~~/shaders/NVScaler.glsl"     # NVIDIA Image Scaling v1.0.2 - Scaler
glsl-shader="~~/shaders/ssimds.glsl"     # SSimDownscaler L2 pass 1

[야동-예고편]
profile-cond=require 'mp.utils'.join_path(working_directory, path):match('dmhttp://m.co.jp') ~= nil
profile="야동-HD"
vf-clr
glsl-shaders-clr
vf="vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow_b4c8_480p.vpy:buffered-frames=4:concurrent-frames=8,vapoursynth=~~/vapoursynth/flash3kyuu.vpy"
#glsl-shaders="~~/shaders/krigbl.glsl:~~/shaders/ssimsr.glsl:~~/shaders/FSR.glsl:~~/shaders/adasharp.glsl"
#glsl-shaders="~~/shaders/F8.glsl:~~/shaders/krigbl.glsl:~~/shaders/ssimsr.glsl:~~/shaders/ssimds.glsl"
#glsl-shaders="~~/shaders/ravu_L_r4.hook:~~/shaders/krigbl.glsl:~~/shaders/ssimsr.glsl:~~/shaders/ssimds.glsl"
glsl-shaders="~~/shaders/F8.glsl:~~/shaders/krigbl.glsl:~~/shaders/FSR.glsl:~~/shaders/ssimsr.glsl:~~/shaders/ssimds.glsl:~~/shaders/NVSharpen.glsl"


- 로직 : GPU롤 하드웨어 디코딩 한 후, Vapoursynth NVOF로 60fps 보간 하고, Vapoursynth fd3kdb 16bit 디벤딩 필터를 적용 한다. 영상 관련 스케일링 알고리즘은 고품질로 다 때려 박았다. 이 후 각종 Shaders 들을 적용 하였다

| vf=Video filter : mpv 내장 필터(deband, sharpen, denoise, deinterlace 등등), 외부 lavfi(=ffmpeg 외장 필터), 외부 vapoursynth 필터등을 적용 할 수 잇다. 필터 중첩 적용,필터 무한 확장이 가능하다.

| af=Audio filter : 오디오 Normalize 증폭 하였다. MVSD-541 품번의 시작 부분의 "카나 줌마의 배추 씻는 소리가 잘 들리고, 홍콩 가는 소리가 잘 들리는 것 같다.

| profile-cond=width >=720 and width <= 1920 and fps <=59 and require 'mp.utils'.join_path(working_directory, path):match('/jav/') ~= nil and duration >= 600

   * 옵션 설명

     본인의 야동 모으는 폴더는 "/mnt/jav" 야동 파일은 "/mnt/jav/품번/품번.mp4", 본인의 야동 받는 토랭이 폴더는 "/run/media/user/External_HDD/jav/Studio/제작사" 이다.

     2군데 폴더의 공통점은 경로명에 "jav" 가 들어간다는 것. 따라서 2군데의 영상 파일들을 재생한다면

     "/mnt/jav/MVSD-542.mp4" "/run/media/nietz/External_HDD/Jav/Studio/M s Video Group/MVSD-541.mp4" 파일이며, 파일의 절대 경로에는 jav 가 들어 간다. 리눅스 OS인 경우 폴더 구분자 "/jav/", Winidow OS 인 경우 "\\jav\\"

     따라서,

1. 절대경로 파일명에 "jav' 가 들어가며, 해상도는 가로 1280p 보다 크거나 같고, 가로 1920p 보다 작거나 같으며,, 프레임 레이트는 59fps 보다 작으면 "야동-HD" profile 이 실행 된다.

HD: 1280x720p | FHD: 1920x1080p | 29.970 fps, 30 fps 가 여기에 해당 된다.

59.940 fps, 60fps 는 조건보다 크므로 해당 파일은 이 Profile로 실행 되지 않는다., 480p, 4K 파일도 위 조건을 벗어나므로 해당 영상은 이 Profile 이 적용 되지 않는다.

duration : 런타임 재생시간 600초=10분 보다 클 경우. 이미지들은 duration=0, gif, webp, mp3, flac, m4a 등은 10분 보다 작으므로 이 Profile 이 적용 되지 않느다.

영화/드라마/애니메이션 모으는 사람들은 "영화" 폴더, "드라마" 폴더, "애니메이션" 폴더에 아래에 관련 영상들을 모을 것이다.

위. "profile-cond" 조건을 위와 유사하게 설정하여. 해당 영상의 Auto-Profile 를 적용 할 수 잇다. 애니메이션이면, 애니메이션 전용 shader 들을 적용하고, 드라마면 60fps 보간하고, 고품질 세이더 적용하고, 영화 파일이면, 24fps 고정하고, 프레임 보간 하지 않고, 고품질 세이더만 적용 하는 식으로.

보인의 CPU, GPU 사양을 고려하여, 실시간 재생이 부드럽게 되는 세팅 값을,, 모든 종류의 영사들에 자동으로 적용 할 수 있다.

10개에서 15개 정도의 Auto-Profile 을 조건 지정 하여, 영상 옵션들을 세부적으로 다 다르게 지정하여... 모든 종류의 영상들을 최적의 값으로 자동으로 세팅하여, 재생이 가능하게 된다.

 

[야동-예고편]
profile-cond=require 'mp.utils'.join_path(working_directory, path):match('dmm.co.jp') ~= nil
profile="야동-HD"

 


야동 예고편 주소는 "http://시시삼공공일.다무무.co.제피/litevideo/freepv/h/h_2/h_237nacr549/h_237nacr549_dmb_w.mp4"

이렇다. 영상 경로에 다무라.xx.xx 가 들어간다.

따라서, profilee-cond는 match('dmm.co.jp')

profile="야동-HD" 라고 기재해서 "야동-HD" 영상 옵션들을 그대로 상속해서 똑같이 사용한다.

그러나 2024년 2월 기준 전후, 이전 예고편 주소는 해상도가 "720x404p" SD 해상도이다. NVOP vapoursynth 스크립트에서 HD, FHD, 4K 해상도는 nvof_blk = 16 값을 사용하고, SD 해상도는 nvof_blk = 8 값을 사용한다.

(현재 2024 2월 이후 기준, 해상도는 URL 접미사 기준 mhb=404p, hhb=1080p, 4k=4K, 4ks=4k60fps 이다)

또한 FHD 해상도 모니터에서 SD 소스를 재생하므로, Upscaling 이 많이 발생한다.

따라서, vf-clf, glsl-shaders-clr 옵션으로, 기 적용 된 비디오 필터들과 Shader들을 전부 초기화 한 후, SD 해상도에 많는 필터들과, shader 들을 적용 하여야 한다.

 


※ 기타 : mpv는 인자를 절달하여. 외부 프로그램을 실행시키는 것이 가능하다

- input.conf

ctrl+i run "/bin/sh" "-c" "jt ${filename/no-ext} mpris"; run "/bin/sh" "-c" "dunsty_jav.sh ${filename/no-ext}"


| ${filename/no-ext} :  확장자를 제외, 실제 파일 이름을 의미한다.

| ex. 실제 파일 이름이 "hhd800@JUQ-266.mp4" 인 경우, ${filename}=hhd800@JUQ-266.mp4, ${filename/no-ext}=hhd800@JUQ-266



※ 유용한 링크

1. Anime4K : A High-Quality Real Time Upscaler for Anime Video https://github.com/bloc97/Anime4K


Optimized shaders for higher-end GPU:

(Eg. GTX 1080, RTX 2070, RTX 3060, RX 590, Vega 56, 5700XT, 6600XT)
If upscaling to resolutions smaller than 4K, lower end GPUs can be used.

CTRL+1 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode A (HQ)"
CTRL+2 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode B (HQ)"
CTRL+3 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Upscale_Denoise_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode C (HQ)"
CTRL+4 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_Restore_CNN_M.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode A+A (HQ)"
CTRL+5 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_M.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode B+B (HQ)"
CTRL+6 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Upscale_Denoise_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Restore_CNN_M.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode C+A (HQ)"

CTRL+0 no-osd change-list glsl-shaders clr ""; show-text "GLSL shaders cleared"




2. MPV: User Script:https://github.com/mpv-player/mpv/wiki/User-Scripts


3. Mathematical evaluation of various scalers - My config uses the best scalers/settings from this analysis. https://artoriuz.github.io/blog/mpv_upscaling.html


4. mpv Configuration Guide for Watching Videos by Kokomins : https://kokomins.wordpress.com/2019/10/14/mpv-config-guide/


5. mpv.conf guide by iamscum : https://iamscum.wordpress.com/guides/videoplayback-guide/mpv-conf/


6. uosc : Feature-rich minimalist proximity-based UI for MPV player : 
https://github.com/tomasklaen/uosc
 ( lua script 로 동작하는 mpv 커스텀/미니멀리즘 UI. 구드 파일 custom_mpv.7z 파일에 이미 적용 시켜 놓음 )

 



 

4. Video Custom Script

 

가.  ~/.zshrc 파일 :   $ sod [sod품번]  , $dahlia [fallneo|dahlia 품번] 을 터미널에 치면 실행됩니다.

   * 해당 2개 제작사는 dmm(판자), javdb 에 메타데이타가 올라오기전에. 인터넷에 릴이 되어 배포되는 경우가 많다. 특히 팔라노 계열들. 토랭이 RSS 피드 목록을 구독하다가 선행릴이 발견되면, 무엇을 받아야 할지 고민되는 바,  사전 영화 내용이 궁금할 때,  검색이 필요하다.

: "${useragent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.88 Safari/537.36}"

_get_request () {
    _base_url=$1
    shift 1
    curl -f "$_base_url" -s -L \
        "$@" \
        -H "User-Agent: $useragent" \
        -H 'Accept-Language: en-Us,en;q=0.9' \
        --compressed
}

# $ sod stars-512
sod () {
    [[ !$# -eq 0 ]] || return;
    sod_create_host="https://ec.sod.co.jp/prime"
    sod_dvd_id=$(echo ${1} | tr '[a-z]' '[A-Z]')
    sod_sample_video_page="https://ec.sod.co.jp/prime/videos/sample.php?id=${sod_dvd_id}"
    sod_id_page="${sod_create_host}/videos/?id=${sod_dvd_id}"
    sod_cookie="/tmp/${dvd_id}-cookie-jar.txt"
    # cookie creation
    _get_request "${sod_create_host}/_ontime.php" --referer ${sod_create_host} --cookie-jar ${sod_cookie} 
    sod_sample_video=$(_get_request ${sod_sample_video_page} --referer ${sod_create_host} --cookie ${sod_cookie} | grep "source src" | sed -e 's/.*\"\(.*\)\".*/\1/')
    sod_id_page_info=$(_get_request ${sod_id_page} --referer ${sod_create_host} --cookie ${sod_cookie})
    sod_original_title=$(echo ${sod_id_page_info})
    sod_fanart=$(echo ${sod_id_page_info} | grep -Ei "$(echo ${sod_dvd_id} | sed -e 's/-/_/')_l" | cut -d '"' -f2)
    sod_gallery_set=$(echo ${sod_id_page_info} | grep -Ei "$(echo ${sod_dvd_id} | sed -e 's/-/_/')_[0-9]{2}_l" | sed -e 's/<div class=\"viclear\"><\/div>//g' | sed -e 's/^.*\"\(.*\)\".*/\1/')

    jav_lib="/mnt/jav"
    fanart_dir="${jav_lib}/${sod_dvd_id}/extrafanart"
    local_sod_fanart="${jav_lib}/${sod_dvd_id}/${sod_dvd_id}-fanart.jpg"
    local_sod_trailer="${jav_lib}/${sod_dvd_id}/${sod_dvd_id}-trailer.mp4"
    [ -d ${fanart_dir} ] || mkdir -p ${fanart_dir} || printf "Error. can't make directory."
    _i=0
    for _x in $(echo ${sod_gallery_set});
    do
        (( _i++ ))
        if [ ! -f ${fanart_dir}/${sod_dvd_id}-fanart-${_i}.jpg ]; then
            echo -e "[Gallery][${_i}] ${_x} --> ${fanart_dir}/${sod_dvd_id}-fanart-${_i}.jpg\n"
            _get_request ${_x} --referer ${sod_create_host} --cookie-jar ${sod_cookie} -o "${fanart_dir}/${sod_dvd_id}-fanart-${_i}.jpg"
        fi
    done
    if [ ! -f ${local_sod_fanart} ]; then
        echo -e "[Fanart] ${sod_create_host} --> ${sod_fanart}\n"
        _get_request ${sod_fanart} --referer ${sod_create_host} --cookie-jar ${sod_cookie} -o "${local_sod_fanart}"
    fi
    if [ ! -f ${local_sod_trailer} ]; then
        echo "[Trailer] ${sod_sample_video}\n"
        _get_request ${sod_sample_video} --referer ${sod_create_host} --cookie-jar ${sod_cookie} -o "${local_sod_trailer}"
    fi

    jav_poster_upscale ${sod_dvd_id}

    [[ $# -eq 1 ]] && \
        { feh -r ${fanart_dir} -w --slideshow-delay -0.1 --on-last-slide quit >/dev/null 2>&1
        /usr/bin/sxiv ${jav_lib}/${sod_dvd_id} -r -b -p -s f -t &
        mpv --geometry=50% --x11-name=mpsyt --profile=mpsyt --http-header-fields="Referer: ${sod_create_host}"  ${sod_sample_video} >/dev/null 2>&1 }
    rm ${sod_cookie}
}

# $ dahlia dldss-254
dahlia ()
{
    [[ !$# -eq 0 ]] || return;

    local _dvd_id=$(echo ${1} | tr '[a-z]' '[A-Z]')
    local _prefix=$(echo ${_dvd_id} | cut -d "-" -f1)
    local _number=$(echo ${_dvd_id} | cut -d "-" -f2)
    local _prefix_min=$(echo ${_prefix} | tr '[A-Z]' '[a-z]')
    
    local jav_lib="/mnt/jav"
    local fanart_image="${jav_lib}/${_dvd_id}/${_dvd_id}-fanart.jpg"
    local poster_image="${jav_lib}/${_dvd_id}/${_dvd_id}-poster.jpg"
    local extra_fanart_folder="${jav_lib}/${_dvd_id}/extrafanart"
    local extra_fanart_prefix="${jav_lib}/${_dvd_id}/extrafanart/${_dvd_id}-fanart-"
    local sample_video="${jav_lib}/${_dvd_id}/${_dvd_id}-trailer.mp4"
    local work_detail_html="${jav_lib}/${_dvd_id}/${_dvd_id}.html"
    download_poster_flag=1          # 1: Downlad,   0; Not Download
    download_extrafanart_flag=1     # 1: Downlad,   0; Not Downloadj
    download_sample_video_flag=0    # 1: Downlad,   0; Not Download
    make_work_detail_html_flag=0    # 1: Make html, 0; Not Make

    dahlia_dvd_id="${_prefix_min}${_number}"
    case ${_prefix} in
        DLDSS|DCDSS|DHLA|DLVSS)
            _maker="DAHLIA"
            dahlia_page="https://dahlia-av.jp/works/${dahlia_dvd_id}/";;
        FSDSS|FADSS|FCDSS|FLNS)
            _maker="Faleno"
            dahlia_page="https://faleno.jp/top/works/${dahlia_dvd_id}/";;
        *)  return;;
    esac

    dahlia_info=$(curl -s ${dahlia_page})
    if (echo ${dahlia_info} | grep -E "つかりませんでした">/dev/null) then echo "${_maker} ${_dvd_id} : 404 Not Found";return; fi
    dahlia_fanart=$(echo ${dahlia_info} | grep -i 'output-quality' | head -1 | cut -d'"' -f2 | cut -d'?' -f1)
    dahlia_poster=$(echo ${dahlia_fanart} | sed -e 's/1200/2125/')
    dahlia_samplevideo=$(echo ${dahlia_info} | grep -i 'pop_sample' | head -1 | cut -d'"' -f4)
    dahlia_title_ja=$(echo ${dahlia_info} | grep -i '<h1>' | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')
    dahlia_title_ko=$(trans :ko -b ${dahlia_title_ja})
    #dahlia_plot_ko=$(/home/user/.local/venv/bin/python /home/user/.local/venv/bin/translate ${dahlia_plot_ja} -d ko)
    dahlia_plot_ja=$(echo ${dahlia_info} | hxnormalize -x 2>/dev/null | hxselect '.box_works01_text p' -i -c)  # 줄거리 한 문단 출력
    #dahlia_plot_ja=$(echo ${dahlia_info} | grep -i '<p>' | head -4 | tail -1 | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')    # 줄거리 한 문장 출력
    dahlia_plot_ko=$(trans :ko -b ${dahlia_plot_ja})
    dahlia_pop_img_cnt=0
    dahlia_pop_img_cnt=$(echo ${dahlia_info} | grep -i 'pop_img' | wc -l)
    
    if [[ ${2} == "plot" ]]; then echo ${dahlia_plot_ja}; return ;fi; 
    if [[ ${download_poster_flag} == "1" || ${download_sample_video_flag} == "1" || ${make_work_detail_html_flag} == "1" ]]; then
        [[ ! -d "${jav_lib}/${_dvd_id}" ]] && mkdir -p "${jav_lib}/${_dvd_id}" >/dev/null
    fi
    if [[ ${download_extrafanart_flag} == "1" ]]; then
        [[ ! -d "${extra_fanart_folder}" ]] && mkdir -p "${extra_fanart_folder}" >/dev/null
    fi
    [[ ${download_poster_flag} == "1" ]] && [[ ! -f ${poster_image} ]] && \
        { 
            [[ -f ${poster_image} ]] || \
                {   curl -o ${poster_image} ${dahlia_poster};
                    poster_height=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1 ${poster_image} | cut -d "=" -f2);
                    if [[ ${poster_height} -gt "807" ]]; then convert -quality 90 -resize x806 ${poster_image} ${poster_image}; fi 
            [[ -f ${fanart_image} ]] || curl -o ${fanart_image} ${dahlia_fanart}  }
        }
    [[ ${download_sample_video_flag} == "1" ]] && [[ ! -f ${sample_video} ]] && curl -o ${sample_video} ${dahlia_samplevideo}

    #dahlia_box_works01_list=$(echo ${dahlia_info} | hxnormalize -x 2>/dev/null | hxselect '.box_works01_list\ clearfix li' -i -c)
    #<span>出演女優</span>
    #          <p>天使もえ</p>
    #          <span>収録時間</span>
    #          <p>60分</p>
    #          <span>配信開始日</span>
    #            <p>2020/12/13</p>
    #            <span>配信開始日</span>
    #            <p>2020/12/13</p>
    #            <span>発売日</span>
    #            <p>2021/1/21</p>
    #            <span>発売日</span>
    #            <p>2021/01/21</p>
    dahlia_actress_ja=$(echo ${dahlia_info} | grep -i '<p>' | head -5 | tail -1 | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')
    dahlia_actress_ko=$(trans :ko -b ${dahlia_actress_ja})
    #dahlia_plot_ko=$(/home/user/.local/venv/bin/python /home/user/.local/venv/bin/translate ${dahlia_actress_ja} -d ko)
    dahlia_runtime=$(echo ${dahlia_info} | grep -i '<p>' | head -6 | tail -1 | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')
    dahlia_relesedate=$(echo ${dahlia_info} | grep -i '<p>' | head -10 | tail -1 | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')
    dahlia_distributiondate=$(echo ${dahlia_info} | grep -i '<p>' | head -8 | tail -1 | sed -e 's/<[^>]\+>//g' -e 's/^\s*//g' -e 's/\s*$//g')

    ## Digest gallery images html tag
    (( dahlia_pop_img_cnt-- ))
    digest_string=""
    if [[ ${dahlia_pop_img_cnt} != "0" && ${dahlia_pop_img_cnt} != "" ]];then
        for _i in {1..${dahlia_pop_img_cnt}}
        do
            dahlia_pop_img=$(echo $dahlia_info | grep -i 'pop_img' | head -${_i} | tail -1 | cut -d'"' -f4)
            if [[ ${download_extrafanart_flag} == "1" ]]; then
                if [[ ! -f ${extra_fanart_prefix}${_i}.jpg ]]; then
                    curl -o ${extra_fanart_prefix}${_i}.jpg ${dahlia_pop_img}
                fi
            fi
            digest_string="${digest_string}
<a href=\"${dahlia_pop_img}\" target=\"new\"><img width=\"110\" src=\"${dahlia_pop_img}\"></a>"
        done
        digest_string="<center>${digest_string}</center><p>"
    fi

    dahlia_blog="<center><h4 style=\"margin: 0px auto 40px; padding-left: 13px; border-left: 7px solid rgb(210, 21, 119); outline: 0px; font-size: 30.1px; font-weight: bold;  line-height: 1.4;  font-family: 游ゴシック, YuGothic, メイリオ, Meiryo, Helvetica, Arial, sans-serif; letter-spacing: normal; text-align: center; \">
${dahlia_title_ja}
<br><b><font color=\"#ae835a\">
${dahlia_title_ko}
</font></b></h4>
▌ DVD ID: ${_dvd_id} | 제작사: ${_maker} | 게재 시작일: ${dahlia_distributiondate} | 발매일: ${dahlia_relesedate} | Runtime: ${dahlia_runtime}<br>
▌ 배우: ${dahlia_actress_ja} (${dahlia_actress_ko})<br><br>
</center>
<center><video controls muted noautoplay playsinline loop width=\"80%\" poster=\"${dahlia_fanart}\"  height=\"80%\" src=\"${dahlia_samplevideo}\" type=video/mp4></video></center><p>
${digest_string}
<br>
${dahlia_plot_ko}
<br><br>
${dahlia_plot_ja}

<br><br><hr><br>
"
    dahlia_blog=$(echo ${dahlia_blog} | \
        sed -e 's/u200bu200b//g')

    [[ ${make_work_detail_html_flag} == "1" ]] && [[ ! -f ${work_detail_html} ]] && echo ${dahlia_blog} > ${work_detail_html}
    [[ -f ${work_detail_html} ]] && firefox ${work_detail_html} &
    echo -e "\n${dahlia_blog}"
}

# $ dahlia_set fsdss-746 fsdss-744 fsdss-719 dldss-274 dldss-251
dahlia_set () {
    local temp_html="/tmp/Dahlia_set_$(date "+%Y-%m-%d").html"
    rm ${temp_html}
    touch ${temp_html}; for x in $@; dahlia $x >> ${temp_html}; firefox ${temp_html}
}

 


 

나.  ~/.local/bin/vp 파일 :   ,  ripgrep 을 이용한다. 정규식 검색, 한글 검색이 가능하다.

$ vp "검색 키워드"
#!/usr/bin/zsh

###############
# $ vp midv-530
# $ vp juq-4
# $ vp ssis-
# $ vp ".*4k.*ssis-"
# $ vp ".*bvpp.*juq-"
###############

# sftp: Exist some dvd_id movie file? in Movie File List, Support rg's regular-expression search
function vg () {
    local allplex_sftp="\/mnt\/PlexTeamDrive"      # sftp(or google-drive)'s "/" mount point
    local allplex_sftp_video_list="/mnt/Media/JAV/Allplex_jav_video_list.txt"
    local video_path_suffix=$(rg -i "${1}" ${allplex_sftp_video_list})
    [[ ${video_path_suffix} == "" ]] || echo "${video_path_suffix}" | sed -e "s/^/${allplex_sftp}\//"
}

# Allplex sftp: remote file Play with mpv.
# 우선순위
# 1. vg() 결과가 파알 경로 하나이면 바로 영상 재생
# 2. vg() 결과가 파일 경로 2개 이상이면. 파일 경로 리스트를 보여주고, 해당 파일 번호 입력 후 재생
# 3. vg() 결과가 파일 경로 0개 이면, $ jp 실행,
#    $ jp = 1순위: 로컬 하드의 해당 영상 재생. 2순위, 없으면  인터넷 해당 마그넷 검색후 바로 토랭이 재생
#    $ jp (with $ jt)
#    $ jt = 로컬의 메타데이타 또는 인터넷에서 해당 품번 메테데이타 스크랩 후 포스터와 에고편을 띠운다. 해당품번 메테데이타 내용을 /tmp/품번.info 에 저장한다.
function vp () {
    local video_path
    local sub_files=""
    video_path=$(vg "$1")

    __cnt=0;
    __cnt=$(echo "${video_path}" | wc -l)
    if [[ ${__cnt} == "1" ]]; then
        if [[ ${video_path} == "" ]]; then
            #exit
            jp $1
        else
            echo -e "${video_path}\n"
            dvd_id=$(basename "${video_path}" | cut -d' ' -f1 | cut -d'.' -f1)
            sub_files=$(get_sub ${dvd_id})
            play_mpv "${video_path}" "${sub_files}"
        fi
    else
        #declare -A arr
        set -A arr
        for ((_i=1 ; _i <= ${__cnt} ; _i++))
            do
                arr[$_i]="$(echo "${video_path}" | head -"${_i}" | tail -1)"
                echo -e "[${_i}] $arr[${_i}]"
            done
        echo

        #touch "/tmp/${1}.flg"
        #echo 1 >> "/tmp/${1}.flg"
        #if [[ $(cat "/tmp/${1}.flg") == 1 ]];then
        #    /usr/bin/kitty --class=kitty-vp --hold sh -c "vp $1" && sed -n 's/1/0/' "/tmp/${1}.flg"
        #fi
        #/usr/bin/rm "/tmp/${1}.flg"

        echo -n "Enter Number(재생할 영상 번호를 입력하시오): "; read number
        echo -e "[*] $arr[${number}]\n"
        dvd_id=$(basename "$arr[${number}]" | cut -d' ' -f1 | cut -d'.' -f1)
        sub_files=$(get_sub ${dvd_id})
        play_mpv $arr[${number}] ${sub_files}
    fi
}

get_sub ()
{
    local sub_dir="$HOME/.config/mpv/sub"               # 자막들 들가 있는 폴더
    local dvdid=$1
    local _sub_files=""
    _sub_files=$(find ${sub_dir} -type f -regextype posix-extended -regex ".*\/.*${dvdid}.*(srt|ass|sub|sup)")
    #[[ ${_sub_files} == "" ]] || {_sub_files=$(echo ${_sub_files} | sed -e ':a;N;$!ba;s/\s/:/g'); echo ${_sub_files};}     # 파일명에 공백이 들어가니 원치 않은 결과
    [[ ${_sub_files} == "" ]] || { _sub_files=$(echo ${_sub_files} | sed -z -e 's/\n/:/g' | sed -e 's/:$//'); echo ${_sub_files}; }
}

play_mpv ()
{
    local file_path="$1"
    local sub_files="$2"
    color_off='\033[0m'       # Text Reset
    black='\033[0;30m'        # Black
    red='\033[0;31m'          # Red
    green='\033[0;32m'        # Green
    yellow='\033[0;33m'       # Yellow
    blue='\033[0;34m'         # Blue
    magenta='\033[0;35m'      # Magenta
    cyan='\033[0;36m'         # Cyan
    white='\033[0;37m'        # White
    if [[ ${sub_files} == "" ]];then
        echo -e "${red}$ ${color_off}${blue}mpv ${color_off}${yellow}${file_path}\n"
        mpv "${file_path}"
    else
        echo -e "${red}$ ${color_off}${blue}mpv ${color_off}${yellow}${file_path} ${color_off}${green}--sub-files=${sub_files}\n"
        mpv "${file_path}" --sub-files="${sub_files}"
    fi
}

#vg $1
vp $1



    영상(영화/드라마/건전야동) 재생 스크립트. 윈도우 OS에는 "에브리씽" 있습니다. 리눅스에서는 마땅한 거 찾지 못해, 본인 용도에 맞게 대충 쓰자 스크립트. 아래 건전야동을 예시로 들었다.

    + 환경 : 제 하드에는 야동이 하나도 없다. 한글자막, 한글포스터만 있다. 그리고 메테데이타 파일들이 120만개 가량 있다.

    + 아래는 원격지 Remote (sfpt 나 구글 무제한 드라이브) 쪽에 10만개 가량의 영상파일들이 있다 가정한다
       사전 "rclone vfs rc" 마운트 설정이 되어 있어야 한다.

    + "$ ~/.local/bin/vp vm"  커맨드를 cron 탭에 등재하여 하루 한번이나 일주일에 한번 정도 돌려 주어.  영상 파일 경로 리스트를 먼저 생성합니다.

       쌍빵 반기가 / 반기가 속도에서 20만개 정도의 파일경로를 스캐닝 하는데. 구글 rclone 마운트는 어마어마 하게 시간이 많이 걸려서 한달에 한번 추천,  remote sftp 나 그 보다 더 상위 전송 프로토콜에서는 5분 정도 걸려서 하루 한번 정도 스캐닝 추천합니다.

    + 특정(특정 품번) 파일을 보유하고 있는가?
    ~/.zshrc 파일

# Create File List
function vm () {
    /usr/bin/rm allplex_list.txt Allplex_jav_video_list.txt
    rclone rc vfs/refresh recursive=true;
    rclone ls allplex:/ > allplex_list.txt;
    rg -i '.*.(mkv|mp4|wmv|m4v|avi|mov|mpg|mpeg|ts|vob|사본)$' allplex_list.txt | sed -E 's/^(\s*)?[0-9]+ //' | sort > Allplex_jav_video_list.txt;
    mv /mnt/Media/JAV/Allplex_jav_video_list.txt "/mnt/Media/JAV/Allplex_jav_video_list.txt-$(date "+%Y-%m-%d")"
    /usr/bin/cp -f Allplex_jav_video_list.txt /mnt/Media/JAV/Allplex_jav_video_list.txt
}

# sftp: file list check - exist file?
function vg () { 
    local allplex_sftp="\/mnt\/PlexTeamDrive"      # sftp: mount point : "/"
    local allplex_sftp_video_list="/mnt/Media/JAV/Allplex_jav_video_list.txt"
    local video_path_suffix="";
    video_path_suffix=$(rg -i "$1" ${allplex_sftp_video_list} | cut -d':' -f2)
    #[[ ${video_path_suffix} == "" ]] || echo ${allplex_sftp}/${video_path_suffix}
    [[ ${video_path_suffix} == "" ]] || echo ${video_path_suffix} | sed -e "s/^/${allplex_sftp}\//"
}
$ vg "품번"

      



   + 영상 재생 우선순위

# 1. vg() 결과가 파알 경로 하나이면 바로 영상 재생

# 2. vg() 결과가 파일 경로 2개 이상이면. 파일 경로 리스트를 보여주고, 해당 파일 번호 입력 후 재생

# 3. vg() 결과가 파일 경로 0개 이면, $ jp $1 실행,

#    $ jp = 1순위: 로컬 하드의 해당 영상 재생. 2순위,  로컬 영상 없으면  인터넷 해당 마그넷 검색후 바로 토랭이 재생

#    $ jp (with $ jt)

#    $ jt 품번 = 로컬의 메타데이타 또는 인터넷에서 해당 품번 메테데이타 스크랩 후 포스터와 에고편을 띠운다. 해당품번 메테데이타 내용을 /tmp/품번.info 에 저장한다.

 

 

 vp jp jt mpsytctl 4개 실행 파일은 독립적으로도 사용가능하지만,, 서로 연계되어 있는 부분이 있다

소스 테스트는  ~/.zshrc,  jt, jp, mpsytctl, vp  순서로 고고


jt, jp 는 건전야동과 직접적으로 관련되어 있는 부분이라 일체의 설명을 생략. 설치할 패키지 목록 이름들도  생략


위 vp 파일에서  "jp $1" 라인 대신에 이전 게시글에서 소개햇던 "야동()" 커맨드를 사용하여도 된다.

function 야동 () {} 스크립트는 심의 규정상 패키지 목록, 설치방법 역시 생략한다. )

야동 () { mpv $(tbitsearch $(dmenu -p "🔎 Search Jav Torrent 😀 Watch: " -fn "hack" -sb "#783e57" -nb "#312e37" <&-) -s seeders -j | jq '.[] | .magnet' | head -n1 | sed -e 's/"//g') --terminal=no >/dev/null }




 


다. ~/.local/bin/jp 파일 :

로컬에 해당 파일이 있으면 재생하고, 없으면,  마그넷 검색 후, 토랭이로 재생한다. 추가 패키지 설치/추가 설정 없이는 절대 안돌아 간다. (청소년 보호 규정을 준수하자.). 알아서 수십개 패키지 설치하면 스크립트를 작동 시킬 수 있을 것 같다.

모든 스크립트들은 로직만 있다. 그냥은 돌아가지 않는다. 미성년 카피 사용 금지 (그런데 리눅스에서 영상 감상 실사용으로 스크립트 쓸 정도면 최소 30대 이상 아닌가?

#/usr/bin/zsh

[ $# -eq 1 ] || exit 1;

javlib="/mnt/jav"                                   # Jav 라이브러리 폴더
dvdid=$(echo $1 | tr '[a-z]' '[A-Z]')               # DVD ID (ex: ipzz-052 --> IPZZ-052)
poster="${javlib}/${dvdid}/${dvdid}-poster.jpg"     # Jav local 포스터
tmp_poster="/tmp/${dvdid}.jpg"                      # javdb 사이트에서 다운받을 포스터
sub_dir="/home/nietz/.config/mpv/sub"               # jav 자막들 들가 있는 폴더
video_list=""                                       # Jav 라이브러리의 해당 품번 영상 파일들
proxy_server="http://127.0.0.1:12345"               # green tunnel proxy

#sukebei="https://sukebei.nyaa.si/?page=rss&q=${dvdid}&c=0_0&f=0&u=offkab&s=seeders&o=desc"
sukebei="https://sukebei.nyaa.si/?page=rss&q=%22%5BFHD%5D+${dvdid}%22&c=0_0&f=0&u=offkab&s=seeders&o=desc"
hash=""
magnet_suffix="&tr=http%3A%2F%2Fsukebei.tracker.wf%3A8888%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce"
magnet=""

# 로컬 내장하드/로컬 외장 하드/리모트 구글드라이브에 해당 품번 영상파일 있는가 체크
exist_video ()
{
    fold_path="${javlib}/${dvdid}"      # 위 3군데 영상 파일들이 Jav 라이브러리 풂번 폴더에 사전에 미리 심벌릭 링크가 되어 있다
    video_list=$( [ -d ${fold_path} ] && find ${fold_path} -maxdepth 2 -type f -size +102400 -regextype posix-extended -regex '.*\.(mkv|mp4|wmv|m4v|avi|mov|mpg|mpeg|ts|m2ts|vob)$')
    [[ ${video_list} == ""  ]] && video_list=$( [ -d ${fold_path} ] && find ${fold_path} -maxdepth 1 -type l  -regextype posix-extended -regex '.*\.(mkv|mp4|wmv|m4v|avi|mov|mpg|mpeg|ts|m2ts|vob)$')
    if [[ ${video_list} == "" ]];then return 1; else return 0;fi
}

exist_local_poster ()
{
    if [[ -f ${poster} ]]; then return 0; else return 1; fi
}

get_poster ()
{
    jt ${dvdid} poster
}

show_poster ()
{
    if ! ( exist_local_poster ); then
        get_poster
        poster=${tmp_poster}
    fi
    poster_height=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1 ${poster} | cut -d "=" -f2)
    #if [ ${poster_height} -gt 1080 ];then mpv ${poster} --no-osc --screen=0 --fullscreen --fs-screen=current; else mpv ${poster}; fi
    #if [ ${poster_height} -ge 1080 ];then mpv ${poster} --no-config --screen=0 --fs-screen=current --keep-open --background=#312e39 --no-osc --geometry=x1016; else mpv ${poster}  --no-config --screen=0  --fs-screen=current --keep-open --background=#312e39; fi
    if [ ${poster_height} -ge 1080 ];then 
        mpv ${poster} --no-config --screen=0 --fs-screen=current --keep-open --background-color=#312e39 --no-osc --x11-name=float --geometry=x1080+1921+0;
        else mpv ${poster}  --no-config --screen=0  --fs-screen=current --no-osc --x11-name=float --geometry=+1921+0 --keep-open --background-color=#312e39;
    fi
}

get_sub ()
{
    _sub_files=""
    _sub_files=$(find ${sub_dir} -type f -regextype posix-extended -regex ".*\/.*${dvdid}.*(srt|ass|sub|sup)")
    if [[ ${_sub_files} != "" ]]; then 
        _sub_files=$(echo ${_sub_files} | sed -e 's/\s/:/g')
        echo ${_sub_files}; 
    fi
}

get_hash ()
{
    _hash=$(curl -s --proxy ${proxy_server} -L ${sukebei} | grep -i cdata | cut -d"|" -f5 | sed -e 's/^\s//g' | cut -d"]" -f1)
    if [[ ${_hash} != "" ]]; then echo ${_hash}; fi
}

main ()
{
    exist_video
    if [[ ${video_list} != "" ]];then
        show_poster &
        mpv ${video_list}
    else
        show_poster &
        hash=$(get_hash)
        if [[ ${hash} == "" ]];then
            echo "아직 토랭이 파일이 뜨지 않았거나 먼가 잘못됫다"
            exit
        else
            magnet="magnet:?xt=urn:btih:${hash}${magnet_suffix}"

            sub=$(get_sub)
            if [[ ${sub} == "" ]];then
                dunstify "─╼ mpv ${magnet} --screen=1 --fs-screen=current"
                mpv ${magnet} &
            else
                dunstify "─╼ mpv ${magnet} --screen=1 --fs-screen=current --sub-files=${sub}"
                mpv ${magnet} --screen=1 --fs-screen=current --sub-files=${sub} &
            fi
        fi
    fi
}

#sleep 0.1; jt ${1} trailer &
jt ${1} trailer &
sleep 1;main &

 

 


 

 

라. ~/.local/bin/jt  파일

    막혔다. 블락 된다. peer 연결이 바로 끊긴다. 추가 프록시와 레퍼러 설정해주어야 한다. 2라인 추가가 필요하다. (심의규정상 생략)

#/usr/bin/sh

############################################################################################################
# $ jt [뭎번] or $ jt [품번] mpris or $ jt "[품번] ㅁ노림로 shfslhf.확장자" or $ jt "hhd800@품번.확장자"
# $ jt juq-266 mpris
# "포스터경로|제목|배우명|출시일|재생시간|제작사|시리즈명"
# /tmp/JUQ-266.jpg|JUQ-266 - 昔俺の事が好きだった地味な教え子が、色気漂う巨乳人妻に進化していたので、性欲が尽き果てるまで生ハメしまくった…。 新井リマ (옛날부터 나를 좋아했던 순진한 학생이 거유의 섹시한 유부녀로 진화해서 성욕이 다할 때까지 범해버렸습니다... 아라이 리마)|아라이 리마(新井リマ/Rima Arai)|2023-04-25|120|Madonna|----
############################################################################################################


#[ $# -eq 1 ] || exit 1;

jt_option=$2
javlib="/mnt/jav"
#dvdid=$(echo $1 | tr '[a-z]' '[A-Z]')
dvdid=$(echo $1 | tr '[a-z]' '[A-Z]' | sed -e 's/V$/v/' | sed -E 's/^(.*@)?([a-zA-Z|tT28]+-[0-9]+[zZ]?[eE]?[vV]?)([-part|-pt]?[0-9]{1,2})?/\2/' | cut -d"." -f1)
## for mgsstage in javdb scrape : 170SENN-006 --> SENN-006 : 앞자리 숫자 코드 잘라야 한다.
#dvdid=$(echo "${dvdid}" | sed -E 's/([0-9]{2,5}+)?([a-zA-Z]+-[0-9]{2,5}+[zZ]?[eE]?.*)/\2/')

poster="${javlib}/${dvdid}/${dvdid}-poster.jpg"
poster_tmp="/tmp/${dvdid}.jpg"
nfo_file="${javlib}/${dvdid}/${dvdid}.nfo"
poster_limit_size=700
pflag="0"
nflag="0"

[ -f ${poster} ] || pflag="1";

# nfo 파일이 Local 컴에 존재하는 경우: 품번 정보는 nfo 파일에서 가져오고, 그렇지 않은 경우는 javdb 사이트에서 품번 정보를 가져온다.
[ -f ${nfo_file} ] && nflag="1";

trailerurl=""
dvd_id=${1}
dvd_id_source="/tmp/${dvd_id}.html"
dvd_id_info="/tmp/${dvd_id}.info"
javdb_id=""
javdb_id_page=""

urlencode() {
    # urlencode <string>
    local length="${#1}"
    for (( i = 0; i < length; i++ )); do
        local c="${1:i:1}"
        case $c in
            [a-zA-Z0-9.~_-]) printf "$c" ;;
            *) printf '%%%02X' "'$c" ;;
        esac
    done
}

urldecode() {
    # urldecode <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

get_javdb_id ()
{
    if [[ ${javdb_id} == "" ]]; then
        javdb_search="https://javdb.com/search"
        ## for mgsstage in javdb scrape : 170SENN-006 --> SENN-006 : 앞자리 숫자 코드 잘라야 한다.
        dvd_id=$(echo "${dvdid}" | sed -E 's/([0-9]{2,5}+)?([a-zA-Z]+-[0-9]{2,5}+[zZ]?[eE]?[vV]?.*)/\2/')
        javdb_id=$(curl -s -L "${javdb_search}?q=${dvd_id}" | \
                # 1th.
                #xargs | \
                #grep -oi ".*${dvd_id}" | \
                #sed -e 's/.*\(\/v\/\w\{4,6\}\).*/\1/' -e 's/\/v\///')
                # 2th
                grep -i "img loading" | \
                head -n1 | \
                cut -d'"' -f4 | \
                sed 's/.*\/\(.*\).jpg/\1/')
        javdb_id_page="https://javdb.com/v/${javdb_id}?locale=en"
        #dunstify "javdb id : ${javdb_id}" "javdb_id_page: ${javdb_id_page}"
    fi
}

get_javdb_source ()
{
    if [ ! -f ${dvd_id_source} ];then
        get_javdb_id
        curl -s -L "${javdb_id_page}" > ${dvd_id_source}
        #dunstify "Downloaded ${dvd_id_source}"
    fi
}

# javdb poster 저장
set_javdb_poster ()
{
    if [[ ${pflag} == "1" ]]; then
        get_javdb_source
        if [ ! -f ${poster_tmp} ]; then
            if [[ ${javdb_id} == "" ]]; then
                get_javdb_id
            fi
            poster="https://c0.jdbstatic.com/covers/$(echo ${javdb_id:0:2} | \
                    tr '[A-Z]' '[a-z]')/${javdb_id}.jpg";
            #echo ${poster}

            curl -s ${poster} -o ${poster_tmp}
            dunstify -i "${poster_tmp}" "fanart ─╼ wget ${poster} -O ${poster_tmp}"
            poster="${poster_tmp}"

            poster_height=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1 ${poster} | cut -d "=" -f2)
            if [ ${poster_height} -lt ${poster_limit_size} ]; then
                echo aaa $dvd_id
                #  캐리이안. 포이즌? 노모 이면 우측 커팅 하지 않는다.
                if ( echo ${dvd_id} | grep -E "^(.*@)?([0-9]{5,7}+)[-_]([0-9]{1,4}+)" >/dev/null ); then
                    :
                # msstage 품번이면 우측 커팅 하지 않고. poster 직다운한다
                # dvdid=390JAC-095  dvd_id=JAC-095
                elif (echo ${dvdid} | grep -E "^([0-9]{2,5}+)[a-zA-Z]+-[0-9]{2,5}+" >/dev/null) || ( echo ${dvd_id} | tr '[a-z]' '[A-Z]' | grep -E "^(.*@)?(SIMM|MAAN|RCON)-" >/dev/null ); then
                    echo --bbb $dvd_id
                    #pwsh -Command "Javinizer -Find ${dvd_id} -MgstageJa -Nfo" > /tmp/${dvd_id}.nfo     # ABC-123
                    pwsh -Command "Javinizer -Find ${dvdid} -MgstageJa -Nfo" > /tmp/${dvd_id}.nfo       # for 123ABC-123
                    poster=$(xidel -s /tmp/${dvd_id}.nfo --extract //thumb | sed -e 's/pb/pf/')
                    dunstify "poster ─╼ wget ${poster} -O ${poster_tmp}"
                    curl -s ${poster} -o ${poster_tmp}
                    /usr/bin/rm /tmp/${dvd_id}.nfo
                    :
                else
                    dunstify "─╼ convert ${poster} -quality 100 -gravity East -crop 47.25%x100+0+0 ${poster}"
                    convert ${poster} -quality 100 -gravity East -crop 47.25%x100+0+0 ${poster} || echo "Fail Cutting"
                    #waifu2x-converter-cpp -i ${poster} -o ${poster} --scale-ratio 1.35 --noise-level 2 --mode noise-scale -q 95 -p 0 || echo "Fail Upscaling"
                fi

                #dunstify "─╼ realesrgan-ncnn-vulkan -i ${poster} -o ${poster} -n RealESRGAN_x4plus -s4 && \\
#        convert -resize x1080 ${poster} ${poster}"
                #urxvt -fn "xft:Iosevka Nerd Font Mono:style=Regular:pixelsize=30" -e realesrgan-ncnn-vulkan -i ${poster} -o ${poster} -n RealESRGAN_x4plus -s4 && convert  -resize x1080 ${poster} ${poster}
                #poster_height=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1 ${poster} | cut -d "=" -f2)
                :
            fi
        else
            poster="${poster_tmp}"
        fi 
        #convert ${poster} -quality 100 -gravity East -crop 47.25%x100+0+0 ${poster} || echo "Fail Cutting"
    fi
}

get_trailer ()
{
    trailerurl=$(cat "${dvd_id_source}" | \
        grep 'type="video/' | \
        sed -e 's/.*src=\"\(.*.mp4\)\".*/\1/' | \
        cut -d '"' -f1 | sed -e 's/dm\./mhb\./' -e 's/_dm_/_dmb_/' | \
        sed -e 's/^\/\//http:\/\//')
        #echo ${trailerurl}
}

# 1순위: nfo <trailer> tag,   2순위: javdb 경유 dmm, mgstage trailer
set_trailer ()
{
    if [[ ${nflag} == "1" ]]; then
        trailerurl=`xidel -s "${nfo_file}" --extract //trailer`
        if [[ ${trailerurl} == "" ]]; then
            get_javdb_source
            get_trailer
        fi
    else
        get_javdb_source
        get_trailer
    fi
}

main ()
{
    # for mpv-mpris notify
    if [[ ${jt_option} == "mpris" ]]; then
        if [ ! -f ${dvd_id_info} ]; then 
            if [[ ${nflag} == "1" ]]; then
                #set -A arr_jp
                declare -a arr
                __cnt=0;
                __cnt=$(xidel -s ${nfo_file} --extract //actor/name | wc -l)
                for ((_i=1 ; _i <= $__cnt ; _i++))
                    do
                    arr[$_i]="$(xidel -s ${nfo_file} --extract //actor/name | head -"$_i" | tail -1)"
                    done
                __actor=""
                for ((_i=1 ; _i <= $__cnt ; _i++))
                do
                    if [[ $_i -eq 1 ]];then
                        __actor=${arr[$_i]};
                    else
                        __actor="${__actor}, ${arr[$_i]}";
                    fi
                done

                __cnt=0;
                __actor_jp=""
                #set -A arr_jp
                declare -a arr_jp
                #arr_jp_set=$(xidel -s ${nfo_file} --extract //actor/altname)
                #__cnt=$(echo ${arr_jp_set} | wc -l)
                __cnt=$(xidel -s ${nfo_file} --extract //actor/altname | wc -l)
                for ((_i=1 ; _i <= $__cnt ; _i++))
                do
                    arr_jp[$_i]="$(xidel -s ${nfo_file} --extract //actor/altname | head -"$_i" | tail -1)"
                    #arr_jp[$_i]=$(echo ${arr_jp_set} | head -$_i | tail -1)
                    if [[ $_i -eq 1 ]];then
                        __actor_jp=${arr_jp[$_i]};
                    else
                        __actor_jp="${__actor_jp}, ${arr_jp[$_i]}";
                    fi
                done

                dvd_id=""
                title=""
                originaltitle=""
                dvd_id=""
                runtime=""
                studio=""
                director=""
                releasedate=""
                premiered=""
                serises=""

                dvd_id=`xidel -s "$nfo_file" --extract //id`
                title=`xidel -s "$nfo_file" --extract //title`
                originaltitle=`xidel -s "$nfo_file" --extract //originaltitle`
                dvd_id=`xidel -s "$nfo_file" --extract //id`
                runtime=`xidel -s "$nfo_file" --extract //runtime`
                maker=`xidel -s "$nfo_file" --extract //studio`
                director=`xidel -s "$nfo_file" --extract //director`
                [[ $director == "" ]] && director="----"
                releaseddate=`xidel -s "$nfo_file" --extract //releasedate`
                premiered=`xidel -s "$nfo_file" --extract //premiered`
                [[ ${premiered} == "" ]] || releasedate=${premiered}
                series=`xidel -s "$nfo_file" --extract //set`
                if [[ $series == "" ]]; then
                    series="----"
                else
                    series="${series} ($(trans :ko -b "${series}"))" 
                fi

                main_title=""
                main_title_ja="${originaltitle}"
                main_title_en=$(trans ja:en -b "${main_title_ja}")
                main_title_ko=$(trans en:ko -b "${main_title_en}" | sed -e 's/u200b//g')
                jav_title="${dvd_id} - ${main_title_ja} (${main_title_ko})"
                if [[ ${__actor_jp} == "" ]] ;then
                    actress_name="$(trans :ko -b "${__actor}")(${__actor})"
                else
                    actress_name="$(trans :ko -b "${__actor_jp}")(${__actor_jp}/${__actor})"
                fi

                movie_info="${poster}|${jav_title}|${actress_name}|${releasedate}|${runtime}|${maker}|${series}"
                #echo ${movie_info}
                echo ${movie_info} > ${dvd_id_info}
                exit
            else
                set_javdb_poster
                jav_title="$(cat "${dvd_id_source}" | \
                    grep -i "current-title" | \
                    cut -d">" -f2 | cut -d"<" -f1)"
                jav_title="$(echo ${dvd_id} | tr '[a-z]' '[A-Z]') ${jav_title} (`trans :ko -b "${jav_title}" | sed -e 's/u200b//g'`)"
                #echo ${jav_title}
                actress_name=$(cat "${dvd_id_source}" | \
                    grep '<a href="/actors' | grep ♀ | sed -e 's/.*>\(.*\)<\/a>.*$/\1/')
                actress_name=$(echo ${actress_name} | sed -e 's/\s/, /g')
                actress_name="${actress_name} ($(trans :ko -b "${actress_name}"))"
                #echo ${actress_name}
                movie_panel_info=$(cat ${dvd_id_source} | xargs | grep -o "panel movie-panel-info.*</span> </div> <div class=panel-block>")
                released_date=$(echo ${movie_panel_info} | sed -e 's/.*\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\)<\/span>.*/\1/')
                #echo ${released_date}
                runtime=$(echo ${movie_panel_info} | sed -e 's/.*value>\([0-9]\{2,4\}\) minute(s).*/\1/')
                #echo ${runtime}
                maker=$(echo ${movie_panel_info} | grep -o "Maker:.*download>.*" | sed -e 's/.*download>\(.*\)<\/a>.*/\1/' | cut -d"<" -f1)
                #echo ${maker}
                series=$(echo ${movie_panel_info} | grep -o "Series:.*" | sed -e 's/.*\/series\/\w\{2,5\}>\(.*\)/\1/' | cut -d"<" -f1)
                if [[ $series == "" ]]; then
                    series="----"
                else
                    series="${series} ($(trans :ko -b "${series}"))"
                fi
                #echo ${series}

                movie_info="${poster}|${jav_title}|${actress_name}|${released_date}|${runtime}|${maker}|${series}"
                #echo ${movie_info}
                echo ${movie_info} > ${dvd_id_info}
                exit
            fi
        else
            exit
        fi
    elif [[ ${jt_option} == "poster" ]]; then
        set_javdb_poster
        exit
    elif [[ ${jt_option} == "trailer" ]]; then
        set_trailer
        dunstify "😀 Trailer ─╼ mpv --no-config --ontop --ontop-level=window --profile=dmmtrailer ${trailerurl}"
        #mpv --input-conf="/home/nietz/.config/mpv/input.conf" --ontop --ontop-level=desktop --pause=no --no-keepaspect --x11-name=tv --profile=tv ${trailerurl} --resume-playback --vf-clr &
        mpv --input-conf="/home/nietz/.config/mpv/input.conf" --ontop --ontop-level=desktop --pause=no --mute --no-keepaspect --x11-name=mpsyt --profile=mpsyt ${trailerurl} --resume-playback --vf-clr &
        exit
    fi

    set_javdb_poster
    set_trailer

    if [[ ${trailerurl} == "" ]];then 
        echo "trailer does'not exist"; 
    #else nohup mpv --x11-name=livetv ${trailerurl} >/dev/null ;
    else
        dunstify "😀 Trailer ─╼ mpv --no-config --ontop --ontop-level=window --profile=dmmtrailer ${trailerurl}"
        mpv --no-config --ontop --ontop-level=desktop --pause --no-keepaspect --x11-name=float --geometry=1024x576+2817+505 ${trailerurl} &
        #mpv ${trailerurl} &
        #mpv --no-config --ontop --ontop-level=desktop --pause --no-keepaspect  --profile=dmmtrailer ${trailerurl} &
        #nohup mpv --x11-name=livetv ${trailerurl} >/dev/null ;
    fi

    poster_height=$(ffprobe -v error -show_entries stream=height -of default=noprint_wrappers=1 ${poster} | cut -d "=" -f2)
    if [[ ${poster_height} -gt 1080 ]];then mpv ${poster} --screen=1 --fullscreen --fs-screen=current; else mpv ${poster}; fi

    # 아래 라인은 주석 처리 하지 않으면 https://sample.mgstage.com 사이트는
    # vpn 없이 접근할 수 없으므로 걸러진다. --> mgstage 링크는 영상 존재 유무 체크를 하지 않는다
    if [[ ${trailerurl} != "" ]];then
        #if ( ! echo "${trailerurl}" | grep "sample.mgstage.com"  > /dev/null ); then
        if ( ! echo "${trailerurl}" | grep "sample.mgstage.com"  > /dev/null ); then
            if ! (wget --no-proxy --spider -o/dev/null -v ${trailerurl}); then trailerurl="";fi   # 예고편 동영상이 서버에 존재 하지 않음
        fi
    fi

    #[[ ${pflag} == "1" ]] && rm ${poster}
    #rm ${dvd_id_source}
}

main

 

 


 

 

마. ~/.local/bin/mpsytctl 파일 

#!/bin/bash

########################################################################################
# Summary      : mpsyt control for wm keybinding, notification, open browser, download track
#               mpris app control & notification - (experimental)
#              
# Dependencies : mpv, mpv-mpris, mpsyt, dunst, playerctl, youtube-dl, paplay,(mopidy,spotify, gpmdp, ...)
########################################################################################

MPRIS_APP="mpv"     # "mpv" "mps-youtube", "mopidy", "spotify", "google-play-music-desktop-player", ...
#MPRIS_NAME="$(playerctl -l | grep -i $MPRIS_APP)"
MPRIS_NAME="$(playerctl -l | grep -i $MPRIS_APP | tail -n1)"
#MPRIS_NAME="mpv"

pflag=1             # exit flag
tflag=0             # tv channel(=tvheadend playlist) flag
youtube_flag=0      # youtube flag
jav_flag=0          # jav 야동 flag
javlib="/mnt/jav"   # jav 야동 라이브러리 폴더

urlencode() {
    # urlencode <string>
    local length="${#1}"
    for (( i = 0; i < length; i++ )); do
        local c="${1:i:1}"
        case $c in
            [a-zA-Z0-9.~_-]) printf "$c" ;;
            *) printf '%%%02X' "'$c" ;;
        esac
    done
}

urldecode() {
    # urldecode <string>
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

#if [ "$MPRIS_NAME" = "" ]
if ( ! playerctl -l | grep mpv >/dev/null )
then
    echo "$MPRIS_APP is not running..."
    exit 0
fi

MPRIS_STATUS="$(playerctl -p $MPRIS_NAME status)"
if [ "$MPRIS_STATUS" = "Stopped" ]
then
    echo "$MKPRIS_APP is Stoped"
    #exit 0
fi

xesam_url="$(playerctl -p $MPRIS_NAME metadata xesam:url)"
file_path=$(urldecode ${xesam_url})
#echo [File path] : "'$file_path'";
file_name=$(basename "${file_path}")
## for mgsstage in javdb scrape : 170SENN-006 --> SENN-006 : 앞자리 숫자 코드 잘라야 한다.
#file_name=$(echo "${file_name}" | sed -E 's/([0-9]{2,5}+)?([a-zA-Z]+-[0-9]{2,5}+[zZ]?[eE]?.*)/\2/')
#echo [File Name] : "'$file_name'";

#if ( echo ${xesam_url} | grep -E ".*\.(webp|png|jpg|jpeg|gif|webm|mp3|flac)" >/dev/null ) ;then exit; fi
if ( echo ${xesam_url} | grep -E ".*\.(webp|png|jpg|jpeg|gif|webm|mp3|flac)" >/dev/null ) ;then exit; fi
if ( echo ${xesam_url} | grep -i channelid >/dev/null ) ;then 
    #echo ---0. TV 채널
    tflag=1; 
fi
if ( echo ${xesam_url} | grep -i "/jav/" >/dev/null ) ;then 
    #echo ---1. 야동 파일   # 야동 라이브러리 폴더 경로 아래에 있는 영상들
    jav_flag="1"; pflag=0; 
fi
#if ( echo ${file_name} | grep -E "^(.*@)?[a-zA-Z|tT28]+-[0-9]+[zZ]?[eE]?([-part|-pt]?[0-9]{1,2})?" >/dev/null ); then
if ( echo ${file_name} | grep -E "^(.*@)?([0-9]{2,5}+)?[a-zA-Z|tT28]+-[0-9]{2,5}+[zZ]?[eE]?[vV]?([-part|-pt]+[0-9]{1,2}?)?( - [BVPP|Leaked|AI|4K|Chinese|60FPS|Upscale].*)?.(mkv|mp4|wmv|m4v|avi|mov|mpg|ts|m2ts|vob)$" >/dev/null ); then
    #echo ---1. 야동 파일
    jav_flag=1; pflag=0
elif ( echo ${file_path} | grep -E "dmm.co.jp|dmm.com|mgstage.com|prestige-av.com|cloudfront.net|faleno.net" >/dev/null ); then
    #echo ---1. 야동 예고편 파일
    jav_flag=0; pflag=1     #jav_flag=1; pflag=0
elif ( echo ${xesam_url} | grep -E "www.youtube|youtu.be|watch?" ); then
    #echo ---2. 유부트 파일
    youtube_flag=1; pflag=0
elif ( echo ${file_name} | grep -E "^(.*@)?([0-9]{5,7}+)-([0-9]{1,4}+)([-part|-pt]+[0-9]{1,2}?)?( - [BVPP|Leaked|AI|4K|Chinese|60FPS|Upscale].*)?.(mkv|mp4|wmv|m4v|avi|mov|mpg|ts|m2ts|vob)$" >/dev/null ); then
        jav_flag="1"; pflag=0; 
        #echo ---3-1 야동 노모 carib 파일
elif ( echo ${file_name} | grep -E "^(.*@)?([0-9]{5,7}+)_([0-9]{1,4}+)([-part|-pt]+[0-9]{1,2}?)?( - [BVPP|Leaked|AI|4K|Chinese|60FPS|Upscale].*)?.(mkv|mp4|wmv|m4v|avi|mov|mpg|ts|m2ts|vob)$" >/dev/null ); then
        jav_flag="1"; pflag=0; 
        #echo "---3-2 야동 노모 1pondo 파일:"
elif ( echo ${file_name} | grep -E "^(.*@)?([FC2PPV|FC2-PPV)-([0-9]{4,7}+)([-part|-pt]+[0-9]{1,2}?)?( - [BVPP|Leaked|AI|4K|Chinese|60FPS|Upscale].*)?.(mkv|mp4|wmv|m4v|avi|mov|mpg|ts|m2ts|vob)$" >/dev/null ); then
        jav_flag="1"; pflag=0; 
        #echo "---3-3 야동 FC2-PPV-xxxxxx 파일"        # for javdb scrape FC2PPV- 로 하이픈 붙여야 한다
else
    jav_flag=0; pflag=1
    #echo ---4. 그냥 파일
fi

if [ ${tflag} == 1 ] || [ ${pflag} == 1 ];then exit;fi

MPRIS_ARTURL="$(playerctl -p $MPRIS_NAME metadata mpris:artUrl)"
MPRIS_ARTURL_PREFIX=""
if [ "$MPRIS_APP" = "mopidy" ]
then
    if ( echo "$MPRIS_ARTURL" | grep -qi "/images/" )
    then
        echo "local track"      # possible download coverart
        MPRIS_ARTURL_PREFIX="http://127.0.0.1:6680"
        MPRIS_ARTURL=$MPRIS_ARTURL_PREFIX$MPRIS_ARTURL
        echo $MPRIS_ARTURL
    elif ( echo "$MPRIS_URL" | grep -qi "file://" )
    then
        # impossible download coverart . file path ??
        echo "file:// is local file url string. $MPRIS_URL :  How to convert local file path string?"
    else
        echo "Streaming track: pass MPRIS http url"
    fi
fi

MPRIS_TITLE="$(playerctl -p $MPRIS_NAME metadata xesam:title)"
MPRIS_TRACK_ID="$(playerctl -p $MPRIS_NAME metadata mpris:trackid)"

#if [ ! "$MPRIS_APP" = "mps-youtube" ]
if [ ! "$MPRIS_APP" = "mpv" ]
then
    MPRIS_ALBUM="$(playerctl -p $MPRIS_NAME metadata xesam:album)"
    MPRIS_ALBUM_ARITIS="$(playerctl -p $MPRIS_NAME metadata xesam:albumArtist)"
    MPRIS_ARTIST="$(playerctl -p $MPRIS_NAME metadata xesam:artist)"
    MPRIS_URL="$(playerctl -p $MPRIS_NAME metadata xesam:url)"
    #echo $MPRIS_URL
else
    #YOUTUBE_TRACK_ID="$(playerctl -p $MPRIS_NAME metadata mpris:trackid | sed 's/\/CurrentPlaylist\/ytid\///' | sed "s/'//g" )"
    YOUTUBE_TRACK_ID="$(echo $MPRIS_TRACK_ID | \
        sed 's/\/CurrentPlaylist\/ytid\///' | sed "s/'//g")"
    #YOUTUBE_URL_PREFIX="https://www.youtube.com/watch?v="
    #YOUTUBE_URL=$YOUTUBE_URL_PREFIX$YOUTUBE_TRACK_ID
    MPRIS_ARTIST="$(playerctl -p $MPRIS_NAME metadata xesam:artist)"
    MPRIS_URL="$(playerctl -p $MPRIS_NAME metadata xesam:url)"
    YOUTUBE_URL=$MPRIS_URL
    MPRIS_TRACK_ID=$(echo $MPRIS_TRACK_ID | sed -e 's/\///g' -e "s/'//g")
    i=`expr $MPRIS_TRACK_ID + 1`
    YOUTUBE_TRACK_ID=$i; YOUTUBE_TRACK_ID="$YOUTUBE_TRACK_ID / -"
    #echo ----aaa----$MPRIS_TRACK_ID-----aaa---

fi

MPRIS_VOLUME="$(playerctl -p $MPRIS_NAME volume)"
MPRIS_VOLUME_LEVEL=$(echo "$MPRIS_VOLUME" | awk '{print ($A)*100}')%

MPRIS_POS="$(playerctl -p $MPRIS_NAME position)"
MPRIS_LENGTH="$(playerctl -p $MPRIS_NAME metadata mpris:length)"
TOTAL_LENGTH=$(echo "$MPRIS_LENGTH" | awk '{printf "%.3f", ($MPRIS_LENGTH)/1000000}')

OLD_COVERART_URL=$(cat /home/nietz/.xmonad/.mpsyt_coverart)
NEW_COVERART_URL=$MPRIS_ARTURL

DUNST_APPNAME="Mpsyt"         # ~/.config/dunst/dunstrc profile name
if [ "$MPRIS_NAME" == "mopidy" ]
then
    DUNST_APPNAME="Mopidy"
    #echo $DUNST_APPNAME
elif [ "$MPRIS_NAME" = "spotify" ]
then
    DUNST_APPNAME="Spotify"
elif [ "$MPRIS_NAME" = "google-play-music-desktop-player" ]
then
    DUNST_APPNAME="google-play-music-desktop-player"
else
    DUNST_APPNAME="Mpsyt"
    #DUNST_APPNAME="mpv_mpris Youtube"
fi

ICON="/tmp/mpsyt_coverart.jpg"
DUNST_POS_STRING="$MPRIS_POS / $TOTAL_LENGTH (sec)"

CMD=""
#YOUTUBE_DL_FORMAT="bestvideo[height<=?2160][fps<=?60][protocol!=http_dash_segments][ext=mp4]+(bestaudio[acodec=opus]/bestaudio[acodec=vorbis]/bestaudio[acodec=aac]/bestaudio/bestaudio[ext=webm])/bestvideo+bestaudio/best[ext=mp4]/best"
YOUTUBE_DL_FORMAT="(bestvideo[height<=?1080][fps<=?60][vcodec=av01.0.09M.08]/bestvideo[height<=?1080][fps<=?30][vcodec=av01.0.08M.08]/bestvideo[height<=?1080][fps<=?60][ext=mp4])+(bestaudio[acodec=opus]/bestaudio[acodec=vorbis]/bestaudio[acodec=aac]/bestaudio/bestaudio[ext=webm])/bestvideo+bestaudio/best[ext=mp4]/best"
DOWNLOAD_DIR="/home/nietz/Videos/"      # 반드시 절대 경로로 사용
YOUTUBE_DL_OUTPUT_TEMPLATE="${DOWNLOAD_DIR}%(title)s.%(ext)s"
YOUTUBE_DL_OPTION=" -f $YOUTUBE_DL_FORMAT -o $YOUTUBE_DL_OUTPUT_TEMPLATE --all-subs --embed-subs --add-metadata --metadata-from-title '(?P<artist>.+?) - (?P<title>.+)' "
YOUTUBE_DL_CMD="yt-dlp $YOUTUBE_DL_OPTION $YOUTUBE_URL"
#echo $YOUTUBE_DL_CMD
#$YOUTUBE_DL_CMD
#echo


get_coverart () {
    if [ ! "$NEW_COVERART_URL" = "$OLD_COVERART_URL" ]
    then
        wget "$MPRIS_ARTURL" -O /tmp/mpsyt_coverart.jpg
    fi
    if [[ $jav_flag == "1" ]]; then
        local xesam_title="$(playerctl -p $MPRIS_NAME metadata xesam:title)"
        ###  @runbkk @maxjav 릴은 미디어 인포에 Movie name 메타데이타 마킹 되어 잇음
        xesam_title=${file_name}
        #jav_dvdid=$(echo ${xesam_title%.*} | sed -e 's/-part.//' -e 's/_/-/g' | cut -d@ -f2 | cut -d" " -f1)
        jav_dvdid=$(echo ${xesam_title%.*} | sed -e 's/-part.//' | cut -d@ -f2 | cut -d" " -f1)
        #jav_dvdid=$(echo ${jav_dvdid} | sed -E 's/([0-9]{2,5}+)([a-zA-Z]+-[0-9]{2,5}+[zZ]?[eE]?.*)/\2/' | sed -e 's/\([a-zA-Z|tT28]*-[0-9]*[zZ]\?[eE]\?\)\([-pt]\?[0-9]\{1,2\}\?\).*/\1/')
        jav_dvdid=$(echo ${jav_dvdid} | sed -E 's/([0-9]{2,5}+[a-zA-Z]+-[0-9]{2,5}+[zZ]?[eE]?[vV]?.*)/\1/' | sed -e 's/\([a-zA-Z|tT28]*-[0-9]*[zZ]\?[eE]\?[vV]\?\)\([-pt]\?[0-9]\{1,2\}\?\).*/\1/')
        jav_cover="${javlib}/${jav_dvdid}/${jav_dvdid}-poster.jpg"
        jav_info="/tmp/$(echo ${jav_dvdid} | tr '[A-Z]' '[a-z]').info"
        #echo ${jav_dvdid}
        if [ ! -f ${jav_cover} ] ; then
            #jav_info=`jt ${jav_dvdid} mpris`
            jav_cover="/tmp/${jav_dvdid}.jpg"
        fi

        #echo cccc----${jav_dvdid}; 
        #jt $(echo ${jav_dvdid} | tr '[A-Z]' '[a-z]') mpris
        jt "$(echo ${jav_dvdid} | tr '[A-Z]' '[a-z]' | cut -d" " -f1)" mpris
        jav_cover=$(cat ${jav_info} | cut -d"|" -f1)
        #echo ${jav_cover}
        jav_title=$(cat ${jav_info} | cut -d"|" -f2)
        #echo ${jav_title}
        jav_actress=$(cat ${jav_info} | cut -d"|" -f3)
        #echo ${jav_actress}
        jav_released_date=$(cat ${jav_info} | cut -d"|" -f4)
        #echo ${jav_released_date}
        jav_runtime=$(cat ${jav_info} | cut -d"|" -f5)
        #echo ${jav_runtime}
        jav_maker=$(cat ${jav_info} | cut -d"|" -f6)
        #echo ${jav_maker}
        jav_series=$(cat ${jav_info} | cut -d"|" -f7)
        #echo ${jav_series}

        MPRIS_URL=$(urldecode ${MPRIS_URL})

        #echo ---aaa----${jav_dvdid}
        #echo ---bbb----${jav_cover}
        [ -f ${jav_cover} ] || ffmpeg -ss 40 -i "${xesam_url}" -frames:v 1 -vf scale=300:-1 -codec png -y ${jav_cover}
    fi
}

put_coverart () {
    echo $NEW_COVERART_URL > /home/nietz/.xmonad/.mpsyt_coverart
}


get_dunstPID () {
    ID=$(cat /home/nietz/.xmonad/.dunst_mpsyt)
}


update_trackinfo () {
    MPRIS_ARTURL="$(playerctl -p $MPRIS_NAME metadata mpris:artUrl)"
    if [ "$MPRIS_APP" = "mopidy" ]
    then
        if ( echo "$MPRIS_ARTURL" | grep -qi "/images/" )
        then
            echo "local track"      # possible download coverart
            MPRIS_ARTURL_PREFIX="http://127.0.0.1:6680"
            MPRIS_ARTURL=$MPRIS_ARTURL_PREFIX$MPRIS_ARTURL
            echo $MPRIS_ARTURL
        elif ( echo "$MPRIS_URL" | grep -qi "file://" )
        then
            # impossible download coverart . file path ??
            echo "file:// is local file url string. $MPRIS_URL :  How to convert local file path string?"
        else
            echo "Streamming track: pass MPRIS http url"
        fi
    fi
    MPRIS_TITLE="$(playerctl -p $MPRIS_NAME metadata xesam:title)"
    MPRIS_TRACK_ID="$(playerctl -p $MPRIS_NAME metadata mpris:trackid)"

    #if [ "$MPRIS_APP" = "mps-youtube" ]
    if [ "$MPRIS_APP" = "mpv" ]
    then
        YOUTUBE_TRACK_ID="$(echo $MPRIS_TRACK_ID | \
            sed 's/\/CurrentPlaylist\/ytid\///' | sed "s/'//g")"
        YOUTUBE_URL_PREFIX="https://www.youtube.com/watch?v="
        YOUTUBE_URL=$YOUTUBE_URL_PREFIX$YOUTUBE_TRACK_ID
    else
        MPRIS_ALBUM="$(playerctl -p $MPRIS_NAME metadata xesam:album)"
        MPRIS_ALBUM_ARITIS="$(playerctl -p $MPRIS_NAME metadata xesam:albumArtist)"
        MPRIS_ARTIST="$(playerctl -p $MPRIS_NAME metadata xesam:artist)"
        MPRIS_URL="$(playerctl -p $MPRIS_NAME metadata xesam:url)"
    fi

    MPRIS_VOLUME="$(playerctl -p $MPRIS_NAME volume)"
    MPRIS_VOLUME_LEVEL=$(echo "$MPRIS_VOLUME" | awk '{print ($A)*100}')%

    MPRIS_POS="$(playerctl -p $MPRIS_NAME position)"
    MPRIS_LENGTH="$(playerctl -p $MPRIS_NAME metadata mpris:length)"
    TOTAL_LENGTH=$(echo "$MPRIS_LENGTH" | awk '{printf "%.3f", ($MPRIS_LENGTH)/1000000}')

    OLD_COVERART_URL=$(cat /home/nietz/.xmonad/.mpsyt_coverart)
    NEW_COVERART_URL=$MPRIS_ARTURL

    DUNST_POS_STRING="$MPRIS_POS / $TOTAL_LENGTH (sec)"
}


get_trackinfo () {
    #update_trackinfo
    get_coverart
    get_dunstPID

    #if [ "$MPRIS_APP" = "mpv" ]
    if [ "$MPRIS_APP" = "mpv" ]
    then
        echo $YOUTUBE_URL | xclip -selection primary        # "primary", or "clipbard"
        #echo "$YOUTUBE_URL String is stored to clipboard"
        #echo

        if [[ ${jav_flag} == "1" ]];then
            if [ $ID -gt "0" ]
            then
#| Track # : $YOUTUBE_TRACK_ID | Volume : $MPRIS_VOLUME_LEVEL | Position : $DUNST_POS_STRING">/home/nietz/.xmonad/.dunst_mpsyt
# 배우: 涼森 れむ (스즈므리 레무)" " <u>출시일</u>: <b>2023年04月28日</b> | <u>재생시간</u>: <b>125分</b> | <u>시리즈</u>: どちゃくそエロい最高級ギャルと中出ししまくった、あの夜。(<b>어리석은 최고급 걸과 질 내 사정 버린 그 밤.</b>)
                #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
#                dunstify -p -r $ID --appname=Jav -i $jav_cover "ABW-348 - どちゃくそエロい最高級ギャルと中出ししまくった、あの夜。 1 (어리석은 최고급 걸과 질 내 사정 버린 그 밤. 1)
# 배우: 涼森 れむ (스즈므리 레무)" " 출시일: 2023年04月28日 | 재생시간: 125分 | 시리즈: どちゃくそエロい最高級ギャルと中出ししまくった、あの夜。(어리석은 최고급 걸과 질 내 사정 버린 그 밤.)
                dunstify -p -r $ID --appname=Jav -i ${jav_cover} "${jav_title}

  배우: ${jav_actress} " "  출시일: ${jav_released_date} | 재생시간: ${jav_runtime} minute(s) | 제작사: ${jav_maker} | 시리즈: ${jav_series}
   <u>$MPRIS_URL</u>">/home/nietz/.xmonad/.dunst_mpsyt
            else
                #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS " "$MPRIS_ARTIST | Track # : $YOUTUBE_TRACK_ID
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
                 Command :  $SUMMARY_PREFIX  $1" >/home/nietz/.xmonad/.dunst_mpsyt
            fi
        else
            if [ $ID -gt "0" ]
            then
                #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING" "$MPRIS_ARTIST | Track: $YOUTUBE_TRACK_ID
                 Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
            else
                #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING" "$MPRIS_ARTIST | Track: $YOUTUBE_TRACK_ID
                 Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
            fi
        fi

    else
        echo $MPRIS_URL | xclip -selection primary
        #echo "$MPRIS_URL String is stored to clipboard"

        if [ $ID -gt "0" ]
        then
            #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
            dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
             Status : $MPRIS_STATUS
             " "$MPRIS_ARTIST - $MPRIS_ALBUM" >/home/nietz/.xmonad/.dunst_mpsyt
#Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
        else
            #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
            dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
             Status : $MPRIS_STATUS
             " "$MPRIS_ARTIST - $MPRIS_ALBUM" >/home/nietz/.xmonad/.dunst_mpsyt
#Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
        fi
    fi

    put_coverart
}


download_track () {
    get_dunstPID

    if [ $ID -gt "0" ]
    then
        #dunstify -p -r $ID --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "<b>$MPRIS_TITLE</b>
        dunstify -p -r $ID --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "$MPRIS_TITLE
         " "Downloading started
         Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
    else
        #dunstify -p --appname=$DUNST_APPNAME --urgency=low  --timeout=2000 -i $ICON "<b>$MPRIS_TITLE</b>
        dunstify -p --appname=$DUNST_APPNAME --urgency=low  --timeout=2000 -i $ICON "$MPRIS_TITLE
         " "Downloading started
         Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
    fi

    $YOUTUBE_DL_CMD
    paplay /usr/share/sounds/freedesktop/stereo/complete.oga

    if [ $ID -gt "0" ]
    then
        #dunstify -p -r $ID --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "<b>$MPRIS_TITLE</b>
        dunstify -p -r $ID --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "$MPRIS_TITLE
         " "finished downloading 
         Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
    else
        #dunstify -p --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "<b>$MPRIS_TITLE</b>
        dunstify -p --appname=$DUNST_APPNAME --urgency=low --timeout=2000 -i $ICON "$MPRIS_TITLE
         " "finished downloading 
         Youtube URL : $YOUTUBE_URL" >/home/nietz/.xmonad/.dunst_mpsyt
    fi

    sleep 1
    MPRIS_TITLE=$(echo $MPRIS_TITLE | sed -e 's/\//_/g')
    playerctl -p $MPRIS_NAME pause
    mpv "${DOWNLOAD_DIR}${MPRIS_TITLE}.mkv" &
}


get_cmdinfo() {
    $1
    echo $1
    echo
    #sleep 1

    update_trackinfo
    get_coverart
    get_dunstPID

    local SUMMARY_PREFIX

    # start of Summary iconFont prefix
    if [ "$2" == "next" ]
    then
        SUMMARY_PREFIX=""

    elif [ "$2" == "previous" ]
    then
        SUMMARY_PREFIX=""

    elif [ "$2" == "play-pause" ]
    then
        if [ "$MPRIS_STATUS" == "Playing" ]
        then
            SUMMARY_PREFIX=""
            MPRIS_STATUS="Paused"
        else
            SUMMARY_PREFIX=""
            MPRIS_STATUS="Playing"
        fi

    elif [ "$2" == "volumeUp" ]
    then
        SUMMARY_PREFIX=" "

    elif [ "$2" == "volumeDown" ]
    then
        SUMMARY_PREFIX=" "

    elif [ "$2" == "volume10%" ]
    then
        SUMMARY_PREFIX=" 10% :"

    elif [ "$2" == "volume20%" ]
    then
        SUMMARY_PREFIX=" 20% :"

    elif [ "$2" == "volume20%" ]
    then
        SUMMARY_PREFIX=" 20% :"

    elif [ "$2" == "volume30%" ]
    then
        SUMMARY_PREFIX=" 30% :"

    elif [ "$2" == "volume40%" ]
    then
        SUMMARY_PREFIX=" 40% :"

    elif [ "$2" == "volume50%" ]
    then
        SUMMARY_PREFIX=" 50% :"

    elif [ "$2" == "volume60%" ]
    then
        SUMMARY_PREFIX=" 60% :"

    elif [ "$2" == "volume70%" ]
    then
        SUMMARY_PREFIX=" 70% :"

    elif [ "$2" == "volume80%" ]
    then
        SUMMARY_PREFIX=" 80% :"

    elif [ "$2" == "volume90%" ]
    then
        SUMMARY_PREFIX=" 90% :"

    elif [ "$2" == "volume100%" ]
    then
        SUMMARY_PREFIX=" 100% :"

    elif [ "$2" == "mute" ]
    then
        SUMMARY_PREFIX=" mute :"

    elif [ "$2" == "forward" ]
    then
        SUMMARY_PREFIX=" 1 sec :"

    elif [ "$2" == "backward" ]
    then
        SUMMARY_PREFIX=" 1 sec :"

    elif [ "$2" == "forward10" ]
    then
        SUMMARY_PREFIX=" 10 sec :"

    elif [ "$2" == "backward10" ]
    then
        SUMMARY_PREFIX=" 10 sec :"


    else
        SUMMARY_PREFIX=""
    fi
    # end of Summary iconFont prefix


    #if [ "$MPRIS_APP" = "mps-youtube" ]
    if [ "$MPRIS_APP" = "mpv" ]
    then
        echo $YOUTUBE_URL | xclip -selection primary        # "primary", or "clipbard"
        #echo "$YOUTUBE_URL String is stored to clipboard"
        #echo

        MPRIS_TRACK_ID=$(echo $MPRIS_TRACK_ID | sed -e 's/\///g' -e "s/'//g")
        i=`expr $MPRIS_TRACK_ID + 1`
        YOUTUBE_TRACK_ID=$i; YOUTUBE_TRACK_ID="$YOUTUBE_TRACK_ID / -"

        if [[ ${jav_flag} == "1" ]];then
            if [ $ID -gt "0" ]
            then
                #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p -r $ID --appname=$DUNST_APPNAME -i $jav_cover "$MPRIS_TITLE
                 Status : $MPRIS_STATUS " "$MPRIS_ARTIST | Track # : $YOUTUBE_TRACK_ID
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
                 Command :  $SUMMARY_PREFIX  $1" >/home/nietz/.xmonad/.dunst_mpsyt
            else
                #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS " "$MPRIS_ARTIST | Track # : $YOUTUBE_TRACK_ID
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
                 Command :  $SUMMARY_PREFIX  $1" >/home/nietz/.xmonad/.dunst_mpsyt
            fi
        else
            if [ $ID -gt "0" ]
            then
                #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS " "$MPRIS_ARTIST | Track # : $YOUTUBE_TRACK_ID
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
                 Command :  $SUMMARY_PREFIX  $1" >/home/nietz/.xmonad/.dunst_mpsyt
            else
                #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
                dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
                 Status : $MPRIS_STATUS " "$MPRIS_ARTIST | Track # : $YOUTUBE_TRACK_ID
                 Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
                 Command :  $SUMMARY_PREFIX  $1" >/home/nietz/.xmonad/.dunst_mpsyt
            fi
        fi

    else
        echo $MPRIS_URL | xclip -selection primary
        #echo "$MPRIS_URL String is stored to clipboard"

        if [ $ID -gt "0" ]
        then
            #dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
            dunstify -p -r $ID --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
             Status : $MPRIS_STATUS " "$MPRIS_ARTIST - $MPRIS_ALBUM
             Command :  $SUMMARY_PREFIX $1" >/home/nietz/.xmonad/.dunst_mpsyt
#Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
        else
            #dunstify -p --appname=$DUNST_APPNAME -i $ICON "<b>$MPRIS_TITLE</b>
            dunstify -p --appname=$DUNST_APPNAME -i $ICON "$MPRIS_TITLE
             Status : $MPRIS_STATUS " "$MPRIS_ARTIST - $MPRIS_ALBUM
             Command :  $SUMMARY_PREFIX $1" >/home/nietz/.xmonad/.dunst_mpsyt
#Volume : $MPRIS_VOLUME_LEVEL ,  Position : $DUNST_POS_STRING
        fi
    fi


    put_coverart
}

if [ "$1" == "trackinfo" ]
then
    get_trackinfo

elif [ "$1" == "openURL" ]
then
    qutebrowser --backend webengine $YOUTUBE_URL &
    playerctl -p $MPRIS_NAME pause
    sleep 2
    get_trackinfo

elif [ "$1" == "download" ]
then
    download_track

elif [ "$1" == "next" ]
then
    CMD="playerctl -p $MPRIS_NAME next"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "previous" ]
then
    CMD="playerctl -p $MPRIS_NAME previous"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "play-pause" ]
then
    CMD="playerctl -p $MPRIS_NAME play-pause"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volumeUp" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.1+"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volumeDown" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.1-"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume10%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.1"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume20%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.2"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume30%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.3"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume40%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.4"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume50%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.5"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume60%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.6"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume70%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.7"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume80%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.8"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume90%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.9"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "volume100%" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 1.0"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "mute" ]
then
    CMD="playerctl -p $MPRIS_NAME volume 0.0"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "forward" ]
then
    CMD="playerctl -p $MPRIS_NAME position 1+"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "backward" ]
then
    CMD="playerctl -p $MPRIS_NAME position 1-"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "forward10" ]
then
    CMD="playerctl -p $MPRIS_NAME position 10+"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "backward10" ]
then
    CMD="playerctl -p $MPRIS_NAME position 10-"
    get_cmdinfo "$CMD" $1

elif [ "$1" == "backward10" ]
then
    CMD="playerctl -p $MPRIS_NAME position 10-"
    get_cmdinfo "$CMD" $1


else
    #echo "- room 503 out -"
    get_trackinfo
fi



  1. mpv instance 가 실행될 때, 실행되는 스크립트 :  mpsytctl 이 자동 실행되게 mpv 에 연결 작업을 해주어야한다.


  2. mpv 재생/컨트롤 단축키를 위한 스크립트 (mpv 윈도우 창에 포커스가 없는 상태에서 단축키로 재생 제어를 애용하는 경우)

 

  3.  ~/.config/mpv/input.conf 파일

ctrl+i run "/bin/sh" "-c" "jt ${filename/no-ext} mpris"; run "/bin/sh" "-c" "mpsytctl"

       
        건전 영상을 하나 띠운 뒤.  mpv 창 윈도우 포커스 상태에서  "ctrl + i" 키를 누르면 메타 정보 noti 알림을 수동으로 띠울 수 있다.

 

워낙 오래전에 유투브 재생용으로 만들었던걸 jav noti 기능을 추가로 넣었다 .  스크립트 소스가 중구난방에 난잡하고 어질어질 합니다. 더럽고 지저분하다.  아 고칠 능력이 안된다.

 

😭  주목적과 마인드 : 에라 모르겠다. "돌아가면 장땡이다"  😭

 


 

바. ~/.config/mpv/scripts/notify.lua 파일.  

function print_debug(s)
    -- print("DEBUG: " .. s) -- comment out for no debug info
    return true
end

function notify_current_track()
	command = ("~/.local/bin/mpsytctl")
	print_debug("command: " .. command)
	os.execute(command)
end

-- insert main() here
mp.register_event("file-loaded", notify_current_track)

 


 

사. ~/.local/bin/SVP4_Portable_Install.sh 파일 : SVP4 GUI 포터블 설치 (CLI 는 자동 포함된다)

      예전에 짜깁기 해서 만든거라. 지금은 soucrce 버젼이 올라갔을 지도??.  리눅스가 좋은 점.. SVP4 리눅스 버젼이 처음부터 지끔까지 언제나 공짜.  SVP4 초창기 때는 리눅스에서  SVP4 설치하는 것 조차 최대 난제였구,  1관문 패스한 후, 제대로 돌아가게 하는 것 조차도 헬게이트. 어느 순간부턴 다 잘되는 듯.

 

그래서.. 혹 실행에 문제가 있을 경우, GUI 다 제거하고,,  SVP4 모듈만 다 빼와서, 바로 vapoursynth 스크립트로 cli로 돌리면 에러없이 잘 돌아간다)

 

#!/usr/bin/sh

# Installing Directory --> Edit
srcdir="/mnt/Media/SVP4"

# Latest SVP4 Source : https://www.svp-team.com/files/svp4-latest.php?linux
svp4source="https://www.svp-team.com/files/svp4-linux.4.5.210-2.tar.bz2"

# Download SVP4 source
wget -P ${srcdir} ${svp4source}

# Extract "svp4-linux.4.5.210-2.tar.bz2" ---> output : "svp4-linux-64.run"
7z x "${srcdir}/$(basename ${svp4source})" -so | 7z x -aoa -si -ttar -o"${srcdir}"

# Portable Install : "svp4-linux-64.run"
echo "Finding 7z archives in installer..."
LANG=C grep --only-matching --byte-offset --binary --text  $'7z\xBC\xAF\x27\x1C' "${srcdir}/svp4-linux-64.run" |
    cut -f1 -d: |
    while read ofs; do dd if="$srcdir/svp4-linux-64.run" bs=1M iflag=skip_bytes status=none skip=$ofs of="$srcdir/bin-$ofs.7z"; done

echo "Extracting 7z archives from installer..."
for f in "${srcdir}/"*.7z; do
    7z -bd -bb0 -y x -o"${srcdir}" "${f}" || true
done

rm ${srcdir}/*.7z

 


 

아. ~/.config/mpv/vapoursynth/NVDIA_Optical_Flow_b4c8.vpy 파일

    -  Nvidia 카드로 60fps 로 보간,  NVOF 하드웨어 프레임 보간함, 4K 해상도도 쌉가능 (기종 : GTX1660 이상 부터 )

import vapoursynth as vs
core = vs.core
core.num_threads = 8

if not hasattr(core,'svp2'):
    core.std.LoadPlugin("/opt/svp/plugins/libsvpflow2_vs64.so")

clip = video_in

smoothfps_params = "{gpuid:11,gpu_qn:2,rate:{num:600000,den:10000,abs:true},algo:13,mask:{area:100},scene:{}}"


if not (container_fps > 59):

    src_fps     = container_fps if container_fps>0.1 else 29.97
    demo_mode   = 0
    stereo_type = 0
    nvof = 1
    nvof_blk = 16
    
########## BEGIN OF base.py ##########
# This file is a part of SmoothVideo Project (SVP) ver.4
# This is NOT the full Vapoursynth script, all used variables are defined via
# JScript code that generates the full script text.

    def interpolate(clip):
# input_um - original frame in 4:2:0
# input_m  - cropped and resized (if needed) frame
# input_m8 - input_m converted to 8-bit
        input_um = clip.resize.Point(format=vs.YUV420P8,dither_type="random")
        input_m = input_um
        input_m8 = input_m.resize.Bicubic(input_m.width//nvof_blk*4,input_m.height//nvof_blk*4,src_width=input_m.width-(input_m.width % nvof_blk),src_height=input_m.height-(input_m.height % nvof_blk),format=vs.YUV420P8)

        if nvof:
            smooth  = core.svp2.SmoothFps_NVOF(input_m,smoothfps_params,nvof_src=input_m8,src=input_um,fps=src_fps)
        else:
            super   = core.svp1.Super(input_m8,super_params)
            vectors = core.svp1.Analyse(super["clip"],super["data"],input_m8,analyse_params)
            smooth  = core.svp2.SmoothFps(input_m,super["clip"],super["data"],vectors["clip"],vectors["data"],smoothfps_params,src=input_um,fps=src_fps)


        if demo_mode==1:
            return demo(input_m,smooth)
        else:
            return smooth

    if stereo_type == 1:
        lf = interpolate(core.std.CropRel(clip,0,(int)(clip.width/2),0,0))
        rf = interpolate(core.std.CropRel(clip,(int)(clip.width/2),0,0,0))
        smooth = core.std.StackHorizontal([lf, rf])
    elif stereo_type == 2:
        lf = interpolate(core.std.CropRel(clip,0,0,0,(int)(clip.height/2)))
        rf = interpolate(core.std.CropRel(clip,0,0,(int)(clip.height/2),0))
        smooth = core.std.StackVertical([lf, rf])
    else:
        smooth =  interpolate(clip)
########### END OF base.py ###########
    
    
    smooth.set_output()

 

 ~/.config/mpv/input.conf  : 위 스크립트 호출 실행  토글 단축키로 "2" 번 키에 할당함

   ( AMD 카드이거나 내장 그래픽 카드인 경우.  SVP4 GUI로 실행해서,   옵션값들 설정하고 GPU 보간 모드 또는 CPU Only 보간 모드로 돌려서..  해당 스크립트 추출해서,  ~~/vapoursynth/[추출한소스.vpy]  저장해서 단축키로 박아서 사용하면 된다)

2 vf toggle "vapoursynth=~~/vapoursynth/NVDIA_Optical_Flow_b4c8.vpy:buffered-frames=4:concurrent-frames=8"   #! Video > Interpolation > NVOF

 


▌부록:  Astronvim + Rvnimr + ranger + mpv


 

※ 준비물 : Astronvim, Rnvimr(nvim plugin), ranger, mpv

※ 목적 : 파일을 빠르게 검색/조작하고, 설정 파일들을 빠르게 편집힌다. 마우스를 사용하지 않는다.

 


🔎 설치 : Rnvinr Git: https://github.com/kevinhwang91/rnvimr

$ sudo pacman -S ranger, mpv
$ yay -S astronvim python-pynvim ueberzugpp



😀 ~/.config/ranger/rc.conf

set preview_images true
set preview_images_method mpv
set preview_script ~/.config/ranger/scope.sh
set use_preview_script true



😀 ~/.config/ranger/commands.py

## Image preivew With mpv
# The mpv image preview method allows ranger to control an external client for viewing media. The benefit of this approach is that both images and videos share a single, separate window.
import subprocess
import json
import atexit
import socket
from pathlib import Path

import logging
logger = logging.getLogger(__name__)
import traceback

from ranger.ext.img_display import ImageDisplayer, register_image_displayer

@register_image_displayer("mpv")
class MPVImageDisplayer(ImageDisplayer):
    """Implementation of ImageDisplayer using mpv, a general media viewer.
    Opens media in a separate X window.

    mpv 0.25+ needs to be installed for this to work.
    """

    def _send_command(self, path, sock):

        message = '{"command": ["raw","loadfile",%s]}\n' % json.dumps(path)
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.connect(str(sock))
        logger.info('-> ' + message)
        s.send(message.encode())
        message = s.recv(1024).decode()
        logger.info('<- ' + message)

    def _launch_mpv(self, path, sock):

        proc = subprocess.Popen([
            * os.environ.get("MPV", "mpv").split(),
            "--no-config",
            "--no-terminal",
            "--force-window",
            "--input-ipc-server=" + str(sock),
            "--image-display-duration=inf",
            "--loop-file=inf",
            "--no-osc",
            "--no-input-default-bindings",
            "--keep-open",
            "--idle",
            "--ontop",
            "--x11-name=mpsyt",
            "--geometry=854x480+1067+0",
            "--",
            path,
        ])

        @atexit.register
        def cleanup():
            proc.terminate()
            sock.unlink()

    def draw(self, path, start_x, start_y, width, height):

        path = os.path.abspath(path)
        cache = Path(os.environ.get("XDG_CACHE_HOME", "~/.cache")).expanduser()
        cache = cache / "ranger"
        cache.mkdir(exist_ok=True)
        sock = cache / "mpv.sock"

        try:
            self._send_command(path, sock)
        except (ConnectionRefusedError, FileNotFoundError):
            logger.info('LAUNCHING ' + path)
            self._launch_mpv(path, sock)
        except Exception as e:
            logger.exception(traceback.format_exc())
            sys.exit(1)
        logger.info('SUCCESS')


"--x11-name=mpsyt" : ranger 에서 띠우는 mvp 윈도우 창을 $ xprop 로 체크하면 class name, resource name 은 mpv, mpsyt가 된다.

wm에서 mpsyt class/resoruce 네임을 가지는 윈도우 창은 무시하도록 설정 한다.

ex) xmonad wm 인경우

😀 ~/.xmonad/xmonad.hs

   , [ className =? "mpv" <&&> resource =? "mpsyt"  --> doF W.focusDown ]   -- 또는 "doIgnore"

 

"--geometry=854x480+1067+0" : 우측 상단 코너로 지정하였다. ( mpv의 --geometry 옵션 대신, wm 설정에 따로 설정해도 된다.)


ranger에서 띠운 mpv 이미지 프리뷰는 ranger에서 이미지 파일인 아닌 곳으로 ranger 커서(포커스)가 이동 해도, 사라지지 않는다.

따라서, ranger 에서 띠운 mpv 윈도우 창 삭제 커맨드를 만들어서 keybinding 해 주었다

$ sudo pacmsn -S xbindkeys


😀 ~/.xbindkeysrc

"kill $(ps -ef | grep "x11-name=mpsyt" | head -n1 | awk '{printf $2}')"
  shift+BackSpace


"Shift" + "BackSpace" 키룰 누르면 mpv 이미지를 종료한다.

위 xbindkeys 키바인딩 대신, wm 설정 파일에서 지정 하여도 된다.


😀.zshrc:

야구 () { mpv $(tbitsearch $(dmenu -p "🔎 Search Torrent 😀 Watch: " -fn "hack" -sb "#783e57" -nb "#312e37" <&-) -s seeders -j | jq '.[] | .magnet' | head -n1 | sed -e 's/"//g') --terminal=no >/dev/null }

mmm () { mpv --ytdl-format="(bestvideo[height<=?1080][fps<=?60][vcodec=av01.0.09M.08]/bestvideo[height<=?1080][fps<=?30][vcodec=av01.0.08M.08]/bestvideo[height<=?1080][fps<=?60][ext=mp4])+(bestaudio[acodec=opus]/bestaudio[acodec=vorbis]/bestaudio[acodec=aac]/bestaudio/bestaudio[ext=webm])/bestvideo+bestaudio/best[ext=mp4]/best" ytdl://ytsearch:"$@" }



   1. 야구() 커맨드 실행시 필요한 프로그램:

$ sudo pacman -S dmenu npm mpv jq
$ yay -S tbitsearch-git webtorrent-cli
$ sudo npm install --global webtorrent-mpv-hook
$ webtorrent-mpv-hook
$ ln -s "/usr/lib/node_modules/webtorrent-mpv-hook/build/webtorrent.js" "/home/[유저명]/.config/mpv/scripts/webtorrent.js"


   2. mmm() 커맨드 실행시 필요한 프로그램:

$ sudo pacman -S mpv yt-dlp
(또는) $ yay -S mpv-git (또는) $ yay -S mpv-vapoursynth