Basic navigation and playback - mpv-jellyfin - MPV script for adding an interface for Jellyfin.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit f7aab8b73496a1db9960524cd70a66d53dfa2f15
 (DIR) parent b31f0c5706cad4a92a1cd95971e78546f41f4ecb
 (HTM) Author: EmperorPenguin18 <60635017+EmperorPenguin18@users.noreply.github.com>
       Date:   Sun,  6 Nov 2022 21:59:56 -0500
       
       Basic navigation and playback
       
       Diffstat:
         A script-opts/jellyfin.conf           |       3 +++
         A scripts/jellyfin.lua                |     159 +++++++++++++++++++++++++++++++
       
       2 files changed, 162 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/script-opts/jellyfin.conf b/script-opts/jellyfin.conf
       @@ -0,0 +1,3 @@
       +url=
       +username=
       +password=
 (DIR) diff --git a/scripts/jellyfin.lua b/scripts/jellyfin.lua
       @@ -0,0 +1,159 @@
       +local opt = require 'mp.options'
       +local utils = require 'mp.utils'
       +
       +local options = {
       +        url = "",
       +        username = "",
       +        password = ""
       +}
       +opt.read_options(options, mp.get_script_name())
       +
       +local overlay = mp.create_osd_overlay("ass-events")
       +local connected = false
       +local shown = false
       +local user_id = nil
       +local api_key = nil
       +local library_id = nil
       +local title_id = nil
       +local season_id = nil
       +local video_id = nil
       +local selection = 1
       +local items = {}
       +
       +local toggle_overlay
       +
       +local function send_request(url)
       +        if connected then
       +                local request = mp.command_native({
       +                        name = "subprocess",
       +                        capture_stdout = true,
       +                        capture_stderr = true,
       +                        playback_only = false,
       +                        args = {"curl", url}
       +                })
       +                return utils.parse_json(request.stdout)
       +        end
       +        return nil
       +end
       +
       +local function connect()
       +        local request = mp.command_native({
       +                name = "subprocess",
       +                capture_stdout = true,
       +                capture_stderr = true,
       +                playback_only = false,
       +                args = {"curl", options.url.."/Users/AuthenticateByName", "-H", "accept: application/json", "-H", "content-type: application/json", "-H", "x-emby-authorization: MediaBrowser Client=\"Custom Client\", Device=\"Custom Device\", DeviceId=\"1\", Version=\"0.0.1\"", "-d", "{\"username\":\""..options.username.."\",\"Pw\":\""..options.password.."\"}"}
       +        })
       +        local result = utils.parse_json(request.stdout)
       +        user_id = result.User.Id
       +        api_key = result.AccessToken
       +        connected = true
       +end
       +
       +local function update_data()
       +        overlay.data = ""
       +        local ow, oh, op = mp.get_osd_size()
       +        for _, item in ipairs(items) do
       +                if _ >= selection - (19.5 * op / 1.12) + 1 then
       +                        if _ - selection < (19.5 * op / 1.12) then
       +                                if _ == selection then
       +                                        overlay.data = overlay.data.."{\\fs16}{\\c&HFF&}"..item.Name.."\n"
       +                                else
       +                                        overlay.data = overlay.data.."{\\fs16}"..item.Name.."\n"
       +                                end
       +                        end
       +                end
       +        end
       +        overlay:update()
       +end
       +
       +local function update_overlay()
       +        local result
       +        if not library_id then
       +                result = send_request(options.url.."/Items?api_key="..api_key.."&userID="..user_id)
       +        elseif not title_id then
       +                result = send_request(options.url.."/Items?api_key="..api_key.."&userID="..user_id.."&parentId="..library_id.."&sortBy=SortName")
       +        elseif not season_id then
       +                result = send_request(options.url.."/Items?api_key="..api_key.."&userID="..user_id.."&parentId="..title_id)
       +        else
       +                result = send_request(options.url.."/Items?api_key="..api_key.."&userID="..user_id.."&parentId="..season_id)
       +        end
       +        items = result.Items
       +        update_data()
       +end
       +
       +local function play_video()
       +        toggle_overlay()
       +        mp.commandv("loadfile", options.url.."/Videos/"..video_id.."/stream?static=true&api_key="..api_key)
       +end
       +
       +local function key_up()
       +        selection = selection - 1
       +        if selection == 0 then selection = table.getn(items) end
       +        update_data()
       +end
       +
       +local function key_right()
       +        if items[selection].MediaType == "Video" then
       +                video_id = items[selection].Id
       +                play_video()
       +        else
       +                if not library_id then
       +                        library_id = items[selection].Id
       +                elseif not title_id then
       +                        title_id = items[selection].Id
       +                elseif not season_id then
       +                        season_id = items[selection].Id
       +                end
       +                items = {}
       +                selection = 1
       +                update_overlay()
       +        end
       +end
       +
       +local function key_down()
       +        selection = selection + 1
       +        if selection > table.getn(items) then selection = 1 end
       +        update_data()
       +end
       +
       +local function key_left()
       +        if not library_id then
       +                --nothing
       +        elseif not title_id then
       +                library_id = nil
       +        elseif not season_id then
       +                title_id = nil
       +        end
       +        items = {}
       +        selection = 1
       +        update_overlay()
       +end
       +
       +toggle_overlay = function()
       +        if shown then
       +                overlay:remove()
       +                mp.remove_key_binding("jup")
       +                mp.remove_key_binding("jright")
       +                mp.remove_key_binding("jdown")
       +                mp.remove_key_binding("jleft")
       +        else
       +                if not connected then connect() end
       +                if table.getn(items) == 0 then update_overlay() end
       +                mp.add_forced_key_binding("UP", "jup", key_up)
       +                mp.add_forced_key_binding("RIGHT", "jright", key_right)
       +                mp.add_forced_key_binding("DOWN", "jdown", key_down)
       +                mp.add_forced_key_binding("LEFT", "jleft", key_left)
       +        end
       +        shown = not shown
       +end
       +
       +local function mark_watched(data)
       +        if data.reason == "eof" then
       +                send_request(options.url.."/Users/"..user_id.."/PlayedItems/"..video_id.."?api_key="..api_key)
       +                video_id = nil
       +        end
       +end
       +
       +mp.register_event("end-file", mark_watched)
       +mp.add_key_binding("Ctrl+j", "jf", toggle_overlay)